summaryrefslogtreecommitdiff
path: root/Build/source/texk/web2c
diff options
context:
space:
mode:
Diffstat (limited to 'Build/source/texk/web2c')
-rw-r--r--Build/source/texk/web2c/ChangeLog61
-rw-r--r--Build/source/texk/web2c/Makefile.am3
-rw-r--r--Build/source/texk/web2c/Makefile.in478
-rw-r--r--Build/source/texk/web2c/ac/web2c.ac1
-rw-r--r--Build/source/texk/web2c/bibtex.ch10
-rwxr-xr-xBuild/source/texk/web2c/configure26
-rw-r--r--Build/source/texk/web2c/ctangleboot.cin2
-rw-r--r--Build/source/texk/web2c/cwebdir/ChangeLog69
-rw-r--r--Build/source/texk/web2c/cwebdir/Makefile6
-rw-r--r--Build/source/texk/web2c/cwebdir/comm-mac.ch4
-rw-r--r--Build/source/texk/web2c/cwebdir/comm-mini.ch4
-rw-r--r--Build/source/texk/web2c/cwebdir/comm-ql.ch8
-rw-r--r--Build/source/texk/web2c/cwebdir/comm-w2c.ch22
-rw-r--r--Build/source/texk/web2c/cwebdir/comm-w2c.h2
-rw-r--r--Build/source/texk/web2c/cwebdir/common.h2
-rw-r--r--Build/source/texk/web2c/cwebdir/common.w6
-rw-r--r--Build/source/texk/web2c/cwebdir/ctang-bs.ch4
-rw-r--r--Build/source/texk/web2c/cwebdir/ctang-pc.ch4
-rw-r--r--Build/source/texk/web2c/cwebdir/ctang-ql.ch8
-rw-r--r--Build/source/texk/web2c/cwebdir/ctang-vms.ch4
-rw-r--r--Build/source/texk/web2c/cwebdir/ctang-w2c.ch12
-rw-r--r--Build/source/texk/web2c/cwebdir/ctang-w32.ch4
-rw-r--r--Build/source/texk/web2c/cwebdir/ctangle.c2
-rw-r--r--Build/source/texk/web2c/cwebdir/ctangle.w8
-rw-r--r--Build/source/texk/web2c/cwebdir/ctwill-mini.ch12
-rw-r--r--Build/source/texk/web2c/cwebdir/ctwill-w2c.ch298
-rw-r--r--Build/source/texk/web2c/cwebdir/cweav-bs.ch4
-rw-r--r--Build/source/texk/web2c/cwebdir/cweav-pc.ch4
-rw-r--r--Build/source/texk/web2c/cwebdir/cweav-ql.ch8
-rw-r--r--Build/source/texk/web2c/cwebdir/cweav-vms.ch4
-rw-r--r--Build/source/texk/web2c/cwebdir/cweav-w2c.ch160
-rw-r--r--Build/source/texk/web2c/cwebdir/cweav-w32.ch4
-rw-r--r--Build/source/texk/web2c/cwebdir/cweave.w57
-rw-r--r--Build/source/texk/web2c/cwebdir/cwebacromac.tex125
-rw-r--r--Build/source/texk/web2c/cwebdir/cwebmac.tex80
-rw-r--r--Build/source/texk/web2c/cwebdir/cwebman-w2c.ch14
-rw-r--r--Build/source/texk/web2c/cwebdir/cwebman.tex12
-rw-r--r--Build/source/texk/web2c/cwebdir/po/cweb-tl.pot10
-rw-r--r--Build/source/texk/web2c/cwebdir/po/cweb.pot6
-rw-r--r--Build/source/texk/web2c/cwebdir/po/de/cweb-tl.po20
-rw-r--r--Build/source/texk/web2c/cwebdir/po/de/cweb.po12
-rw-r--r--Build/source/texk/web2c/cwebdir/po/it/cweb.po20
-rw-r--r--Build/source/texk/web2c/cwebdir/prod-cweave.w2
-rw-r--r--Build/source/texk/web2c/cwebdir/prod-twill.w2
-rw-r--r--Build/source/texk/web2c/cwebdir/prod.w2
-rw-r--r--Build/source/texk/web2c/cwebdir/refsort.ch12
-rw-r--r--Build/source/texk/web2c/cwebdir/texinputs/dcwebmac.tex2
-rw-r--r--Build/source/texk/web2c/cwebdir/texinputs/fcwebmac.tex2
-rw-r--r--Build/source/texk/web2c/cwebdir/texinputs/icwebmac.tex14
-rw-r--r--Build/source/texk/web2c/cwebdir/texinputs/pdfctproofmac.tex76
-rw-r--r--Build/source/texk/web2c/cwebdir/texinputs/pdfctwimac.tex57
-rw-r--r--Build/source/texk/web2c/dvicopy.ch98
-rw-r--r--Build/source/texk/web2c/dvicopy.web19
-rw-r--r--Build/source/texk/web2c/dvitype.ch10
-rw-r--r--Build/source/texk/web2c/euptexdir/ChangeLog26
-rw-r--r--Build/source/texk/web2c/euptexdir/am/euptex.am52
-rw-r--r--Build/source/texk/web2c/euptexdir/eptex.ech45
-rwxr-xr-xBuild/source/texk/web2c/euptexdir/euptex-ctrlsym.test49
-rwxr-xr-xBuild/source/texk/web2c/euptexdir/euptex-kinsoku.test69
-rwxr-xr-xBuild/source/texk/web2c/euptexdir/euptex-prntknj.test61
-rwxr-xr-xBuild/source/texk/web2c/euptexdir/euptex-ucs.test43
-rw-r--r--Build/source/texk/web2c/euptexdir/pdfutils.ch4
-rw-r--r--Build/source/texk/web2c/euptexdir/tests/control-symbol.tex108
-rw-r--r--Build/source/texk/web2c/euptexdir/tests/ctrlsym.tex6
-rw-r--r--Build/source/texk/web2c/euptexdir/tests/endline.log43
-rw-r--r--Build/source/texk/web2c/euptexdir/tests/endlinechar.tex57
-rw-r--r--Build/source/texk/web2c/euptexdir/tests/eup-endlinea.typ93
-rw-r--r--Build/source/texk/web2c/euptexdir/tests/eup-inhibitgluea.typ1846
-rw-r--r--Build/source/texk/web2c/euptexdir/tests/eup-jctrlsyma.typ121
-rw-r--r--Build/source/texk/web2c/euptexdir/tests/eup-pknj-ea.typ825
-rw-r--r--Build/source/texk/web2c/euptexdir/tests/eup-pknj-sa.typ829
-rw-r--r--Build/source/texk/web2c/euptexdir/tests/eup-pknj-ua.typ829
-rw-r--r--Build/source/texk/web2c/euptexdir/tests/free_ixsp.log29
-rw-r--r--Build/source/texk/web2c/euptexdir/tests/free_ixsp.tex (renamed from Build/source/texk/web2c/ptexdir/tests/free_ixsp.tex)7
-rw-r--r--Build/source/texk/web2c/euptexdir/tests/free_pena.log29
-rw-r--r--Build/source/texk/web2c/euptexdir/tests/free_pena.tex (renamed from Build/source/texk/web2c/ptexdir/tests/free_pena.tex)14
-rw-r--r--Build/source/texk/web2c/euptexdir/tests/inhibitglue.tex (renamed from Build/source/texk/web2c/ptexdir/tests/inhibitglue.tex)24
-rw-r--r--Build/source/texk/web2c/euptexdir/tests/jctrlsym.log85
-rw-r--r--Build/source/texk/web2c/euptexdir/tests/kinsoku_search.log84
-rw-r--r--Build/source/texk/web2c/euptexdir/tests/kinsoku_search.tex (renamed from Build/source/texk/web2c/ptexdir/tests/kinsoku_search.tex)0
-rw-r--r--Build/source/texk/web2c/euptexdir/tests/kinsoku_table.log38
-rw-r--r--Build/source/texk/web2c/euptexdir/tests/kinsoku_table.tex (renamed from Build/source/texk/web2c/ptexdir/tests/kinsoku_table.tex)5
-rw-r--r--Build/source/texk/web2c/euptexdir/tests/pknj-e.log224
-rw-r--r--Build/source/texk/web2c/euptexdir/tests/pknj-s.log224
-rw-r--r--Build/source/texk/web2c/euptexdir/tests/pknj-u.log224
-rw-r--r--Build/source/texk/web2c/euptexdir/tests/postpena.log43
-rw-r--r--Build/source/texk/web2c/euptexdir/tests/postpena.tex (renamed from Build/source/texk/web2c/ptexdir/tests/postpena.tex)8
-rw-r--r--Build/source/texk/web2c/euptexdir/tests/printkanji.tex176
-rw-r--r--Build/source/texk/web2c/euptexdir/tests/ucs-e.log29
-rw-r--r--Build/source/texk/web2c/euptexdir/tests/ucs-s.log29
-rw-r--r--Build/source/texk/web2c/euptexdir/tests/ucs-u.log529
-rw-r--r--Build/source/texk/web2c/euptexdir/tests/ucs.tex36
-rw-r--r--Build/source/texk/web2c/gftodvi.ch10
-rw-r--r--Build/source/texk/web2c/gftopk.ch12
-rw-r--r--Build/source/texk/web2c/gftype.ch12
-rw-r--r--Build/source/texk/web2c/hitexdir/hitex.w708
-rw-r--r--Build/source/texk/web2c/luatexdir/ChangeLog5
-rw-r--r--Build/source/texk/web2c/luatexdir/lang/texlang.c2
-rw-r--r--Build/source/texk/web2c/luatexdir/luatex_svnversion.h2
-rw-r--r--Build/source/texk/web2c/man/ChangeLog4
-rw-r--r--Build/source/texk/web2c/man/ctwill.man4
-rw-r--r--Build/source/texk/web2c/mft.ch4
-rw-r--r--Build/source/texk/web2c/patgen.ch8
-rw-r--r--Build/source/texk/web2c/patgen.web11
-rw-r--r--Build/source/texk/web2c/pdftexdir/ChangeLog10
-rw-r--r--Build/source/texk/web2c/pdftexdir/pdftex.ch4
-rw-r--r--Build/source/texk/web2c/pdftexdir/pdftoepdf.cc6
-rw-r--r--Build/source/texk/web2c/pktogf.ch8
-rw-r--r--Build/source/texk/web2c/pktogf.web11
-rw-r--r--Build/source/texk/web2c/pktype.ch14
-rw-r--r--Build/source/texk/web2c/pltotf.ch10
-rw-r--r--Build/source/texk/web2c/pooltype.ch10
-rw-r--r--Build/source/texk/web2c/ptexdir/ChangeLog26
-rw-r--r--Build/source/texk/web2c/ptexdir/am/ptex.am32
-rw-r--r--Build/source/texk/web2c/ptexdir/ptex-base.ch30
-rwxr-xr-xBuild/source/texk/web2c/ptexdir/ptex-ctrlsym.test77
-rwxr-xr-xBuild/source/texk/web2c/ptexdir/ptex-prntknj.test48
-rwxr-xr-xBuild/source/texk/web2c/ptexdir/ptex-ucs.test37
-rw-r--r--Build/source/texk/web2c/ptexdir/ptex_version.h2
-rw-r--r--Build/source/texk/web2c/ptexdir/tests/chartoken.log41
-rw-r--r--Build/source/texk/web2c/ptexdir/tests/control-symbol.tex11
-rw-r--r--Build/source/texk/web2c/ptexdir/tests/ctrlsym.log11
-rw-r--r--Build/source/texk/web2c/ptexdir/tests/ctrlsym.tex36
-rw-r--r--Build/source/texk/web2c/ptexdir/tests/endline.log24
-rw-r--r--Build/source/texk/web2c/ptexdir/tests/endlinechar.tex30
-rw-r--r--Build/source/texk/web2c/ptexdir/tests/jctrlsym.log42
-rw-r--r--Build/source/texk/web2c/ptexdir/tests/p-endlinea.typ68
-rw-r--r--Build/source/texk/web2c/ptexdir/tests/p-jctrlsyma.typ123
-rw-r--r--Build/source/texk/web2c/ptexdir/tests/p-lineenda.typ295
-rw-r--r--Build/source/texk/web2c/ptexdir/tests/p-pknj-ea.typ825
-rw-r--r--Build/source/texk/web2c/ptexdir/tests/p-pknj-sa.typ829
-rw-r--r--Build/source/texk/web2c/ptexdir/tests/pknj-e.log223
-rw-r--r--Build/source/texk/web2c/ptexdir/tests/pknj-s.log223
-rw-r--r--Build/source/texk/web2c/ptexdir/tests/printkanji.tex12
-rw-r--r--Build/source/texk/web2c/ptexdir/tests/ptexlineendmode.tex10
-rw-r--r--Build/source/texk/web2c/ptexdir/tests/ucs-e.log28
-rw-r--r--Build/source/texk/web2c/ptexdir/tests/ucs-s.log28
-rw-r--r--Build/source/texk/web2c/ptexdir/tests/ucs.tex9
-rw-r--r--Build/source/texk/web2c/showstream.ch26
-rw-r--r--Build/source/texk/web2c/tangle.ch12
-rw-r--r--Build/source/texk/web2c/tests/dump-basic.tex6
-rw-r--r--Build/source/texk/web2c/tests/ec-lmr10.tfmbin0 -> 12056 bytes
-rw-r--r--Build/source/texk/web2c/texprofdir/ChangeLog16
-rw-r--r--Build/source/texk/web2c/texprofdir/README.TXT5
-rw-r--r--Build/source/texk/web2c/texprofdir/am/texprof.am76
-rw-r--r--Build/source/texk/web2c/texprofdir/doc/texprof.hntbin0 -> 11685007 bytes
-rw-r--r--Build/source/texk/web2c/texprofdir/doc/texprof.pdfbin0 -> 4191378 bytes
-rw-r--r--Build/source/texk/web2c/texprofdir/doc/texprofile.hntbin0 -> 968072 bytes
-rw-r--r--Build/source/texk/web2c/texprofdir/doc/texprofile.pdfbin0 -> 328206 bytes
-rw-r--r--Build/source/texk/web2c/texprofdir/doc/texprofileman.hntbin0 -> 518370 bytes
-rw-r--r--Build/source/texk/web2c/texprofdir/doc/texprofileman.pdfbin0 -> 193602 bytes
-rw-r--r--Build/source/texk/web2c/texprofdir/man/texprof.man345
-rw-r--r--Build/source/texk/web2c/texprofdir/man/texprofile.man596
-rw-r--r--Build/source/texk/web2c/texprofdir/tex/latexprof.ini1
-rw-r--r--Build/source/texk/web2c/texprofdir/tex/pdflatexprof.ini2
-rw-r--r--Build/source/texk/web2c/texprofdir/tex/pdftexprof.ini3
-rw-r--r--Build/source/texk/web2c/texprofdir/tex/texprof.ini2
-rw-r--r--Build/source/texk/web2c/texprofdir/texprof.w33304
-rw-r--r--Build/source/texk/web2c/texprofdir/texprofile.w1981
-rw-r--r--Build/source/texk/web2c/texprofdir/texprofilemac.tex1535
-rw-r--r--Build/source/texk/web2c/texprofdir/texprofileman.tex442
-rw-r--r--Build/source/texk/web2c/tftopl.ch34
-rw-r--r--Build/source/texk/web2c/tiedir/ChangeLog4
-rw-r--r--Build/source/texk/web2c/tiedir/tie-w2c.ch12
-rw-r--r--Build/source/texk/web2c/uptexdir/ChangeLog30
-rw-r--r--Build/source/texk/web2c/uptexdir/am/uptex.am4
-rw-r--r--Build/source/texk/web2c/uptexdir/kanji.c71
-rw-r--r--Build/source/texk/web2c/uptexdir/ptex-base.ch30
-rw-r--r--Build/source/texk/web2c/uptexdir/ptex_version.h2
-rw-r--r--Build/source/texk/web2c/uptexdir/tests/unibib.bbl7
-rw-r--r--Build/source/texk/web2c/uptexdir/tests/unibib.bib22
-rw-r--r--Build/source/texk/web2c/uptexdir/tests/unibib.tex9
-rw-r--r--Build/source/texk/web2c/uptexdir/upbibtex.ch6
-rw-r--r--Build/source/texk/web2c/uptexdir/uptex-m.ch31
-rw-r--r--Build/source/texk/web2c/vftovp.ch4
-rw-r--r--Build/source/texk/web2c/vptovf.ch10
-rw-r--r--Build/source/texk/web2c/weav-twill.ch26
-rw-r--r--Build/source/texk/web2c/weave.ch66
178 files changed, 50161 insertions, 1373 deletions
diff --git a/Build/source/texk/web2c/ChangeLog b/Build/source/texk/web2c/ChangeLog
index c9203566413..b34327fb6fe 100644
--- a/Build/source/texk/web2c/ChangeLog
+++ b/Build/source/texk/web2c/ChangeLog
@@ -1,3 +1,64 @@
+2024-09-16 TANAKA Takuji <ttk@t-lab.opal.ne.jp>
+
+ * tests/ec-lmr10.tfm, tests/dump-basic.tex:
+ Add for tests of pTeX family
+
+2024-09-07 Karl Berry <karl@freefriends.org>
+
+ * Makefile.am (texprof.am): include.
+ * ac/web2c.ac (kpse_tex_progs) <texprof>: new entry in table.
+
+2024-09-06 Andreas Scherer <https://ascherer.github.io>
+
+ * weav-twill.ch,
+ * weave.ch: Sort section names in TWILL.
+
+2024-09-02 Andreas Scherer <https://ascherer.github.io>
+
+ * bibtex.ch,
+ * dvitype.ch,
+ * gftodvi.ch,
+ * gftopk.ch,
+ * gftype.ch,
+ * mft.ch,
+ * pktype.ch,
+ * pltotf.ch,
+ * pooltype.ch,
+ * tangle.ch,
+ * tftopl.ch,
+ * vftovp.ch,
+ * vptovf.ch,
+ * weave.ch: Flip two section names in the index.
+ * dvicopy.ch,
+ * dvicopy.web,
+ * patgen.ch,
+ * patgen.web,
+ * pktogf.ch,
+ * pktogf.web: Fix sorting order in 'other' WEB codees.
+
+2024-08-31 Andreas Scherer <https://ascherer.github.io>
+
+ * vftovp.ch: Fix sorting order of new named sections.
+
+2024-08-23 Andreas Scherer <https://ascherer.github.io>
+
+ * gftopk.ch,
+ * gftype.ch,
+ * pktogf.ch,
+ * pktype.ch: Fix a few spaces.
+
+2024-08-21 Andreas Scherer <https://ascherer.github.io>
+
+ * weav-twill.ch: Prime the title page.
+
+2024-08-16 Andreas Scherer <https://ascherer.github.io>
+
+ * showstream.ch: Restore section numbering as far as possible.
+
+2024-08-12 Andreas Scherer <https://ascherer.github.io>
+
+ * ctangleboot.cin: CWEB 4.12 release.
+
2024-07-14 Andreas Scherer <https://ascherer.github.io>
* mft.ch: Preserve Metafont logo on title page.
diff --git a/Build/source/texk/web2c/Makefile.am b/Build/source/texk/web2c/Makefile.am
index 6e271293a66..183082f7e22 100644
--- a/Build/source/texk/web2c/Makefile.am
+++ b/Build/source/texk/web2c/Makefile.am
@@ -299,6 +299,9 @@ include $(srcdir)/luatexdir/am/luatex.am
## luahbTeX
include $(srcdir)/luatexdir/am/luaharfbuzz.am
+## texprof
+include $(srcdir)/texprofdir/am/texprof.am
+
## XeTeX
include $(srcdir)/xetexdir/am/xetex.am
diff --git a/Build/source/texk/web2c/Makefile.in b/Build/source/texk/web2c/Makefile.in
index 9a762efeef9..0edf625113b 100644
--- a/Build/source/texk/web2c/Makefile.in
+++ b/Build/source/texk/web2c/Makefile.in
@@ -115,22 +115,22 @@ bin_PROGRAMS = tangle$(EXEEXT) ctangle$(EXEEXT) $(am__EXEEXT_4) \
$(am__EXEEXT_20) $(am__EXEEXT_21) $(am__EXEEXT_22) \
$(am__EXEEXT_23) $(am__EXEEXT_24) $(am__EXEEXT_25) \
$(am__EXEEXT_26) $(am__EXEEXT_27) $(am__EXEEXT_28) \
- $(am__EXEEXT_29) $(am__EXEEXT_30)
+ $(am__EXEEXT_29) $(am__EXEEXT_30) $(am__EXEEXT_31)
noinst_PROGRAMS = tangleboot$(EXEEXT) ctangleboot$(EXEEXT) \
- $(am__EXEEXT_31) $(am__EXEEXT_32) $(am__EXEEXT_33) \
- $(am__EXEEXT_34) $(am__EXEEXT_35) $(am__EXEEXT_36) \
- $(am__EXEEXT_37) $(am__EXEEXT_38) $(am__EXEEXT_39) \
- $(am__EXEEXT_40) $(am__EXEEXT_41)
-TESTS = tangle.test $(am__EXEEXT_44) ctiedir/ctie.test \
+ $(am__EXEEXT_32) $(am__EXEEXT_33) $(am__EXEEXT_34) \
+ $(am__EXEEXT_35) $(am__EXEEXT_36) $(am__EXEEXT_37) \
+ $(am__EXEEXT_38) $(am__EXEEXT_39) $(am__EXEEXT_40) \
+ $(am__EXEEXT_41) $(am__EXEEXT_42)
+TESTS = tangle.test $(am__EXEEXT_45) ctiedir/ctie.test \
cwebdir/ctwill.test cwebdir/cweave.test tiedir/tie.test \
$(am__append_6) $(am__append_16) $(am__append_25) \
$(am__append_34) $(am__append_42) $(am__append_55) \
$(am__append_56) $(am__append_58) $(am__append_62) \
- $(am__EXEEXT_45) $(am__append_68) $(am__append_71) \
+ $(am__EXEEXT_46) $(am__append_68) $(am__append_71) \
$(am__append_76) $(am__append_81) $(am__append_84) \
$(am__append_105) $(am__append_106) $(am__append_107) \
- $(am__append_108) $(am__append_116) $(am__append_118) \
- $(am__append_120) $(am__append_143) libmd5/md5.test
+ $(am__append_108) $(am__append_117) $(am__append_119) \
+ $(am__append_121) $(am__append_144) libmd5/md5.test
@WEB_TRUE@am__append_1 = $(web_programs)
@WEB_TRUE@am__append_2 = $(web_tests)
@TEX_TRUE@am__append_3 = tex
@@ -249,85 +249,86 @@ TESTS = tangle.test $(am__EXEEXT_44) ctiedir/ctie.test \
@LUAHBTEX_TRUE@am__append_106 = $(luahbtex_tests)
@LUAJITTEX_TRUE@am__append_107 = $(luajittex_tests)
@LUAJITHBTEX_TRUE@am__append_108 = $(luajithbtex_tests)
-@XETEX_TRUE@am__append_109 = xetex
-@XETEX_MACOSX_TRUE@am__append_110 = -DXETEX_MAC
-@XETEX_MACOSX_TRUE@am__append_111 = -std=c++11
-@XETEX_MACOSX_FALSE@am__append_112 = $(FONTCONFIG_INCLUDES)
-@XETEX_MACOSX_FALSE@am__append_113 = $(FONTCONFIG_LIBS)
-@XETEX_MACOSX_TRUE@am__append_114 = \
+@TEXPROF_TRUE@am__append_109 = texprof texprofile
+@XETEX_TRUE@am__append_110 = xetex
+@XETEX_MACOSX_TRUE@am__append_111 = -DXETEX_MAC
+@XETEX_MACOSX_TRUE@am__append_112 = -std=c++11
+@XETEX_MACOSX_FALSE@am__append_113 = $(FONTCONFIG_INCLUDES)
+@XETEX_MACOSX_FALSE@am__append_114 = $(FONTCONFIG_LIBS)
+@XETEX_MACOSX_TRUE@am__append_115 = \
@XETEX_MACOSX_TRUE@ xetexdir/XeTeXFontInst_Mac.cpp \
@XETEX_MACOSX_TRUE@ xetexdir/XeTeXFontInst_Mac.h \
@XETEX_MACOSX_TRUE@ xetexdir/XeTeXFontMgr_Mac.mm \
@XETEX_MACOSX_TRUE@ xetexdir/XeTeXFontMgr_Mac.h \
@XETEX_MACOSX_TRUE@ xetexdir/XeTeX_mac.c
-@XETEX_MACOSX_FALSE@am__append_115 = \
+@XETEX_MACOSX_FALSE@am__append_116 = \
@XETEX_MACOSX_FALSE@ xetexdir/XeTeXFontMgr_FC.cpp \
@XETEX_MACOSX_FALSE@ xetexdir/XeTeXFontMgr_FC.h
-@XETEX_TRUE@am__append_116 = $(xetex_tests)
-@OTANGLE_TRUE@am__append_117 = $(omegaware_programs)
-@OTANGLE_TRUE@am__append_118 = $(OTANGLE_tests) $(OMFONTS_tests)
-@ALEPH_TRUE@am__append_119 = aleph
-@ALEPH_TRUE@am__append_120 = $(aleph_tests)
-@SYNCTEX_TRUE@am__append_121 = synctex
-@SYNCTEX_TRUE@am__append_122 = $(LTLIBSYNCTEX)
-@SYNCTEX_TRUE@am__append_123 = $(LIBSYNCTEX)
-@MINGW32_TRUE@am__append_124 = $(KPATHSEA_INCLUDES)
-@MINGW32_TRUE@am__append_125 = -lshlwapi $(KPATHSEA_LIBS)
-@MINGW32_TRUE@am__append_126 = -lshlwapi
-@TEX_SYNCTEX_TRUE@am__append_127 = -I$(srcdir)/synctexdir \
+@XETEX_TRUE@am__append_117 = $(xetex_tests)
+@OTANGLE_TRUE@am__append_118 = $(omegaware_programs)
+@OTANGLE_TRUE@am__append_119 = $(OTANGLE_tests) $(OMFONTS_tests)
+@ALEPH_TRUE@am__append_120 = aleph
+@ALEPH_TRUE@am__append_121 = $(aleph_tests)
+@SYNCTEX_TRUE@am__append_122 = synctex
+@SYNCTEX_TRUE@am__append_123 = $(LTLIBSYNCTEX)
+@SYNCTEX_TRUE@am__append_124 = $(LIBSYNCTEX)
+@MINGW32_TRUE@am__append_125 = $(KPATHSEA_INCLUDES)
+@MINGW32_TRUE@am__append_126 = -lshlwapi $(KPATHSEA_LIBS)
+@MINGW32_TRUE@am__append_127 = -lshlwapi
+@TEX_SYNCTEX_TRUE@am__append_128 = -I$(srcdir)/synctexdir \
@TEX_SYNCTEX_TRUE@ $(ZLIB_INCLUDES) -D__SyncTeX__ \
@TEX_SYNCTEX_TRUE@ -DSYNCTEX_ENGINE_H=\"synctex-tex.h\"
-@TEX_SYNCTEX_TRUE@am__append_128 = $(ZLIB_LIBS)
-@TEX_SYNCTEX_TRUE@am__append_129 = $(ZLIB_DEPEND)
-@TEX_SYNCTEX_TRUE@am__append_130 = \
+@TEX_SYNCTEX_TRUE@am__append_129 = $(ZLIB_LIBS)
+@TEX_SYNCTEX_TRUE@am__append_130 = $(ZLIB_DEPEND)
+@TEX_SYNCTEX_TRUE@am__append_131 = \
@TEX_SYNCTEX_TRUE@ synctexdir/synctex.c \
@TEX_SYNCTEX_TRUE@ synctexdir/synctex.h \
@TEX_SYNCTEX_TRUE@ synctexdir/synctex-common.h \
@TEX_SYNCTEX_TRUE@ synctexdir/synctex-tex.h
-@ETEX_SYNCTEX_TRUE@am__append_131 = -I$(srcdir)/synctexdir \
+@ETEX_SYNCTEX_TRUE@am__append_132 = -I$(srcdir)/synctexdir \
@ETEX_SYNCTEX_TRUE@ $(ZLIB_INCLUDES) -D__SyncTeX__ \
@ETEX_SYNCTEX_TRUE@ -DSYNCTEX_ENGINE_H=\"synctex-etex.h\"
-@ETEX_SYNCTEX_TRUE@am__append_132 = $(ZLIB_LIBS)
-@ETEX_SYNCTEX_TRUE@am__append_133 = $(ZLIB_DEPEND)
-@ETEX_SYNCTEX_TRUE@am__append_134 = \
+@ETEX_SYNCTEX_TRUE@am__append_133 = $(ZLIB_LIBS)
+@ETEX_SYNCTEX_TRUE@am__append_134 = $(ZLIB_DEPEND)
+@ETEX_SYNCTEX_TRUE@am__append_135 = \
@ETEX_SYNCTEX_TRUE@ synctexdir/synctex.c \
@ETEX_SYNCTEX_TRUE@ synctexdir/synctex.h \
@ETEX_SYNCTEX_TRUE@ synctexdir/synctex-common.h \
@ETEX_SYNCTEX_TRUE@ synctexdir/synctex-etex.h
-@EUPTEX_SYNCTEX_TRUE@am__append_135 = -I$(srcdir)/synctexdir \
+@EUPTEX_SYNCTEX_TRUE@am__append_136 = -I$(srcdir)/synctexdir \
@EUPTEX_SYNCTEX_TRUE@ $(ZLIB_INCLUDES) -D__SyncTeX__ \
@EUPTEX_SYNCTEX_TRUE@ -DSYNCTEX_ENGINE_H=\"synctex-euptex.h\"
-@EUPTEX_SYNCTEX_TRUE@am__append_136 = $(ZLIB_LIBS)
-@EUPTEX_SYNCTEX_TRUE@am__append_137 = $(ZLIB_DEPEND)
-@EUPTEX_SYNCTEX_TRUE@am__append_138 = \
+@EUPTEX_SYNCTEX_TRUE@am__append_137 = $(ZLIB_LIBS)
+@EUPTEX_SYNCTEX_TRUE@am__append_138 = $(ZLIB_DEPEND)
+@EUPTEX_SYNCTEX_TRUE@am__append_139 = \
@EUPTEX_SYNCTEX_TRUE@ synctexdir/synctex.c \
@EUPTEX_SYNCTEX_TRUE@ synctexdir/synctex.h \
@EUPTEX_SYNCTEX_TRUE@ synctexdir/synctex-common.h \
@EUPTEX_SYNCTEX_TRUE@ synctexdir/synctex-euptex.h
-@PDFTEX_SYNCTEX_TRUE@am__append_139 = -I$(srcdir)/synctexdir \
+@PDFTEX_SYNCTEX_TRUE@am__append_140 = -I$(srcdir)/synctexdir \
@PDFTEX_SYNCTEX_TRUE@ -D__SyncTeX__ \
@PDFTEX_SYNCTEX_TRUE@ -DSYNCTEX_ENGINE_H=\"synctex-pdftex.h\"
-@PDFTEX_SYNCTEX_TRUE@am__append_140 = \
+@PDFTEX_SYNCTEX_TRUE@am__append_141 = \
@PDFTEX_SYNCTEX_TRUE@ synctexdir/synctex.c \
@PDFTEX_SYNCTEX_TRUE@ synctexdir/synctex.h \
@PDFTEX_SYNCTEX_TRUE@ synctexdir/synctex-common.h \
@PDFTEX_SYNCTEX_TRUE@ synctexdir/synctex-pdftex.h
-@XETEX_SYNCTEX_TRUE@am__append_141 = -I$(srcdir)/synctexdir \
+@XETEX_SYNCTEX_TRUE@am__append_142 = -I$(srcdir)/synctexdir \
@XETEX_SYNCTEX_TRUE@ -D__SyncTeX__ \
@XETEX_SYNCTEX_TRUE@ -DSYNCTEX_ENGINE_H=\"synctex-xetex.h\"
-@XETEX_SYNCTEX_TRUE@am__append_142 = \
+@XETEX_SYNCTEX_TRUE@am__append_143 = \
@XETEX_SYNCTEX_TRUE@ synctexdir/synctex.c \
@XETEX_SYNCTEX_TRUE@ synctexdir/synctex.h \
@XETEX_SYNCTEX_TRUE@ synctexdir/synctex-common.h \
@XETEX_SYNCTEX_TRUE@ synctexdir/synctex-xetex.h
-@SYNCTEX_TRUE@am__append_143 = $(synctex_tests)
+@SYNCTEX_TRUE@am__append_144 = $(synctex_tests)
subdir = .
ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
am__aclocal_m4_deps = $(top_srcdir)/m4/web2c-disable.m4 \
@@ -424,25 +425,26 @@ am__EXEEXT_3 = odvicopy$(EXEEXT) odvitype$(EXEEXT) otangle$(EXEEXT) \
@LUAHBTEX_TRUE@am__EXEEXT_24 = luahbtex$(EXEEXT)
@LUAJITTEX_TRUE@am__EXEEXT_25 = luajittex$(EXEEXT)
@LUAJITHBTEX_TRUE@am__EXEEXT_26 = luajithbtex$(EXEEXT)
-@XETEX_TRUE@am__EXEEXT_27 = xetex$(EXEEXT)
-@OTANGLE_TRUE@am__EXEEXT_28 = $(am__EXEEXT_3)
-@ALEPH_TRUE@am__EXEEXT_29 = aleph$(EXEEXT)
-@SYNCTEX_TRUE@am__EXEEXT_30 = synctex$(EXEEXT)
+@TEXPROF_TRUE@am__EXEEXT_27 = texprof$(EXEEXT) texprofile$(EXEEXT)
+@XETEX_TRUE@am__EXEEXT_28 = xetex$(EXEEXT)
+@OTANGLE_TRUE@am__EXEEXT_29 = $(am__EXEEXT_3)
+@ALEPH_TRUE@am__EXEEXT_30 = aleph$(EXEEXT)
+@SYNCTEX_TRUE@am__EXEEXT_31 = synctex$(EXEEXT)
am__installdirs = "$(DESTDIR)$(bindir)" "$(DESTDIR)$(libdir)" \
"$(DESTDIR)$(libdir)" "$(DESTDIR)$(bindir)" \
"$(DESTDIR)$(man1dir)" "$(DESTDIR)$(man5dir)" \
"$(DESTDIR)$(pkgconfigdir)" "$(DESTDIR)$(syncincludedir)"
-@MF_TRUE@@WIN32_TRUE@am__EXEEXT_31 = call_mf$(EXEEXT)
-@MFLUA_TRUE@@WIN32_TRUE@am__EXEEXT_32 = call_mflua$(EXEEXT)
-@MFLUAJIT_TRUE@@WIN32_TRUE@am__EXEEXT_33 = call_mfluajit$(EXEEXT)
-@MP_TRUE@@WIN32_TRUE@am__EXEEXT_34 = call_mpost$(EXEEXT)
-@PMP_TRUE@@WIN32_TRUE@am__EXEEXT_35 = call_pmpost$(EXEEXT)
-@UPMP_TRUE@@WIN32_TRUE@am__EXEEXT_36 = call_upmpost$(EXEEXT)
-@HITEX_TRUE@am__EXEEXT_37 = himktables$(EXEEXT)
-@LUATEX_TRUE@@WIN32_TRUE@am__EXEEXT_38 = call_luatex$(EXEEXT)
-@LUAHBTEX_TRUE@@WIN32_TRUE@am__EXEEXT_39 = call_luahbtex$(EXEEXT)
-@LUAJITTEX_TRUE@@WIN32_TRUE@am__EXEEXT_40 = call_luajittex$(EXEEXT)
-@LUAJITHBTEX_TRUE@@WIN32_TRUE@am__EXEEXT_41 = \
+@MF_TRUE@@WIN32_TRUE@am__EXEEXT_32 = call_mf$(EXEEXT)
+@MFLUA_TRUE@@WIN32_TRUE@am__EXEEXT_33 = call_mflua$(EXEEXT)
+@MFLUAJIT_TRUE@@WIN32_TRUE@am__EXEEXT_34 = call_mfluajit$(EXEEXT)
+@MP_TRUE@@WIN32_TRUE@am__EXEEXT_35 = call_mpost$(EXEEXT)
+@PMP_TRUE@@WIN32_TRUE@am__EXEEXT_36 = call_pmpost$(EXEEXT)
+@UPMP_TRUE@@WIN32_TRUE@am__EXEEXT_37 = call_upmpost$(EXEEXT)
+@HITEX_TRUE@am__EXEEXT_38 = himktables$(EXEEXT)
+@LUATEX_TRUE@@WIN32_TRUE@am__EXEEXT_39 = call_luatex$(EXEEXT)
+@LUAHBTEX_TRUE@@WIN32_TRUE@am__EXEEXT_40 = call_luahbtex$(EXEEXT)
+@LUAJITTEX_TRUE@@WIN32_TRUE@am__EXEEXT_41 = call_luajittex$(EXEEXT)
+@LUAJITHBTEX_TRUE@@WIN32_TRUE@am__EXEEXT_42 = \
@LUAJITHBTEX_TRUE@@WIN32_TRUE@ call_luajithbtex$(EXEEXT)
PROGRAMS = $(bin_PROGRAMS) $(noinst_PROGRAMS)
am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`;
@@ -1599,6 +1601,10 @@ tex_OBJECTS = $(dist_tex_OBJECTS) $(nodist_tex_OBJECTS)
@TEX_SYNCTEX_TRUE@am__DEPENDENCIES_13 = $(am__DEPENDENCIES_1)
tex_DEPENDENCIES = $(am__DEPENDENCIES_2) $(am__DEPENDENCIES_1) \
$(am__DEPENDENCIES_13)
+texprof_SOURCES = texprof.c
+texprof_OBJECTS = texprof-texprof.$(OBJEXT)
+texprofile_SOURCES = texprofile.c
+texprofile_OBJECTS = texprofile-texprofile.$(OBJEXT)
nodist_tftopl_OBJECTS = tftopl.$(OBJEXT)
tftopl_OBJECTS = $(nodist_tftopl_OBJECTS)
tftopl_LDADD = $(LDADD)
@@ -1803,7 +1809,8 @@ am__depfiles_remade = ./$(DEPDIR)/aleph-aleph-pool.Po \
./$(DEPDIR)/ptex-ptexini.Po ./$(DEPDIR)/tangle.Po \
./$(DEPDIR)/tangleboot.Po ./$(DEPDIR)/tex-tex-pool.Po \
./$(DEPDIR)/tex-tex0.Po ./$(DEPDIR)/tex-texextra.Po \
- ./$(DEPDIR)/tex-texini.Po ./$(DEPDIR)/tftopl.Po \
+ ./$(DEPDIR)/tex-texini.Po ./$(DEPDIR)/texprof-texprof.Po \
+ ./$(DEPDIR)/texprofile-texprofile.Po ./$(DEPDIR)/tftopl.Po \
./$(DEPDIR)/tie-tie.Po ./$(DEPDIR)/twill.Po \
./$(DEPDIR)/upbibtex-upbibtex.Po \
./$(DEPDIR)/updvitype-updvitype.Po ./$(DEPDIR)/upmpost-pmp.Po \
@@ -2516,8 +2523,8 @@ SOURCES = $(libff_a_SOURCES) $(libkanji_a_SOURCES) \
$(nodist_pmpost_SOURCES) $(nodist_pooltype_SOURCES) \
$(dist_ptex_SOURCES) $(nodist_ptex_SOURCES) $(synctex_SOURCES) \
$(nodist_tangle_SOURCES) $(nodist_tangleboot_SOURCES) \
- $(dist_tex_SOURCES) $(nodist_tex_SOURCES) \
- $(nodist_tftopl_SOURCES) $(nodist_tie_SOURCES) \
+ $(dist_tex_SOURCES) $(nodist_tex_SOURCES) texprof.c \
+ texprofile.c $(nodist_tftopl_SOURCES) $(nodist_tie_SOURCES) \
$(ttf2afm_SOURCES) $(EXTRA_ttf2afm_SOURCES) \
$(nodist_twill_SOURCES) $(upbibtex_SOURCES) \
$(nodist_upbibtex_SOURCES) $(updvitype_SOURCES) \
@@ -2546,10 +2553,11 @@ DIST_SOURCES = $(libff_a_SOURCES) $(libkanji_a_SOURCES) \
$(dist_mf_nowin_SOURCES) $(dist_mflua_SOURCES) \
$(dist_mfluajit_SOURCES) $(am__dist_pdftex_SOURCES_DIST) \
$(pdftosrc_SOURCES) $(dist_ptex_SOURCES) $(synctex_SOURCES) \
- $(am__dist_tex_SOURCES_DIST) $(ttf2afm_SOURCES) \
- $(EXTRA_ttf2afm_SOURCES) $(upbibtex_SOURCES) \
- $(updvitype_SOURCES) $(uppltotf_SOURCES) $(dist_uptex_SOURCES) \
- $(uptftopl_SOURCES) $(am__dist_xetex_SOURCES_DIST)
+ $(am__dist_tex_SOURCES_DIST) texprof.c texprofile.c \
+ $(ttf2afm_SOURCES) $(EXTRA_ttf2afm_SOURCES) \
+ $(upbibtex_SOURCES) $(updvitype_SOURCES) $(uppltotf_SOURCES) \
+ $(dist_uptex_SOURCES) $(uptftopl_SOURCES) \
+ $(am__dist_xetex_SOURCES_DIST)
RECURSIVE_TARGETS = all-recursive check-recursive cscopelist-recursive \
ctags-recursive dvi-recursive html-recursive info-recursive \
install-data-recursive install-dvi-recursive \
@@ -2761,15 +2769,15 @@ am__set_TESTS_bases = \
bases=`echo $$bases`
AM_TESTSUITE_SUMMARY_HEADER = ' for $(PACKAGE_STRING)'
RECHECK_LOGS = $(TEST_LOGS)
-am__EXEEXT_42 = bibtex.test dvicopy.test dvitype.test gftodvi.test \
+am__EXEEXT_43 = bibtex.test dvicopy.test dvitype.test gftodvi.test \
gftopk.test gftype.test mft.test patgen.test pktogf.test \
pktype.test pltotf.test pooltype.test tftopl.test vftovp.test \
vptovf.test weave.test twill.test
-am__EXEEXT_43 = $(am__EXEEXT_42) tests/bibtex-openout-test.pl \
+am__EXEEXT_44 = $(am__EXEEXT_43) tests/bibtex-openout-test.pl \
tests/bibtex-longline-test.pl tests/bibtex-mem.test \
tests/bibtex-bigauth.test tests/bibtex-auxinclude.test
-@WEB_TRUE@am__EXEEXT_44 = $(am__EXEEXT_43)
-am__EXEEXT_45 =
+@WEB_TRUE@am__EXEEXT_45 = $(am__EXEEXT_44)
+am__EXEEXT_46 =
TEST_SUITE_LOG = test-suite.log
am__test_logs1 = $(TESTS:=.log)
am__test_logs2 = $(am__test_logs1:@EXEEXT@.log=.log)
@@ -2822,8 +2830,9 @@ am__DIST_COMMON = $(dist_man_MANS) $(srcdir)/../../am/bin_links.am \
$(srcdir)/pdftexdir/am/ttf2afm.am \
$(srcdir)/pmpostdir/am/pmpost.am $(srcdir)/ptexdir/am/ptex.am \
$(srcdir)/silent-sh.in $(srcdir)/synctexdir/am/synctex.am \
- $(srcdir)/tangle-sh.in $(srcdir)/uptexdir/am/uptex.am \
- $(srcdir)/web2c-sh.in $(srcdir)/xetexdir/am/xetex.am \
+ $(srcdir)/tangle-sh.in $(srcdir)/texprofdir/am/texprof.am \
+ $(srcdir)/uptexdir/am/uptex.am $(srcdir)/web2c-sh.in \
+ $(srcdir)/xetexdir/am/xetex.am \
$(top_srcdir)/../../build-aux/ar-lib \
$(top_srcdir)/../../build-aux/compile \
$(top_srcdir)/../../build-aux/config.guess \
@@ -3002,7 +3011,7 @@ NM = @NM@
NMEDIT = @NMEDIT@
OBJCXX = @OBJCXX@
OBJCXXDEPMODE = @OBJCXXDEPMODE@
-OBJCXXFLAGS = @OBJCXXFLAGS@ $(am__append_111)
+OBJCXXFLAGS = @OBJCXXFLAGS@ $(am__append_112)
OBJDUMP = @OBJDUMP@
OBJEXT = @OBJEXT@
OTANGLE = @OTANGLE@
@@ -3151,8 +3160,8 @@ EXTRA_LIBRARIES = libmf.a libmflua.a libmfluaotfcc.a libmfluapotrace.a \
libluaharfbuzz.a libluajitharfbuzz.a libxetex.a libsynctex.a \
libmd5.a
EXTRA_LTLIBRARIES = libsynctex.la
-lib_LIBRARIES = $(am__append_123)
-lib_LTLIBRARIES = $(am__append_122)
+lib_LIBRARIES = $(am__append_124)
+lib_LTLIBRARIES = $(am__append_123)
dist_man_MANS = synctexdir/man1/synctex.1 synctexdir/man5/synctex.5
nodist_man_MANS =
TEST_EXTENSIONS = .pl .test
@@ -3261,6 +3270,16 @@ EXTRA_DIST = PROJECTS cftests cpascal.h help.h w2c/config.h \
ptexdir/tests/chcode-euc.pl ptexdir/tests/chcode-sjis.pl \
ptexdir/tests/skipjfmp.pl ptexdir/tests/skipjfmp.tfm \
ptexdir/ptrip/ptrip.diffs ptexdir/ptrip/texmf.cnf \
+ ptexdir/tests/ctrlsym.tex ptexdir/tests/ctrlsym.log \
+ ptexdir/tests/endlinechar.tex ptexdir/tests/endline.log \
+ ptexdir/tests/control-symbol.tex ptexdir/tests/jctrlsym.log \
+ ptexdir/tests/p-endlinea.typ ptexdir/tests/p-jctrlsyma.typ \
+ ptexdir/tests/let_char_token.tex ptexdir/tests/chartoken.log \
+ ptexdir/tests/ptexlineendmode.tex ptexdir/tests/p-lineenda.typ \
+ ptexdir/tests/printkanji.tex ptexdir/tests/pknj-s.log \
+ ptexdir/tests/pknj-e.log ptexdir/tests/p-pknj-sa.typ \
+ ptexdir/tests/p-pknj-ea.typ ptexdir/tests/ucs.tex \
+ ptexdir/tests/ucs-s.log ptexdir/tests/ucs-e.log \
$(uptex_web_srcs) $(uptex_ch_srcs) uptexdir/uptex.defines \
uptexdir/upbibtex.ch uptexdir/updvitype.ch \
uptexdir/uppltotf.ch uptexdir/uptftopl.ch uptexdir/COPYRIGHT \
@@ -3288,23 +3307,41 @@ EXTRA_DIST = PROJECTS cftests cpascal.h help.h w2c/config.h \
euptexdir/pdfprimitive.test \
euptexdir/tests/pdfprimitive-test.tex \
euptexdir/tests/pdfprimitive-euptex.log \
- euptexdir/tests/ctrlsym.log euptexdir/tests/ctrlsym.tex \
- hitexdir/ChangeLog $(hitex_web) $(hitex_tests) \
- hitexdir/tests/hello.tex hitexdir/tests/rule.tex \
- pdftexdir/regex/COPYING.LIB pdftexdir/regex/README \
- $(pdftex_ch_srcs) pdftexdir/pdftex.defines pdftexdir/ChangeLog \
- pdftexdir/NEWS pdftexdir/README pdftexdir/change-files.txt \
- $(pdftex_tests) tests/wprob.tex pdftexdir/tests/pdfimage.tex \
- tests/1-4.jpg tests/B.pdf tests/basic.tex \
- tests/lily-ledger-broken.png tests/expanded.tex \
- tests/expanded.txt tests/cnfline.tex tests/partoken-ok.tex \
- tests/partoken-xfail.tex $(ttf2afm_tests) \
- pdftexdir/tests/postV3.afm pdftexdir/tests/postV3.ttf \
- pdftexdir/tests/postV7.afm pdftexdir/tests/postV7.ttf \
- $(pdftosrc_tests) pdftexdir/tests/test-13.pdf \
- pdftexdir/tests/test-13.xref pdftexdir/tests/test-15.pdf \
- pdftexdir/tests/test-15.xref $(libluasocket_sources) \
- luatexdir/luasocket/src/ftp_lua.c \
+ euptexdir/tests/ctrlsym.tex euptexdir/tests/ctrlsym.log \
+ euptexdir/tests/endlinechar.tex euptexdir/tests/endline.log \
+ euptexdir/tests/control-symbol.tex \
+ euptexdir/tests/jctrlsym.log euptexdir/tests/eup-endlinea.typ \
+ euptexdir/tests/eup-jctrlsyma.typ \
+ euptexdir/tests/printkanji.tex euptexdir/tests/pknj-s.log \
+ euptexdir/tests/pknj-e.log euptexdir/tests/pknj-u.log \
+ euptexdir/tests/eup-pknj-sa.typ \
+ euptexdir/tests/eup-pknj-ea.typ \
+ euptexdir/tests/eup-pknj-ua.typ euptexdir/tests/postpena.tex \
+ euptexdir/tests/postpena.log euptexdir/tests/free_ixsp.tex \
+ euptexdir/tests/free_ixsp.log euptexdir/tests/free_pena.tex \
+ euptexdir/tests/free_pena.log euptexdir/tests/inhibitglue.tex \
+ euptexdir/tests/eup-inhibitgluea.typ \
+ euptexdir/tests/kinsoku-table.tex \
+ euptexdir/tests/kinsoku-table.log \
+ euptexdir/tests/kinsoku-search.tex \
+ euptexdir/tests/kinsoku-search.log euptexdir/tests/ucs.tex \
+ euptexdir/tests/ucs-s.log euptexdir/tests/ucs-e.log \
+ euptexdir/tests/ucs-u.log hitexdir/ChangeLog $(hitex_web) \
+ $(hitex_tests) hitexdir/tests/hello.tex \
+ hitexdir/tests/rule.tex pdftexdir/regex/COPYING.LIB \
+ pdftexdir/regex/README $(pdftex_ch_srcs) \
+ pdftexdir/pdftex.defines pdftexdir/ChangeLog pdftexdir/NEWS \
+ pdftexdir/README pdftexdir/change-files.txt $(pdftex_tests) \
+ tests/wprob.tex pdftexdir/tests/pdfimage.tex tests/1-4.jpg \
+ tests/B.pdf tests/basic.tex tests/lily-ledger-broken.png \
+ tests/expanded.tex tests/expanded.txt tests/cnfline.tex \
+ tests/partoken-ok.tex tests/partoken-xfail.tex \
+ $(ttf2afm_tests) pdftexdir/tests/postV3.afm \
+ pdftexdir/tests/postV3.ttf pdftexdir/tests/postV7.afm \
+ pdftexdir/tests/postV7.ttf $(pdftosrc_tests) \
+ pdftexdir/tests/test-13.pdf pdftexdir/tests/test-13.xref \
+ pdftexdir/tests/test-15.pdf pdftexdir/tests/test-15.xref \
+ $(libluasocket_sources) luatexdir/luasocket/src/ftp_lua.c \
luatexdir/luasocket/src/headers_lua.c \
luatexdir/luasocket/src/http_lua.c \
luatexdir/luasocket/src/ltn12_lua.c \
@@ -3484,9 +3521,10 @@ EXTRA_DIST = PROJECTS cftests cpascal.h help.h w2c/config.h \
luatexdir/luaharfbuzz/luaharfbuzz-scm-1.rockspec \
luatexdir/luaharfbuzz/Makefile luatexdir/luaharfbuzz/README.md \
luatexdir/luaharfbuzz/README-win.md \
- luatexdir/luaharfbuzz/THANKS $(xetex_web_srcs) \
- $(xetex_ch_srcs) xetexdir/xetex.defines xetexdir/ChangeLog \
- xetexdir/COPYING xetexdir/NEWS xetexdir/image/README \
+ luatexdir/luaharfbuzz/THANKS texprofdir/ChangeLog \
+ $(texprof_web) $(xetex_web_srcs) $(xetex_ch_srcs) \
+ xetexdir/xetex.defines xetexdir/ChangeLog xetexdir/COPYING \
+ xetexdir/NEWS xetexdir/image/README \
xetexdir/unicode-char-prep.pl xetexdir/xewebmac.tex \
$(xetex_tests) xetexdir/tests/bug73.log \
xetexdir/tests/bug73.tex xetexdir/tests/filedump.log \
@@ -3599,7 +3637,9 @@ DISTCLEANFILES = CXXLD.sh tangle.c tangle.h tangle.p tangle-web2c \
ptests/memtest.bib ptests/memtest?.* ptests/xstory.dvityp \
ptests/xpagenum.typ ptests/xptex[34]*.typ ptests/xcmr10.tfm \
ptests/xcmr10.pl ptests/xsample*.typ ptests/x*min10.* \
- ptests/xchcode*.* ptests/xskipjfmp.* ptrip.diffs \
+ ptests/xchcode*.* ptests/xskipjfmp.* ptrip.diffs p-ctrlsym.log \
+ p-ctrlsym.out p-ctrlsym.tex p-endline*.* p-jctrlsym*.* \
+ p-chartoken.* p-lineend.* p-pknj-*.* p-ucs*.* \
$(nodist_uptex_SOURCES) uptex.web uptex.ch uptex-web2c uptex.p \
uptex.pool uptex-tangle $(upweb_programs:=.c) \
$(upweb_programs:=.h) $(upweb_programs:=.p) \
@@ -3614,40 +3654,42 @@ DISTCLEANFILES = CXXLD.sh tangle.c tangle.h tangle.p tangle-web2c \
uptests/ygk256*.* uptests/xskipjfmp.* uptrip.diffs \
$(nodist_euptex_SOURCES) euptex.web euptex.ch euptex-web2c \
euptex.p euptex.pool euptex-tangle euptrip.diffs \
- pdfprimitive-euptex.* eup-ctrlsym.fmt eup-ctrlsym.log \
- eup-ctrlsym.out eup-ctrlsym.tex $(nodist_hitex_SOURCES) \
- $(nodist_hishrink_SOURCES) $(nodist_histretch_SOURCES) \
- hiformat-tangle hitex-tangle hello.log rule.log \
- $(nodist_pdftex_SOURCES) pdftex-final.ch pdftex-web2c pdftex.p \
- pdftex.pool pdftex-tangle pwprob.log pwprob.tex pdfimage.fmt \
- pdfimage.log pdfimage.pdf expanded.log cnfline.log \
- partoken-ok.log partoken-xfail.log pdftests/fn*.* postV3.afm \
- postV7.afm test-13.pdf test-13.xref test-15.pdf test-15.xref \
- $(nodist_libluatex_sources) luaimage.* luajitimage.* \
- $(nodist_xetex_SOURCES) xetex.web xetex-final.ch xetex-web2c \
- xetex.p xetex.pool xetex-tangle bug73.fmt bug73.log bug73.out \
- bug73.tex filedump.log filedump.out filedump.tex \
- xe-ctrlsym.fmt xe-ctrlsym.log xe-ctrlsym.out xe-ctrlsym.tex \
- xetests/fn*.* $(omegaware_programs:=.c) \
- $(omegaware_programs:=.h) $(omegaware_programs:=.p) \
- $(omegaware_programs:=-web2c) ofm2opl.web opl2ofm.web \
- ovf2ovp.web ovp2ovf.web omegaware/bad*.* \
- omegaware/tests/charwdr.* omegaware/tests/charwdv.* \
- omegaware/tests/xcheck* omegaware/tests/xlevel1.* \
- omegaware/tests/xlig*.* ofont*vf omegaware/tests/xpagenum.* \
- omegaware/tests/xofont* omegaware/tests/Cherokee.tfm \
- omegaware/tests/OCherokee.ofm omegaware/tests/OCherokee.opl \
- omegaware/tests/OCherokee.ovf omegaware/tests/xCherokee.* \
- omegaware/tests/xOCherokee.* ocftest.* omegaware/tests/xinbmp* \
- omegaware/tests/xoverbmp* omegaware/tests/xrealnum.* \
- omegaware/tests/xrepeated.* omegaware/tests/sample*.ofm \
- omegaware/tests/sample*.ovf omegaware/tests/sample1-h.opl \
- omegaware/tests/xsample*.out omegaware/tests/shortend.* \
- omegaware/tests/specialhex.ofm omegaware/tests/specialhex.opl \
- omegaware/tests/specialhex.ovf omegaware/tests/xspecialhex.* \
- omegaware/tests/yrepeat* omegaware/tests/*yarabic* \
- $(nodist_aleph_SOURCES) aleph.web aleph.ch aleph-web2c aleph.p \
- aleph.pool aleph-tangle
+ pdfprimitive-euptex.* eup-ctrlsym.log eup-ctrlsym.out \
+ eup-ctrlsym.tex eup-endline*.* eup-jctrlsym*.* eup-pknj-*.* \
+ eup-postpena*.* eup-free_*.* eup-inhibitglue*.* eup-kinsoku*.* \
+ eup-ucs*.* $(nodist_hitex_SOURCES) $(nodist_hishrink_SOURCES) \
+ $(nodist_histretch_SOURCES) hiformat-tangle hitex-tangle \
+ hello.log rule.log $(nodist_pdftex_SOURCES) pdftex-final.ch \
+ pdftex-web2c pdftex.p pdftex.pool pdftex-tangle pwprob.log \
+ pwprob.tex pdfimage.fmt pdfimage.log pdfimage.pdf expanded.log \
+ cnfline.log partoken-ok.log partoken-xfail.log pdftests/fn*.* \
+ postV3.afm postV7.afm test-13.pdf test-13.xref test-15.pdf \
+ test-15.xref $(nodist_libluatex_sources) luaimage.* \
+ luajitimage.* $(nodist_texprof_SOURCES) texprofile-tangle \
+ texprof-tangle $(nodist_xetex_SOURCES) xetex.web \
+ xetex-final.ch xetex-web2c xetex.p xetex.pool xetex-tangle \
+ bug73.fmt bug73.log bug73.out bug73.tex filedump.log \
+ filedump.out filedump.tex xe-ctrlsym.fmt xe-ctrlsym.log \
+ xe-ctrlsym.out xe-ctrlsym.tex xetests/fn*.* \
+ $(omegaware_programs:=.c) $(omegaware_programs:=.h) \
+ $(omegaware_programs:=.p) $(omegaware_programs:=-web2c) \
+ ofm2opl.web opl2ofm.web ovf2ovp.web ovp2ovf.web \
+ omegaware/bad*.* omegaware/tests/charwdr.* \
+ omegaware/tests/charwdv.* omegaware/tests/xcheck* \
+ omegaware/tests/xlevel1.* omegaware/tests/xlig*.* ofont*vf \
+ omegaware/tests/xpagenum.* omegaware/tests/xofont* \
+ omegaware/tests/Cherokee.tfm omegaware/tests/OCherokee.ofm \
+ omegaware/tests/OCherokee.opl omegaware/tests/OCherokee.ovf \
+ omegaware/tests/xCherokee.* omegaware/tests/xOCherokee.* \
+ ocftest.* omegaware/tests/xinbmp* omegaware/tests/xoverbmp* \
+ omegaware/tests/xrealnum.* omegaware/tests/xrepeated.* \
+ omegaware/tests/sample*.ofm omegaware/tests/sample*.ovf \
+ omegaware/tests/sample1-h.opl omegaware/tests/xsample*.out \
+ omegaware/tests/shortend.* omegaware/tests/specialhex.ofm \
+ omegaware/tests/specialhex.opl omegaware/tests/specialhex.ovf \
+ omegaware/tests/xspecialhex.* omegaware/tests/yrepeat* \
+ omegaware/tests/*yarabic* $(nodist_aleph_SOURCES) aleph.web \
+ aleph.ch aleph-web2c aleph.p aleph.pool aleph-tangle
CLEANFILES = $(EXTRA_PROGRAMS) $(EXTRA_LIBRARIES) $(EXTRA_LTLIBRARIES)
TRIPTRAP_CLEAN = $(am__append_8) $(am__append_18) $(am__append_27) \
$(am__append_36) $(am__append_44) $(am__append_60) \
@@ -3846,18 +3888,18 @@ tie_CPPFLAGS = $(AM_CPPFLAGS) -DNOT_WEB2C
initex_CPPFLAGS = -DEXEPROG=\"tex.exe\"
nodist_initex_SOURCES = callexe.c
initex_LDADD =
-tex_CPPFLAGS = $(AM_CPPFLAGS) $(am__append_127)
+tex_CPPFLAGS = $(AM_CPPFLAGS) $(am__append_128)
# With --enable-ipc, TeX may need to link with -lsocket.
-tex_LDADD = $(LDADD) $(ipc_socketlibs) $(am__append_128)
+tex_LDADD = $(LDADD) $(ipc_socketlibs) $(am__append_129)
# TeX C sources
tex_c_h = texini.c tex0.c texcoerce.h texd.h
nodist_tex_SOURCES = $(tex_c_h) tex-pool.c
-dist_tex_SOURCES = texextra.c $(am__append_130)
+dist_tex_SOURCES = texextra.c $(am__append_131)
# We must create texd.h before building the tex_OBJECTS.
-tex_prereq = texd.h $(am__append_129)
+tex_prereq = texd.h $(am__append_130)
tex_ch_srcs = \
tex.web \
tex.ch \
@@ -4357,21 +4399,21 @@ libmplib_web = mplibdir/mp.w mplibdir/psout.w mplibdir/svgout.w \
mplibdir/pngout.w mplibdir/mpmath.w mplibdir/mpmathbinary.w \
mplibdir/mpmathinterval.w mplibdir/mpmathdecimal.w \
mplibdir/mpmathdouble.w mplibdir/mpstrings.w mplibdir/tfmin.w
-etex_CPPFLAGS = $(AM_CPPFLAGS) $(ZLIB_INCLUDES) $(am__append_131)
+etex_CPPFLAGS = $(AM_CPPFLAGS) $(ZLIB_INCLUDES) $(am__append_132)
# With --enable-ipc, e-TeX may need to link with -lsocket.
-etex_LDADD = $(LDADD) $(ZLIB_LIBS) $(ipc_socketlibs) $(am__append_132)
+etex_LDADD = $(LDADD) $(ZLIB_LIBS) $(ipc_socketlibs) $(am__append_133)
etex_DEPENDENCIES = $(ZLIB_DEPEND)
# e-TeX C sources
etex_c_h = etexini.c etex0.c etexcoerce.h etexd.h
nodist_etex_SOURCES = $(etex_c_h) etex-pool.c
dist_etex_SOURCES = etexdir/etexextra.c etexdir/etexextra.h \
- etexdir/etex_version.h $(am__append_134)
+ etexdir/etex_version.h $(am__append_135)
# We must create etexd.h and etexdir/etex_version.h before building the
# etex_OBJECTS.
-etex_prereq = etexd.h etexdir/etex_version.h $(am__append_133)
+etex_prereq = etexd.h etexdir/etex_version.h $(am__append_134)
etex_web_srcs = \
tex.web \
etexdir/etex.ch
@@ -4436,7 +4478,10 @@ ptex_ch_srcs = \
ptex_tests = \
ptexdir/ptriptest.test \
ptexdir/pver.test \
- ptexdir/wcfname.test
+ ptexdir/wcfname.test \
+ ptexdir/ptex-ctrlsym.test \
+ ptexdir/ptex-prntknj.test \
+ ptexdir/ptex-ucs.test
uptex_cppflags = $(PTEXENC_INCLUDES) $(AM_CPPFLAGS) $(ZLIB_INCLUDES)
uptex_ldadd = libukanji.a $(pproglib) $(PTEXENC_LIBS) $(LDADD) $(ZLIB_LIBS)
@@ -4511,23 +4556,23 @@ upweb_tests = uptexdir/upbibtex.test uptexdir/updvitype.test \
uptexdir/newjfm.test uptexdir/uparse.test uptexdir/gkhuge.test \
uptexdir/gk256.test
euptex_CPPFLAGS = $(PTEXENC_INCLUDES) $(AM_CPPFLAGS) \
- -I$(srcdir)/libmd5 $(ZLIB_INCLUDES) $(am__append_135)
+ -I$(srcdir)/libmd5 $(ZLIB_INCLUDES) $(am__append_136)
# With --enable-ipc, e-upTeX may need to link with -lsocket.
euptex_LDADD = libukanji.a $(pproglib) $(PTEXENC_LIBS) $(LDADD) \
- $(ipc_socketlibs) libmd5.a $(ZLIB_LIBS) $(am__append_136)
+ $(ipc_socketlibs) libmd5.a $(ZLIB_LIBS) $(am__append_137)
euptex_DEPENDENCIES = libukanji.a $(pproglib) $(PTEXENC_DEPEND) $(default_dependencies) libmd5.a $(ZLIB_DEPEND)
# e-upTeX C sources
euptex_c_h = euptexini.c euptex0.c euptexcoerce.h euptexd.h
nodist_euptex_SOURCES = $(euptex_c_h) euptex-pool.c
dist_euptex_SOURCES = euptexdir/euptexextra.c euptexdir/euptexextra.h \
- $(am__append_138)
+ $(am__append_139)
# We must create euptexd.h and [eu]ptexdir/[eu]ptex_version.h before building the euptex_OBJECTS.
euptex_prereq = euptexd.h etexdir/etex_version.h \
uptexdir/ptex_version.h euptexdir/eptex_version.h \
- uptexdir/uptex_version.h $(am__append_137)
+ uptexdir/uptex_version.h $(am__append_138)
euptex_web_srcs = \
tex.web \
etexdir/etex.ch \
@@ -4560,10 +4605,13 @@ euptex_ch_srcs = \
euptex_tests = \
euptexdir/euptriptest.test euptexdir/eptriptest.test \
euptexdir/pdfprimitive.test \
- euptexdir/euptex-ctrlsym.test \
euptexdir/eupver.test \
euptexdir/upkcat.test \
- euptexdir/wcfname.test euptexdir/wcfname0.test
+ euptexdir/wcfname.test euptexdir/wcfname0.test \
+ euptexdir/euptex-ctrlsym.test \
+ euptexdir/euptex-prntknj.test \
+ euptexdir/euptex-kinsoku.test \
+ euptexdir/euptex-ucs.test
# At the moment, these are the only man pages handled in the
@@ -4572,6 +4620,9 @@ euptex_tests = \
@HITEX_TRUE@man1_MANS = hitexdir/man/hishrink.man hitexdir/man/histretch.man \
@HITEX_TRUE@ hitexdir/man/hitex.man
+
+# See hitex.am for comments about handling man.
+@TEXPROF_TRUE@man1_MANS = texprofdir/man/texprof.man texprofdir/man/texprofile.man
hitex_CPPFLAGS = $(AM_CPPFLAGS) $(ZLIB_INCLUDES) -I$(srcdir)/libmd5 \
-DINIT -DSTAT -DWEB2CVERSION="\"${WEB2CVERSION}\""
# -DDEBUG drops into an interactive debugging session.
@@ -4660,7 +4711,7 @@ pdftex_dependencies = $(proglib) $(KPATHSEA_DEPEND) $(LIBPNG_DEPEND) \
# Force Automake to use CXXLD for linking
nodist_EXTRA_pdftex_SOURCES = dummy.cxx
pdf_tangle = WEBINPUTS=.:$(srcdir)/pdftexdir AM_V_P=$(AM_V_P) $(SHELL) ./tangle-sh $@ $(TANGLE)
-pdftex_CPPFLAGS = $(pdftex_cppflags) $(am__append_139)
+pdftex_CPPFLAGS = $(pdftex_cppflags) $(am__append_140)
pdftex_CXXFLAGS = $(WARNING_CXXFLAGS)
# With --enable-ipc, pdfTeX may need to link with -lsocket.
@@ -4673,7 +4724,7 @@ pdftex_c_h = pdftexini.c pdftex0.c pdftexcoerce.h pdftexd.h
nodist_pdftex_SOURCES = $(pdftex_c_h) pdftex-pool.c
dist_pdftex_SOURCES = pdftexdir/pdftexextra.c pdftexdir/pdftexextra.h \
pdftexdir/pdftex_version.h pdftexdir/etex_version.h \
- $(am__append_140)
+ $(am__append_141)
pdftex_ch_srcs = \
pdftexdir/pdftex.web \
pdftexdir/tex.ch0 \
@@ -5348,6 +5399,26 @@ libluaharfbuzz_a_SOURCES = \
luatexdir/luaharfbuzz/src/luaharfbuzz/variation.c
libluajitharfbuzz_a_SOURCES = $(libluaharfbuzz_a_SOURCES)
+texprof_CPPFLAGS = $(AM_CPPFLAGS) -I$(srcdir)/libmd5 -DINIT -DSTAT \
+ -DWEB2CVERSION="\"${WEB2CVERSION}\""
+# -DDEBUG drops into an interactive debugging session.
+texprof_LDADD = $(KPATHSEA_LIBS) libmd5.a
+texprof_DEPENDENCIES = $(KPATHSEA_DEPEND) libmd5.a
+#
+texprofile_CPPFLAGS = $(AM_CPPFLAGS)
+texprofile_LDADD =
+texprofile_DEPENDENCIES =
+#
+# TeXprof CWEB sources
+texprof_web = texprofdir/texprof.w
+texprofile_web = texprofdir/texprofile.w
+
+# Creating several files: need stamp file and two rules with identical recipes
+texprof_ctangle_sh = CWEBINPUTS=$(srcdir)/texprofdir AM_V_P=$(AM_V_P) $(SHELL) ./tangle-sh $@ $(CTANGLE)
+
+# For each cweb (.w) source file, list the files generated.
+texprof_c = texprof.c
+texprofile_c = texprofile.c
# Force Automake to use CXXLD for linking
nodist_EXTRA_xetex_SOURCES = dummy.cxx
@@ -5356,18 +5427,18 @@ libxetex = libxetex.a
xetex_cppflags = $(AM_CPPFLAGS) -I$(srcdir)/xetexdir $(ICU_INCLUDES) \
$(FREETYPE2_INCLUDES) $(TECKIT_INCLUDES) $(HARFBUZZ_INCLUDES) \
$(GRAPHITE2_INCLUDES) $(LIBPNG_INCLUDES) $(ZLIB_INCLUDES) \
- $(PPLIB_INCLUDES) -I$(srcdir)/libmd5 $(am__append_110) \
- $(am__append_112)
+ $(PPLIB_INCLUDES) -I$(srcdir)/libmd5 $(am__append_111) \
+ $(am__append_113)
xetex_ldadd = $(libxetex) $(HARFBUZZ_LIBS) $(GRAPHITE2_LIBS) \
$(ICU_LIBS) $(ICU_LIBS_EXTRA) $(TECKIT_LIBS) $(LIBPNG_LIBS) \
$(FREETYPE2_LIBS) $(PPLIB_LIBS) $(ZLIB_LIBS) libmd5.a \
- $(am__append_113)
+ $(am__append_114)
xetex_dependencies = $(proglib) $(KPATHSEA_DEPEND) $(ICU_DEPEND) \
$(TECKIT_DEPEND) $(HARFBUZZ_DEPEND) $(GRAPHITE2_DEPEND) \
$(LIBPNG_DEPEND) $(FREETYPE2_DEPEND) $(ZLIB_DEPEND) \
$(PPLIB_DEPEND) libmd5.a
@XETEX_MACOSX_TRUE@xetex_LDFLAGS = -framework ApplicationServices -framework Cocoa
-xetex_CPPFLAGS = $(xetex_cppflags) $(am__append_141)
+xetex_CPPFLAGS = $(xetex_cppflags) $(am__append_142)
xetex_CFLAGS = $(WARNING_CFLAGS)
xetex_CXXFLAGS = # $(WARNING_CXXFLAGS)
xetex_LDADD = $(xetex_ldadd) $(LDADD) $(ipc_socketlibs)
@@ -5376,7 +5447,7 @@ xetex_c_h = xetexini.c xetex0.c xetexcoerce.h xetexd.h
nodist_xetex_SOURCES = $(xetex_c_h) xetex-pool.c
dist_xetex_SOURCES = xetexdir/xetexextra.c xetexdir/xetexextra.h \
xetexdir/etex_version.h xetexdir/xetex_version.h \
- $(am__append_142)
+ $(am__append_143)
xetex_ch_srcs = \
xetexdir/xetex.web \
xetexdir/tex.ch0 \
@@ -5410,7 +5481,7 @@ libxetex_a_SOURCES = xetexdir/XeTeXFontInst.cpp \
xetexdir/image/jpegimage.h xetexdir/image/mfileio.c \
xetexdir/image/mfileio.h xetexdir/image/numbers.c \
xetexdir/image/numbers.h xetexdir/image/pngimage.c \
- xetexdir/image/pngimage.h $(am__append_114) $(am__append_115)
+ xetexdir/image/pngimage.h $(am__append_115) $(am__append_116)
# We must create xetexd.h etc. before building the libxetex_a_OBJECTS.
libxetex_prereq = xetexd.h $(xetex_dependencies)
@@ -5517,13 +5588,13 @@ aleph_tests = alephdir/aleph.test
synctex_SOURCES = \
synctexdir/synctex_main.c
-synctex_CPPFLAGS = -I$(srcdir)/synctexdir $(am__append_124)
-synctex_LDADD = $(libsynctex) $(ZLIB_LIBS) $(am__append_125)
+synctex_CPPFLAGS = -I$(srcdir)/synctexdir $(am__append_125)
+synctex_LDADD = $(libsynctex) $(ZLIB_LIBS) $(am__append_126)
libsynctex = $(LTLIBSYNCTEX) $(LIBSYNCTEX)
libsynctex_la_CPPFLAGS = -I$(srcdir)/synctexdir $(ZLIB_INCLUDES) -DSYNCTEX_USE_LOCAL_HEADER
libsynctex_a_CPPFLAGS = $(libsynctex_la_CPPFLAGS)
libsynctex_la_LDFLAGS = -rpath @libdir@ -bindir @bindir@ -no-undefined -version-info $(SYNCTEX_LT_VERSINFO)
-libsynctex_la_LIBADD = $(ZLIB_LIBS) $(am__append_126)
+libsynctex_la_LIBADD = $(ZLIB_LIBS) $(am__append_127)
libsynctex_la_SOURCES = \
synctexdir/synctex_parser.c \
synctexdir/synctex_parser_local.h \
@@ -5605,7 +5676,7 @@ all: ff-config.h
.SUFFIXES: .c .cc .cin .cpp .cxx .h .hin .lo .log .mm .o .obj .p .pin .pl .pl$(EXEEXT) .test .test$(EXEEXT) .trs
am--refresh: Makefile
@:
-$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(srcdir)/../../am/dist_hook.am $(srcdir)/am/bootstrap.am $(srcdir)/am/web.am $(srcdir)/am/cweb.am $(srcdir)/am/texmf.am $(srcdir)/mfluadir/am/mflua.am $(srcdir)/mfluadir/am/mfluaotfcc.am $(srcdir)/mfluadir/am/mfluapotrace.am $(srcdir)/mfluadir/am/mflualpeg.am $(srcdir)/mfluajitdir/am/mfluajit.am $(srcdir)/mfluajitdir/am/mfluajitlpeg.am $(srcdir)/mplibdir/am/mplib.am $(srcdir)/pmpostdir/am/pmpost.am $(srcdir)/mplibdir/am/libmputil.am $(srcdir)/mplibdir/am/libmplib.am $(srcdir)/etexdir/am/etex.am $(srcdir)/ptexdir/am/ptex.am $(srcdir)/uptexdir/am/uptex.am $(srcdir)/euptexdir/am/euptex.am $(srcdir)/hitexdir/am/hitex.am $(srcdir)/pdftexdir/am/libpdftex.am $(srcdir)/pdftexdir/am/pdftex.am $(srcdir)/pdftexdir/am/ttf2afm.am $(srcdir)/pdftexdir/am/pdftosrc.am $(srcdir)/luatexdir/am/luasocket.am $(srcdir)/luatexdir/am/luamisc.am $(srcdir)/luatexdir/am/libunilib.am $(srcdir)/luatexdir/am/luafontforge.am $(srcdir)/luatexdir/am/libluatex.am $(srcdir)/luatexdir/am/luaffi.am $(srcdir)/luatexdir/am/luatex.am $(srcdir)/luatexdir/am/luaharfbuzz.am $(srcdir)/xetexdir/am/xetex.am $(srcdir)/omegaware/am/omegaware.am $(srcdir)/alephdir/am/aleph.am $(srcdir)/synctexdir/am/synctex.am $(srcdir)/libmd5/am/md5.am $(srcdir)/../../am/bin_links.am $(am__configure_deps)
+$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(srcdir)/../../am/dist_hook.am $(srcdir)/am/bootstrap.am $(srcdir)/am/web.am $(srcdir)/am/cweb.am $(srcdir)/am/texmf.am $(srcdir)/mfluadir/am/mflua.am $(srcdir)/mfluadir/am/mfluaotfcc.am $(srcdir)/mfluadir/am/mfluapotrace.am $(srcdir)/mfluadir/am/mflualpeg.am $(srcdir)/mfluajitdir/am/mfluajit.am $(srcdir)/mfluajitdir/am/mfluajitlpeg.am $(srcdir)/mplibdir/am/mplib.am $(srcdir)/pmpostdir/am/pmpost.am $(srcdir)/mplibdir/am/libmputil.am $(srcdir)/mplibdir/am/libmplib.am $(srcdir)/etexdir/am/etex.am $(srcdir)/ptexdir/am/ptex.am $(srcdir)/uptexdir/am/uptex.am $(srcdir)/euptexdir/am/euptex.am $(srcdir)/hitexdir/am/hitex.am $(srcdir)/pdftexdir/am/libpdftex.am $(srcdir)/pdftexdir/am/pdftex.am $(srcdir)/pdftexdir/am/ttf2afm.am $(srcdir)/pdftexdir/am/pdftosrc.am $(srcdir)/luatexdir/am/luasocket.am $(srcdir)/luatexdir/am/luamisc.am $(srcdir)/luatexdir/am/libunilib.am $(srcdir)/luatexdir/am/luafontforge.am $(srcdir)/luatexdir/am/libluatex.am $(srcdir)/luatexdir/am/luaffi.am $(srcdir)/luatexdir/am/luatex.am $(srcdir)/luatexdir/am/luaharfbuzz.am $(srcdir)/texprofdir/am/texprof.am $(srcdir)/xetexdir/am/xetex.am $(srcdir)/omegaware/am/omegaware.am $(srcdir)/alephdir/am/aleph.am $(srcdir)/synctexdir/am/synctex.am $(srcdir)/libmd5/am/md5.am $(srcdir)/../../am/bin_links.am $(am__configure_deps)
@for dep in $?; do \
case '$(am__configure_deps)' in \
*$$dep*) \
@@ -5627,7 +5698,7 @@ Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
echo ' cd $(top_builddir) && $(SHELL) ./config.status $@ $(am__maybe_remake_depfiles)'; \
cd $(top_builddir) && $(SHELL) ./config.status $@ $(am__maybe_remake_depfiles);; \
esac;
-$(srcdir)/../../am/dist_hook.am $(srcdir)/am/bootstrap.am $(srcdir)/am/web.am $(srcdir)/am/cweb.am $(srcdir)/am/texmf.am $(srcdir)/mfluadir/am/mflua.am $(srcdir)/mfluadir/am/mfluaotfcc.am $(srcdir)/mfluadir/am/mfluapotrace.am $(srcdir)/mfluadir/am/mflualpeg.am $(srcdir)/mfluajitdir/am/mfluajit.am $(srcdir)/mfluajitdir/am/mfluajitlpeg.am $(srcdir)/mplibdir/am/mplib.am $(srcdir)/pmpostdir/am/pmpost.am $(srcdir)/mplibdir/am/libmputil.am $(srcdir)/mplibdir/am/libmplib.am $(srcdir)/etexdir/am/etex.am $(srcdir)/ptexdir/am/ptex.am $(srcdir)/uptexdir/am/uptex.am $(srcdir)/euptexdir/am/euptex.am $(srcdir)/hitexdir/am/hitex.am $(srcdir)/pdftexdir/am/libpdftex.am $(srcdir)/pdftexdir/am/pdftex.am $(srcdir)/pdftexdir/am/ttf2afm.am $(srcdir)/pdftexdir/am/pdftosrc.am $(srcdir)/luatexdir/am/luasocket.am $(srcdir)/luatexdir/am/luamisc.am $(srcdir)/luatexdir/am/libunilib.am $(srcdir)/luatexdir/am/luafontforge.am $(srcdir)/luatexdir/am/libluatex.am $(srcdir)/luatexdir/am/luaffi.am $(srcdir)/luatexdir/am/luatex.am $(srcdir)/luatexdir/am/luaharfbuzz.am $(srcdir)/xetexdir/am/xetex.am $(srcdir)/omegaware/am/omegaware.am $(srcdir)/alephdir/am/aleph.am $(srcdir)/synctexdir/am/synctex.am $(srcdir)/libmd5/am/md5.am $(srcdir)/../../am/bin_links.am $(am__empty):
+$(srcdir)/../../am/dist_hook.am $(srcdir)/am/bootstrap.am $(srcdir)/am/web.am $(srcdir)/am/cweb.am $(srcdir)/am/texmf.am $(srcdir)/mfluadir/am/mflua.am $(srcdir)/mfluadir/am/mfluaotfcc.am $(srcdir)/mfluadir/am/mfluapotrace.am $(srcdir)/mfluadir/am/mflualpeg.am $(srcdir)/mfluajitdir/am/mfluajit.am $(srcdir)/mfluajitdir/am/mfluajitlpeg.am $(srcdir)/mplibdir/am/mplib.am $(srcdir)/pmpostdir/am/pmpost.am $(srcdir)/mplibdir/am/libmputil.am $(srcdir)/mplibdir/am/libmplib.am $(srcdir)/etexdir/am/etex.am $(srcdir)/ptexdir/am/ptex.am $(srcdir)/uptexdir/am/uptex.am $(srcdir)/euptexdir/am/euptex.am $(srcdir)/hitexdir/am/hitex.am $(srcdir)/pdftexdir/am/libpdftex.am $(srcdir)/pdftexdir/am/pdftex.am $(srcdir)/pdftexdir/am/ttf2afm.am $(srcdir)/pdftexdir/am/pdftosrc.am $(srcdir)/luatexdir/am/luasocket.am $(srcdir)/luatexdir/am/luamisc.am $(srcdir)/luatexdir/am/libunilib.am $(srcdir)/luatexdir/am/luafontforge.am $(srcdir)/luatexdir/am/libluatex.am $(srcdir)/luatexdir/am/luaffi.am $(srcdir)/luatexdir/am/luatex.am $(srcdir)/luatexdir/am/luaharfbuzz.am $(srcdir)/texprofdir/am/texprof.am $(srcdir)/xetexdir/am/xetex.am $(srcdir)/omegaware/am/omegaware.am $(srcdir)/alephdir/am/aleph.am $(srcdir)/synctexdir/am/synctex.am $(srcdir)/libmd5/am/md5.am $(srcdir)/../../am/bin_links.am $(am__empty):
$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
$(SHELL) ./config.status --recheck
@@ -8239,6 +8310,14 @@ tex$(EXEEXT): $(tex_OBJECTS) $(tex_DEPENDENCIES) $(EXTRA_tex_DEPENDENCIES)
@rm -f tex$(EXEEXT)
$(AM_V_CCLD)$(LINK) $(tex_OBJECTS) $(tex_LDADD) $(LIBS)
+texprof$(EXEEXT): $(texprof_OBJECTS) $(texprof_DEPENDENCIES) $(EXTRA_texprof_DEPENDENCIES)
+ @rm -f texprof$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(texprof_OBJECTS) $(texprof_LDADD) $(LIBS)
+
+texprofile$(EXEEXT): $(texprofile_OBJECTS) $(texprofile_DEPENDENCIES) $(EXTRA_texprofile_DEPENDENCIES)
+ @rm -f texprofile$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(texprofile_OBJECTS) $(texprofile_LDADD) $(LIBS)
+
tftopl$(EXEEXT): $(tftopl_OBJECTS) $(tftopl_DEPENDENCIES) $(EXTRA_tftopl_DEPENDENCIES)
@rm -f tftopl$(EXEEXT)
$(AM_V_CCLD)$(LINK) $(tftopl_OBJECTS) $(tftopl_LDADD) $(LIBS)
@@ -8548,6 +8627,8 @@ distclean-compile:
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tex-tex0.Po@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tex-texextra.Po@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tex-texini.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/texprof-texprof.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/texprofile-texprofile.Po@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tftopl.Po@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tie-tie.Po@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/twill.Po@am__quote@ # am--include-marker
@@ -18162,6 +18243,34 @@ tex-tex-pool.obj: tex-pool.c
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(tex_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o tex-tex-pool.obj `if test -f 'tex-pool.c'; then $(CYGPATH_W) 'tex-pool.c'; else $(CYGPATH_W) '$(srcdir)/tex-pool.c'; fi`
+texprof-texprof.o: texprof.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(texprof_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT texprof-texprof.o -MD -MP -MF $(DEPDIR)/texprof-texprof.Tpo -c -o texprof-texprof.o `test -f 'texprof.c' || echo '$(srcdir)/'`texprof.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/texprof-texprof.Tpo $(DEPDIR)/texprof-texprof.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='texprof.c' object='texprof-texprof.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(texprof_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o texprof-texprof.o `test -f 'texprof.c' || echo '$(srcdir)/'`texprof.c
+
+texprof-texprof.obj: texprof.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(texprof_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT texprof-texprof.obj -MD -MP -MF $(DEPDIR)/texprof-texprof.Tpo -c -o texprof-texprof.obj `if test -f 'texprof.c'; then $(CYGPATH_W) 'texprof.c'; else $(CYGPATH_W) '$(srcdir)/texprof.c'; fi`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/texprof-texprof.Tpo $(DEPDIR)/texprof-texprof.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='texprof.c' object='texprof-texprof.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(texprof_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o texprof-texprof.obj `if test -f 'texprof.c'; then $(CYGPATH_W) 'texprof.c'; else $(CYGPATH_W) '$(srcdir)/texprof.c'; fi`
+
+texprofile-texprofile.o: texprofile.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(texprofile_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT texprofile-texprofile.o -MD -MP -MF $(DEPDIR)/texprofile-texprofile.Tpo -c -o texprofile-texprofile.o `test -f 'texprofile.c' || echo '$(srcdir)/'`texprofile.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/texprofile-texprofile.Tpo $(DEPDIR)/texprofile-texprofile.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='texprofile.c' object='texprofile-texprofile.o' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(texprofile_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o texprofile-texprofile.o `test -f 'texprofile.c' || echo '$(srcdir)/'`texprofile.c
+
+texprofile-texprofile.obj: texprofile.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(texprofile_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT texprofile-texprofile.obj -MD -MP -MF $(DEPDIR)/texprofile-texprofile.Tpo -c -o texprofile-texprofile.obj `if test -f 'texprofile.c'; then $(CYGPATH_W) 'texprofile.c'; else $(CYGPATH_W) '$(srcdir)/texprofile.c'; fi`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/texprofile-texprofile.Tpo $(DEPDIR)/texprofile-texprofile.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='texprofile.c' object='texprofile-texprofile.obj' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(texprofile_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o texprofile-texprofile.obj `if test -f 'texprofile.c'; then $(CYGPATH_W) 'texprofile.c'; else $(CYGPATH_W) '$(srcdir)/texprofile.c'; fi`
+
tie-tie.o: tie.c
@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(tie_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT tie-tie.o -MD -MP -MF $(DEPDIR)/tie-tie.Tpo -c -o tie-tie.o `test -f 'tie.c' || echo '$(srcdir)/'`tie.c
@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/tie-tie.Tpo $(DEPDIR)/tie-tie.Po
@@ -19838,6 +19947,8 @@ distclean: distclean-recursive
-rm -f ./$(DEPDIR)/tex-tex0.Po
-rm -f ./$(DEPDIR)/tex-texextra.Po
-rm -f ./$(DEPDIR)/tex-texini.Po
+ -rm -f ./$(DEPDIR)/texprof-texprof.Po
+ -rm -f ./$(DEPDIR)/texprofile-texprofile.Po
-rm -f ./$(DEPDIR)/tftopl.Po
-rm -f ./$(DEPDIR)/tie-tie.Po
-rm -f ./$(DEPDIR)/twill.Po
@@ -20614,6 +20725,8 @@ maintainer-clean: maintainer-clean-recursive
-rm -f ./$(DEPDIR)/tex-tex0.Po
-rm -f ./$(DEPDIR)/tex-texextra.Po
-rm -f ./$(DEPDIR)/tex-texini.Po
+ -rm -f ./$(DEPDIR)/texprof-texprof.Po
+ -rm -f ./$(DEPDIR)/texprofile-texprofile.Po
-rm -f ./$(DEPDIR)/tftopl.Po
-rm -f ./$(DEPDIR)/tie-tie.Po
-rm -f ./$(DEPDIR)/twill.Po
@@ -21928,7 +22041,10 @@ ptex.web: tie$(EXEEXT) $(ptex_web_srcs)
ptex.ch: tie$(EXEEXT) ptex.web $(ptex_ch_srcs)
$(tie_c) ptex.web $(ptex_ch_srcs)
ptexdir/ptriptest.log: ptex$(EXEEXT) dvitype$(EXEEXT) pltotf$(EXEEXT) tftopl$(EXEEXT)
-ptexdir/pver.log ptexdir/wcfname.log: ptex$(EXEEXT)
+ptexdir/pver.log ptexdir/wcfname.log \
+ ptexdir/ptex-ctrlsym.log ptexdir/ptex-prntknj.log \
+ ptexdir/ptex-ucs.log: ptex$(EXEEXT)
+
ptrip.diffs: ptex$(EXEEXT) dvitype$(EXEEXT) pltotf$(EXEEXT) tftopl$(EXEEXT)
$(triptrap_diffs) $@
.PHONY: ptrip-clean
@@ -22011,6 +22127,7 @@ uptexdir/sample.log: updvitype$(EXEEXT)
uptexdir/yokotate.log uptexdir/skipjfmp.log \
uptexdir/newjfm.log uptexdir/uparse.log \
uptexdir/gkhuge.log uptexdir/gk256.log: uppltotf$(EXEEXT) uptftopl$(EXEEXT)
+
uptrip.diffs: uptex$(EXEEXT) dvitype$(EXEEXT) pltotf$(EXEEXT) tftopl$(EXEEXT)
$(triptrap_diffs) $@
.PHONY: uptrip-clean
@@ -22044,9 +22161,12 @@ euptex.ch: tie$(EXEEXT) euptex.web $(euptex_ch_srcs)
euptexdir/euptriptest.log euptexdir/eptriptest.log: \
euptex$(EXEEXT) dvitype$(EXEEXT) pltotf$(EXEEXT) tftopl$(EXEEXT)
euptexdir/pdfprimitive.log: euptex$(EXEEXT)
-euptexdir/euptex-ctrlsym.log \
- euptexdir/eupver.log euptexdir/upkcat.log \
- euptexdir/wcfname.log euptexdir/wcfname0.log: euptex$(EXEEXT)
+euptexdir/eupver.log euptexdir/upkcat.log \
+ euptexdir/wcfname.log euptexdir/wcfname0.log \
+ euptexdir/euptex-ctrlsym.log euptexdir/euptex-prntknj.log \
+ euptexdir/euptex-kinsoku.log \
+ euptexdir/euptex-ucs.log: euptex$(EXEEXT)
+
euptrip.diffs: euptex$(EXEEXT) dvitype$(EXEEXT) pltotf$(EXEEXT) tftopl$(EXEEXT)
$(triptrap_diffs) $@
.PHONY: euptrip-clean
@@ -22240,6 +22360,40 @@ luatexdir/luajithbtex.log luatexdir/luajithbimage.log: luajithbtex$(EXEEXT)
$(libluaharfbuzz_a_OBJECTS): $(LUA_DEPEND)
$(libluajitharfbuzz_a_OBJECTS): $(LUAJIT_DEPEND)
+
+# convenience target to build all the programs here.
+@TEXPROF_TRUE@texprofall: texprof texprofile
+
+$(texprof_c): texprof-tangle
+$(texprofile_c): texprofile-tangle
+
+#
+texprof-tangle: ctangle$(EXEEXT) texprofdir/texprof.w tangle-sh
+ $(texprof_ctangle_sh) texprof
+
+texprofile-tangle: ctangle$(EXEEXT) texprofdir/texprofile.w tangle-sh
+ $(texprof_ctangle_sh) texprofile
+
+#
+#texprof_tests = texprofdir/tests/hello.test \
+# texprofdir/tests/rule.test
+#
+#texprofdir/tests/hello.log: texprof$(EXEEXT) histretch$(EXEEXT)
+#texprofdir/tests/rule.log: texprof$(EXEEXT) histretch$(EXEEXT) hishrink$(EXEEXT)
+#
+#EXTRA_DIST += $(texprof_tests)
+#
+#if TEXPROF
+#TESTS += $(texprof_tests)
+#endif TEXPROF
+#
+#EXTRA_DIST += texprofdir/tests/hello.tex
+#DISTCLEANFILES += hello.log
+
+#EXTRA_DIST += texprofdir/tests/rule.tex
+#DISTCLEANFILES += rule.log
+
+# (end of texprof.am)
$(xetex_OBJECTS): $(xetex_prereq)
$(xetex_c_h): xetex-web2c
diff --git a/Build/source/texk/web2c/ac/web2c.ac b/Build/source/texk/web2c/ac/web2c.ac
index 9901c93c40c..471c3250f66 100644
--- a/Build/source/texk/web2c/ac/web2c.ac
+++ b/Build/source/texk/web2c/ac/web2c.ac
@@ -43,6 +43,7 @@ m4_define([kpse_tex_progs], [dnl
[[luajithbtex], [yes], [], [LuaHBJITTeX], [pplib libpng zziplib luajit harfbuzz]],
[[mp], [yes], [], [MetaPost], [mpfi mpfr cairo libpng]],
[[pmp], [yes], [], [pMetaPost], [mpfi mpfr cairo libpng ptexenc]],
+[[texprof], [yes], [], [TeXprof], []],
[[upmp], [yes], [], [upMetaPost], [mpfi mpfr cairo libpng ptexenc]],
[[xetex], [yes], [yes], [XeTeX], [pplib libpng freetype2 teckit harfbuzz]],
])[]dnl
diff --git a/Build/source/texk/web2c/bibtex.ch b/Build/source/texk/web2c/bibtex.ch
index c8d566ee60b..b9661c65cdd 100644
--- a/Build/source/texk/web2c/bibtex.ch
+++ b/Build/source/texk/web2c/bibtex.ch
@@ -13,6 +13,12 @@
% 03/09/90 `int' is a bad variable name for C.
% (more recent changes in the ChangeLog)
+@x [0]
+\def\(#1){} % this is used to make section names sort themselves better
+@y
+\def\({} % this is used to make section names sort themselves better
+@z
+
% [0] Let bibtex.tex work with latest webmac (which defines \ET, hence
% making E active loses).
@x
@@ -104,7 +110,7 @@ begin
initialize;
print_ln(banner);@/
@y
-@<Define |parse_arguments|@>
+@<Define \(|parse_arguments|@>
begin
standard_input := stdin;
standard_output := stdout;
@@ -1633,7 +1639,7 @@ itself will get a new section number.
@y
@d argument_is (#) == (strcmp (long_options[option_index].name, #) = 0)
-@<Define |parse_arguments|@> =
+@<Define \(|parse_arguments|@> =
procedure parse_arguments;
const n_options = 4; {Pascal won't count array lengths for us.}
var @!long_options: array[0..n_options] of getopt_struct;
diff --git a/Build/source/texk/web2c/configure b/Build/source/texk/web2c/configure
index 7f9c194d1d1..1b30570db8f 100755
--- a/Build/source/texk/web2c/configure
+++ b/Build/source/texk/web2c/configure
@@ -800,6 +800,8 @@ XETEX_FALSE
XETEX_TRUE
UPMP_FALSE
UPMP_TRUE
+TEXPROF_FALSE
+TEXPROF_TRUE
PMP_FALSE
PMP_TRUE
MP_FALSE
@@ -1047,6 +1049,7 @@ enable_luahbtex
enable_luajithbtex
enable_mp
enable_pmp
+enable_texprof
enable_upmp
enable_xetex
enable_xetex_synctex
@@ -1792,6 +1795,7 @@ Optional Features:
--disable-luajithbtex do not compile and install LuaHBJITTeX
--disable-mp do not compile and install MetaPost
--disable-pmp do not compile and install pMetaPost
+ --disable-texprof do not compile and install TeXprof
--disable-upmp do not compile and install upMetaPost
--disable-xetex do not compile and install XeTeX
--disable-xetex-synctex build XeTeX without SyncTeX support
@@ -20811,6 +20815,17 @@ test "x$enable_web2c:$enable_pmp" = xyes:yes && {
need_libpng=yes
need_ptexenc=yes
}
+# Check whether --enable-texprof was given.
+if test ${enable_texprof+y}
+then :
+ enableval=$enable_texprof;
+fi
+case $enable_texprof in #(
+ yes | no) :
+ ;; #(
+ *) :
+ enable_texprof=yes ;;
+esac
# Check whether --enable-upmp was given.
if test ${enable_upmp+y}
then :
@@ -23601,6 +23616,13 @@ else
PMP_TRUE='#'
PMP_FALSE=
fi
+ if test "x$enable_texprof" = xyes; then
+ TEXPROF_TRUE=
+ TEXPROF_FALSE='#'
+else
+ TEXPROF_TRUE='#'
+ TEXPROF_FALSE=
+fi
if test "x$enable_upmp" = xyes; then
UPMP_TRUE=
UPMP_FALSE='#'
@@ -29657,6 +29679,10 @@ if test -z "${PMP_TRUE}" && test -z "${PMP_FALSE}"; then
as_fn_error $? "conditional \"PMP\" was never defined.
Usually this means the macro was only invoked conditionally." "$LINENO" 5
fi
+if test -z "${TEXPROF_TRUE}" && test -z "${TEXPROF_FALSE}"; then
+ as_fn_error $? "conditional \"TEXPROF\" was never defined.
+Usually this means the macro was only invoked conditionally." "$LINENO" 5
+fi
if test -z "${UPMP_TRUE}" && test -z "${UPMP_FALSE}"; then
as_fn_error $? "conditional \"UPMP\" was never defined.
Usually this means the macro was only invoked conditionally." "$LINENO" 5
diff --git a/Build/source/texk/web2c/ctangleboot.cin b/Build/source/texk/web2c/ctangleboot.cin
index eefec8569c5..3483628ef0c 100644
--- a/Build/source/texk/web2c/ctangleboot.cin
+++ b/Build/source/texk/web2c/ctangleboot.cin
@@ -28,7 +28,7 @@
/*:5*/
#line 67 "cwebdir/ctangle.w"
-#define banner "This is CTANGLE, Version 4.11" \
+#define banner "This is CTANGLE, Version 4.12" \
\
#define _(s) gettext(s) \
diff --git a/Build/source/texk/web2c/cwebdir/ChangeLog b/Build/source/texk/web2c/cwebdir/ChangeLog
index 3a23a67014f..ecd275ac321 100644
--- a/Build/source/texk/web2c/cwebdir/ChangeLog
+++ b/Build/source/texk/web2c/cwebdir/ChangeLog
@@ -1,3 +1,72 @@
+2024-09-14 Andreas Scherer <https://ascherer.github.io>
+
+ * ctwill-mini.c,
+ * ctwill-w2c.ch,
+ * cweav-w2c.ch,
+ * cweave.w,
+ * cwebacromac.tex,
+ * cwebmac.tex,
+ * refsort.ch: Fix sorting order in Names-of-the-Sections.
+
+2024-08-12 Andreas Scherer <https://ascherer.github.io>
+
+ * Makefile
+ * comm-{mac,mini,ql,w2c}.ch
+ * comm-w2c.h
+ * common.h
+ * common.w
+ * ctang-{bs,pc,ql,vms,w2c,w32}.ch
+ * ctangle.c
+ * ctangle.w
+ * ctwill-w2c.ch
+ * cweav-{bs,pc,ql,vms,w2c,w32}.ch
+ * cweave.w
+ * cwebman-w2c.ch
+ * cwebman.tex
+ * po/cweb-tl.pot
+ * po/cweb.pot
+ * po/de/cweb-tl.po
+ * po/de/cweb.po
+ * po/it/cweb.po
+ * prod-{cweave,twill}.w
+ * prod.w: CWEB 4.12 release.
+
+2024-08-10 Andreas Scherer <https://ascherer.github.io>
+
+ * cwebacromac.tex,
+ * cwebmac.tex,
+ * texinputs/icwebmac.tex,
+ * texinputs/pdfctproofmac.tex,
+ * texinputs/pdfctwimac.tex: Apply new scanning macros.
+
+2024-08-08 Andreas Scherer <https://ascherer.github.io>
+
+ * comm-mini.ch,
+ * comm-w2c.ch: Demote 'Extensions' and subsections.
+
+2024-08-07 Andreas Scherer <https://ascherer.github.io>
+
+ * comm-mini.ch,
+ * comm-w2c.ch: The Index is not an Extension.
+
+2024-08-06 Andreas Scherer <https://ascherer.github.io>
+
+ * cwebacromac.tex,
+ * cwebmac.tex,
+ * cwebman.tex,
+ * texinputs/pdfctproofmac.tex,
+ * texinputs/pdfctwimac.tex: Refine PDF-related macros.
+
+2024-08-02 Andreas Scherer <https://ascherer.github.io>
+
+ * ctwill-w2c.ch,
+ * cweav-w2c.ch,
+ * cweave.w: Remove unused 'kludge'.
+
+2024-07-26 Andreas Scherer <https://ascherer.github.io>
+
+ * cwebmac.tex: Reshuffle '\note' macro.
+
2024-07-24 Andreas Scherer <https://ascherer.github.io>
* cwebmac.tex,
diff --git a/Build/source/texk/web2c/cwebdir/Makefile b/Build/source/texk/web2c/cwebdir/Makefile
index 28ea97d5540..a11936f9894 100644
--- a/Build/source/texk/web2c/cwebdir/Makefile
+++ b/Build/source/texk/web2c/cwebdir/Makefile
@@ -1,6 +1,6 @@
# This file is part of CWEB.
# It is distributed WITHOUT ANY WARRANTY, express or implied.
-# Version 4.11 --- December 2023
+# Version 4.12 --- August 2024
# Copyright (C) 1987,1990,1993,2000 Silvio Levy and Donald E. Knuth
@@ -216,6 +216,6 @@ tarball:
tar zcvhf /tmp/cweb.tgz $(ALL) examples
ctan:
- git archive -o ~/cweb-4.11.zip --prefix=cweb/ cweb-4.11
+ git archive -o ~/cweb-4.12.zip --prefix=cweb/ cweb-4.12
make PDF=pdf usermanual
- cd ..; zip -r ~/cweb-4.11.zip cweb/cwebman.pdf
+ cd ..; zip -r ~/cweb-4.12.zip cweb/cwebman.pdf
diff --git a/Build/source/texk/web2c/cwebdir/comm-mac.ch b/Build/source/texk/web2c/cwebdir/comm-mac.ch
index 79b1ec945e8..1540a9a4e21 100644
--- a/Build/source/texk/web2c/cwebdir/comm-mac.ch
+++ b/Build/source/texk/web2c/cwebdir/comm-mac.ch
@@ -4,9 +4,9 @@ No changes to CTANGLE or CWEAVE are needed.
(Contributed 13 Oct 2000 by AndPio@aol.com; slightly edited by Don Knuth)
@x in limbo, change the title page document to specify Mac version
- \centerline{(Version 4.11)}
+ \centerline{(Version 4.12)}
@y
- \centerline{(Version 4.11 for MacOS)}
+ \centerline{(Version 4.12 for MacOS)}
@z
@x section 23: Make input_ln accept \n, \r, \n\r, or \r\n as line endings
diff --git a/Build/source/texk/web2c/cwebdir/comm-mini.ch b/Build/source/texk/web2c/cwebdir/comm-mini.ch
index d1bd623b2a2..adf9fc0c28e 100644
--- a/Build/source/texk/web2c/cwebdir/comm-mini.ch
+++ b/Build/source/texk/web2c/cwebdir/comm-mini.ch
@@ -1,9 +1,9 @@
Limbo.
@x
-\def\title{Common code for CTANGLE and CWEAVE (4.11 [\TeX~Live])}
+\def\title{Common code for CTANGLE and CWEAVE (4.12 [\TeX~Live])}
@y
-\def\title{COMMON (4.11 [\TeX~Live])}
+\def\title{COMMON (4.12 [\TeX~Live])}
\def\contentspagenumber{0}
@z
diff --git a/Build/source/texk/web2c/cwebdir/comm-ql.ch b/Build/source/texk/web2c/cwebdir/comm-ql.ch
index 44cc39ec45f..8510fc093a2 100644
--- a/Build/source/texk/web2c/cwebdir/comm-ql.ch
+++ b/Build/source/texk/web2c/cwebdir/comm-ql.ch
@@ -10,24 +10,24 @@ ex <dev_>cc;'-v -h -c -=500000 -DCWEBINPUTS=flp2_ common_c'
@x
\def\v{\char'174} % vertical (|) in typewriter font
-\def\title{Common code for CTANGLE and CWEAVE (Version 4.11)}
+\def\title{Common code for CTANGLE and CWEAVE (Version 4.12)}
\def\topofcontents{\null\vfill
\centerline{\titlefont Common code for {\ttitlefont CTANGLE} and
{\ttitlefont CWEAVE}}
\vskip 15pt
- \centerline{(Version 4.11)}
+ \centerline{(Version 4.12)}
\vfill}
\def\botofcontents{\vfill
\noindent
@y
\def\v{\char'174} % vertical (|) in typewriter font
-\def\title{Common code for CTANGLE and CWEAVE (QL Version 4.11)}
+\def\title{Common code for CTANGLE and CWEAVE (QL Version 4.12)}
\def\topofcontents{\null\vfill
\centerline{\titlefont Common code for {\ttitlefont CTANGLE} and
{\ttitlefont CWEAVE}}
\vskip 15pt
- \centerline{(Version 4.11)}
+ \centerline{(Version 4.12)}
\vfill}
\def\botofcontents{\vfill
\noindent
diff --git a/Build/source/texk/web2c/cwebdir/comm-w2c.ch b/Build/source/texk/web2c/cwebdir/comm-w2c.ch
index 4035a633ffb..f0d5b6a8034 100644
--- a/Build/source/texk/web2c/cwebdir/comm-w2c.ch
+++ b/Build/source/texk/web2c/cwebdir/comm-w2c.ch
@@ -17,16 +17,16 @@
@q Please send comments, suggestions, etc. to tex-k@@tug.org. @>
@x [0.0] l.25
-\def\title{Common code for CTANGLE and CWEAVE (Version 4.11)}
+\def\title{Common code for CTANGLE and CWEAVE (Version 4.12)}
@y
\def\Kpathsea/{{\mc KPATHSEA\spacefactor1000}} \ifacro\sanitizecommand\Kpathsea{KPATHSEA}\fi
-\def\title{Common code for CTANGLE and CWEAVE (4.11 [\TeX~Live])}
+\def\title{Common code for CTANGLE and CWEAVE (4.12 [\TeX~Live])}
@z
@x [0.0] l.30
- \centerline{(Version 4.11)}
+ \centerline{(Version 4.12)}
@y
- \centerline{(Version 4.11 [\TeX~Live])}
+ \centerline{(Version 4.12 [\TeX~Live])}
@z
@x [0.0] l.32
@@ -616,21 +616,21 @@ else {
@x [9.85] l.1286
@* Index.
@y
-@** Extensions to {\tentex CWEB}. The following sections introduce new or
+@* Extensions to {\tentex CWEB}. The following sections introduce new or
improved features that have been created by numerous contributors over the
course of a quarter century.
Care has been taken to keep the original section numbering intact, so this new
material should nicely integrate with the original ``\&{85.~Index}.''
-@* Language setting. This global variable is set by the argument of the
+@*1 Language setting. This global variable is set by the argument of the
`\.{+l}' (or `\.{-l}') command-line option.
@<Global var...@>=
const char *use_language=""; /* prefix of \.{cwebmac.tex} in \TEX/ output */
-@* User communication. The |scan_args| and |cb_show_banner| routines and the
+@*1 User communication. The |scan_args| and |cb_show_banner| routines and the
|bindtextdomain| argument string need a few extra variables.
@d max_banner 50
@@ -647,7 +647,7 @@ string texmf_locale;@/
#endif
char separators[]=SEPARATORS;
-@* Temporary file output. Most \CEE/ projects are controlled by a \.{Makefile}
+@*1 Temporary file output. Most \CEE/ projects are controlled by a \.{Makefile}
that automatically takes care of the temporal dependencies between the different
source modules. It may be convenient that \.{CWEB} doesn't create new output
for all existing files, when there are only changes to some of them. Thus the
@@ -696,7 +696,7 @@ if(check_file) fclose(check_file);
if(strlen(check_file_name)) /* Delete the temporary file in case of a break */
remove(check_file_name);
-@* Internationalization. If translation catalogs for your personal
+@*1 Internationalization. If translation catalogs for your personal
\.{LANGUAGE} are installed at the appropriate place, \.{CTANGLE} and \.{CWEAVE}
will talk to you in your favorite language. Catalog \.{cweb} contains all
strings from ``plain \.{CWEB},'' catalog \.{cweb-tl} contains a few extra
@@ -759,7 +759,7 @@ free(texmf_locale);
textdomain("cweb"); /* the majority of |"strings"| come from ``plain \.{CWEB}'' */
@.cweb.mo@>
-@* File lookup with \Kpathsea/. The \.{CTANGLE} and \.{CWEAVE} programs from
+@*1 File lookup with \Kpathsea/. The \.{CTANGLE} and \.{CWEAVE} programs from
the original \.{CWEB} package use the compile-time default directory or the
value of the environment variable \.{CWEBINPUTS} as an alternative place to be
searched for files, if they could not be found in the current directory.
@@ -816,7 +816,7 @@ numbers.
Debugging output is always written to |stderr|, and begins with the string
`\.{kdebug:}'.
-@* System dependent changes. The most volatile stuff comes at the very end.
+@*1 System dependent changes. The most volatile stuff comes at the very end.
Modules for dealing with help messages and version info.
diff --git a/Build/source/texk/web2c/cwebdir/comm-w2c.h b/Build/source/texk/web2c/cwebdir/comm-w2c.h
index c2743c1a388..154371c4fa7 100644
--- a/Build/source/texk/web2c/cwebdir/comm-w2c.h
+++ b/Build/source/texk/web2c/cwebdir/comm-w2c.h
@@ -2,7 +2,7 @@
% This program by Silvio Levy and Donald E. Knuth
% is based on a program by Knuth.
% It is distributed WITHOUT ANY WARRANTY, express or implied.
-% Version 4.11 --- December 2023 (works also with later versions)
+% Version 4.12 --- August 2024 (works also with later versions)
% Copyright (C) 1987,1990,1993 Silvio Levy and Donald E. Knuth
diff --git a/Build/source/texk/web2c/cwebdir/common.h b/Build/source/texk/web2c/cwebdir/common.h
index 97fda82e75a..2e02af934f4 100644
--- a/Build/source/texk/web2c/cwebdir/common.h
+++ b/Build/source/texk/web2c/cwebdir/common.h
@@ -2,7 +2,7 @@
% This program by Silvio Levy and Donald E. Knuth
% is based on a program by Knuth.
% It is distributed WITHOUT ANY WARRANTY, express or implied.
-% Version 4.11 --- December 2023 (works also with later versions)
+% Version 4.12 --- August 2024 (works also with later versions)
% Copyright (C) 1987,1990,1993 Silvio Levy and Donald E. Knuth
diff --git a/Build/source/texk/web2c/cwebdir/common.w b/Build/source/texk/web2c/cwebdir/common.w
index 88932f3118b..1c077736bb2 100644
--- a/Build/source/texk/web2c/cwebdir/common.w
+++ b/Build/source/texk/web2c/cwebdir/common.w
@@ -2,7 +2,7 @@
% This program by Silvio Levy and Donald E. Knuth
% is based on a program by Knuth.
% It is distributed WITHOUT ANY WARRANTY, express or implied.
-% Version 4.11 --- December 2023
+% Version 4.12 --- August 2024
% Copyright (C) 1987,1990,1993,2000 Silvio Levy and Donald E. Knuth
@@ -22,12 +22,12 @@
\def\v{\char'174} % vertical (|) in typewriter font
-\def\title{Common code for CTANGLE and CWEAVE (Version 4.11)}
+\def\title{Common code for CTANGLE and CWEAVE (Version 4.12)}
\def\topofcontents{\null\vfill
\centerline{\titlefont Common code for {\ttitlefont CTANGLE} and
{\ttitlefont CWEAVE}}
\vskip 15pt
- \centerline{(Version 4.11)}
+ \centerline{(Version 4.12)}
\vfill}
\def\botofcontents{\vfill
\noindent
diff --git a/Build/source/texk/web2c/cwebdir/ctang-bs.ch b/Build/source/texk/web2c/cwebdir/ctang-bs.ch
index c70a87fe1d0..234f41039e0 100644
--- a/Build/source/texk/web2c/cwebdir/ctang-bs.ch
+++ b/Build/source/texk/web2c/cwebdir/ctang-bs.ch
@@ -17,12 +17,12 @@ by using "huge" pointers.
The ``banner line'' defined here should be changed whenever \.{CTANGLE}
is modified.
-@d banner "This is CTANGLE (Version 4.11)"
+@d banner "This is CTANGLE (Version 4.12)"
@y
The ``banner line'' defined here should be changed whenever \.{CTANGLE}
is modified.
-@d banner "This is CTANGLE (Version 4.11pc/big)"
+@d banner "This is CTANGLE (Version 4.12pc/big)"
@z
diff --git a/Build/source/texk/web2c/cwebdir/ctang-pc.ch b/Build/source/texk/web2c/cwebdir/ctang-pc.ch
index 3e6e7ac5ef2..61db92aa7e4 100644
--- a/Build/source/texk/web2c/cwebdir/ctang-pc.ch
+++ b/Build/source/texk/web2c/cwebdir/ctang-pc.ch
@@ -9,9 +9,9 @@ that allows >64K arrays. If you need lots of bytes and toks, try the
alternate change files with -bs suffix instead of -pc.
@x section 1
-@d banner "This is CTANGLE (Version 4.11)"
+@d banner "This is CTANGLE (Version 4.12)"
@y
-@d banner "This is CTANGLE (Version 4.11pc)"
+@d banner "This is CTANGLE (Version 4.12pc)"
@z
@x section 17
@d max_bytes 100000 /* the number of bytes in identifiers,
diff --git a/Build/source/texk/web2c/cwebdir/ctang-ql.ch b/Build/source/texk/web2c/cwebdir/ctang-ql.ch
index 8072aff51ab..41831c850d9 100644
--- a/Build/source/texk/web2c/cwebdir/ctang-ql.ch
+++ b/Build/source/texk/web2c/cwebdir/ctang-ql.ch
@@ -7,15 +7,15 @@ ex <dev_>cc;"-v -h -c -=500000 ctangle_c"
@x
-\def\title{CTANGLE (Version 4.11)}
+\def\title{CTANGLE (Version 4.12)}
@y
-\def\title{CTANGLE (QL Version 4.11)}
+\def\title{CTANGLE (QL Version 4.12)}
@z
@x section 1
-@d banner "This is CTANGLE (Version 4.11)"
+@d banner "This is CTANGLE (Version 4.12)"
@y
-@d banner "This is CTANGLE (QL Version 4.11)"
+@d banner "This is CTANGLE (QL Version 4.12)"
@z
@x
diff --git a/Build/source/texk/web2c/cwebdir/ctang-vms.ch b/Build/source/texk/web2c/cwebdir/ctang-vms.ch
index 2c4df3bf60e..7f142df49a7 100644
--- a/Build/source/texk/web2c/cwebdir/ctang-vms.ch
+++ b/Build/source/texk/web2c/cwebdir/ctang-vms.ch
@@ -10,9 +10,9 @@ created:
(these changes not necessary for initial bootstrapping)
@x section 1 (01-FEB-1992 ST)
-@d banner "This is CTANGLE (Version 4.11)"
+@d banner "This is CTANGLE (Version 4.12)"
@y
-@d banner "This is CTANGLE (VAX/VMS Version 4.11)"
+@d banner "This is CTANGLE (VAX/VMS Version 4.12)"
@z
@x section 5 (01-FEB-1992 ST)
diff --git a/Build/source/texk/web2c/cwebdir/ctang-w2c.ch b/Build/source/texk/web2c/cwebdir/ctang-w2c.ch
index 177daac7ab2..61eea96a0de 100644
--- a/Build/source/texk/web2c/cwebdir/ctang-w2c.ch
+++ b/Build/source/texk/web2c/cwebdir/ctang-w2c.ch
@@ -17,15 +17,15 @@
@q Please send comments, suggestions, etc. to tex-k@@tug.org. @>
@x [0.0] l.30
-\def\title{CTANGLE (Version 4.11)}
+\def\title{CTANGLE (Version 4.12)}
@y
-\def\title{CTANGLE (Version 4.11 [\TeX~Live])}
+\def\title{CTANGLE (Version 4.12 [\TeX~Live])}
@z
@x [0.0] l.34
- \centerline{(Version 4.11)}
+ \centerline{(Version 4.12)}
@y
- \centerline{(Version 4.11 [\TeX~Live])}
+ \centerline{(Version 4.12 [\TeX~Live])}
@z
@x [0.0] l.36
@@ -41,9 +41,9 @@
@z
@x [1.1] l.64
-@d banner "This is CTANGLE (Version 4.11)"
+@d banner "This is CTANGLE (Version 4.12)"
@y
-@d banner "This is CTANGLE, Version 4.11"
+@d banner "This is CTANGLE, Version 4.12"
/* will be extended by the \TeX~Live |versionstring| */
@z
diff --git a/Build/source/texk/web2c/cwebdir/ctang-w32.ch b/Build/source/texk/web2c/cwebdir/ctang-w32.ch
index c0384b48881..2a5c6f11868 100644
--- a/Build/source/texk/web2c/cwebdir/ctang-w32.ch
+++ b/Build/source/texk/web2c/cwebdir/ctang-w32.ch
@@ -4,9 +4,9 @@ This is the change file for CWEB's CTANGLE under Win32
Changes necessary for compiling with Borland C/C++
@x section 1
-@d banner "This is CTANGLE (Version 4.11)"
+@d banner "This is CTANGLE (Version 4.12)"
@y
-@d banner "This is CTANGLE (Version 4.11win32)"
+@d banner "This is CTANGLE (Version 4.12win32)"
@z
@x section 24
diff --git a/Build/source/texk/web2c/cwebdir/ctangle.c b/Build/source/texk/web2c/cwebdir/ctangle.c
index 58e37b27b41..d1cf6c67694 100644
--- a/Build/source/texk/web2c/cwebdir/ctangle.c
+++ b/Build/source/texk/web2c/cwebdir/ctangle.c
@@ -15,7 +15,7 @@
/*:5*/
#line 67 "ctangle.w"
-#define banner "This is CTANGLE (Version 4.11)" \
+#define banner "This is CTANGLE (Version 4.12)" \
#define ctangle false
#define cweave true \
diff --git a/Build/source/texk/web2c/cwebdir/ctangle.w b/Build/source/texk/web2c/cwebdir/ctangle.w
index 37b09e7fc02..b42595baa9e 100644
--- a/Build/source/texk/web2c/cwebdir/ctangle.w
+++ b/Build/source/texk/web2c/cwebdir/ctangle.w
@@ -2,7 +2,7 @@
% This program by Silvio Levy and Donald E. Knuth
% is based on a program by Knuth.
% It is distributed WITHOUT ANY WARRANTY, express or implied.
-% Version 4.11 --- December 2023
+% Version 4.12 --- August 2024
% Copyright (C) 1987,1990,1993,2000 Silvio Levy and Donald E. Knuth
@@ -27,11 +27,11 @@
\mathchardef\RA="3221 % right arrow
\mathchardef\BA="3224 % double arrow
-\def\title{CTANGLE (Version 4.11)}
+\def\title{CTANGLE (Version 4.12)}
\def\topofcontents{\null\vfill
\centerline{\titlefont The {\ttitlefont CTANGLE} processor}
\vskip 15pt
- \centerline{(Version 4.11)}
+ \centerline{(Version 4.12)}
\vfill}
\def\botofcontents{\vfill
\noindent
@@ -61,7 +61,7 @@ Joachim Schrod, Lee Wittenberg, and others who have contributed improvements.
The ``banner line'' defined here should be changed whenever \.{CTANGLE}
is modified.
-@d banner "This is CTANGLE (Version 4.11)"
+@d banner "This is CTANGLE (Version 4.12)"
@c
@<Include files@>@/
diff --git a/Build/source/texk/web2c/cwebdir/ctwill-mini.ch b/Build/source/texk/web2c/cwebdir/ctwill-mini.ch
index c5f7a3df85c..80e8feb3540 100644
--- a/Build/source/texk/web2c/cwebdir/ctwill-mini.ch
+++ b/Build/source/texk/web2c/cwebdir/ctwill-mini.ch
@@ -1589,9 +1589,9 @@ Section 212--213.
Section 215.
@x
-@<Append a string or...@>={@+ int count=-1; /* characters remaining before string break */
+@<Append a \9{s}string or...@>={@+ int count=-1; /* characters remaining before string break */
@y
-@<Append a string or...@>={int count=-1; /* characters remaining before string break */
+@<Append a \9{s}string or...@>={@+ int count=-1; /* characters remaining before string break */
@z
Section 220.
@@ -1819,12 +1819,12 @@ Section 247.
@x
@ @d usage_sentinel (struct perm_meaning *)1
-@<Translate the current section@>= {
+@<Translate the \9{c}current section@>= {
@y
@ @d usage_sentinel (struct perm_meaning *)1
@-usage_sentinel@>
@$usage_sentinel {CTWILL}247 =(\&{struct} \&{perm\_meaning} ${}{*}{}$) \T{1}@>
-@<Translate the current section@>=@+ {
+@<Translate the \9{c}current section@>= {
@z
Section 251.
@@ -1851,9 +1851,9 @@ Section 252.
Section 254.
@x
-@ @<Start a format...@>= {
+@ @<Start \9{a}a format...@>= {
@y
-@r @ @<Start a format...@>= {
+@r @ @<Start \9{a}a format...@>= {
@z
Section 255.
diff --git a/Build/source/texk/web2c/cwebdir/ctwill-w2c.ch b/Build/source/texk/web2c/cwebdir/ctwill-w2c.ch
index 70616695efb..d421c3f78cd 100644
--- a/Build/source/texk/web2c/cwebdir/ctwill-w2c.ch
+++ b/Build/source/texk/web2c/cwebdir/ctwill-w2c.ch
@@ -34,38 +34,38 @@
\datethis % Put timestamp before section 1
@z
-@x [0.0] l.35
-\def\title{CWEAVE (Version 4.11)}
+@x [0.0] l.34
+\def\title{CWEAVE (Version 4.12)}
@y
\def\Kpathsea/{{\mc KPATHSEA\spacefactor1000}} \ifacro\sanitizecommand\Kpathsea{KPATHSEA}\fi
-\def\title{CTWILL (Version 4.11 [\TeX~Live])}
+\def\title{CTWILL (Version 4.12 [\TeX~Live])}
@z
-@x [0.0] l.37
+@x [0.0] l.36
\centerline{\titlefont The {\ttitlefont CWEAVE} processor}
@y
\centerline{\titlefont The {\ttitlefont CTWILL} processor}
@z
-@x [0.0] l.39
- \centerline{(Version 4.11)}
+@x [0.0] l.38
+ \centerline{(Version 4.12)}
@y
- \centerline{(Version 4.11 [\TeX~Live])}
+ \centerline{(Version 4.12 [\TeX~Live])}
@z
-@x [0.0] l.41
+@x [0.0] l.40
\def\botofcontents{\vfill
@y
\def\covernote{\vbox{%
@z
-@x [0.0] l.54
+@x [0.0] l.53
}
@y
}}
@z
-@x [1.1] l.59
+@x [1.1] l.58
This is the \.{CWEAVE} program by Silvio Levy and Donald E. Knuth,
based on \.{WEAVE} by Knuth.
We are thankful to Steve Avery,
@@ -77,7 +77,7 @@ Crusius, and others who have contributed improvements.
The ``banner line'' defined here should be changed whenever \.{CWEAVE}
is modified.
-@d banner "This is CWEAVE (Version 4.11)"
+@d banner "This is CWEAVE (Version 4.12)"
@y
This is the \.{CTWILL} program by D. E. Knuth, based
on \.{CWEAVE} by Silvio Levy and D.~E. Knuth. It is also based on
@@ -101,29 +101,29 @@ Until then, \.{CWEAVE}'s sequence of sections will be preserved.
The ``banner line'' defined here should be changed whenever \.{CTWILL} is
modified. The version number parallels the corresponding version of \.{CWEAVE}.
-@d banner "This is CTWILL, Version 4.11"
+@d banner "This is CTWILL, Version 4.12"
/* will be extended by the \TeX~Live |versionstring| */
@z
-@x [1.2] l.94
+@x [1.2] l.93
program=cweave;
@y
program=ctwill;
@z
-@x [1.2] l.98
+@x [1.2] l.97
if (show_banner) puts(banner); /* print a ``banner line'' */
@y
if (show_banner) cb_show_banner(); /* print a ``banner line'' */
@z
-@x [1.3] l.112
+@x [1.3] l.111
@i common.h
@y
@i comm-w2c.h
@z
-@x [2.21] l.183
+@x [2.21] l.182
turned on during the first phase.
@<Private...@>=
@@ -132,19 +132,19 @@ static boolean change_exists; /* has any section changed? */
turned on during the first phase---NOT!
@z
-@x [2.23] l.217
+@x [2.23] l.216
@ @d max_refs 30000 /* number of cross-references; must be less than 65536 */
@y
@ @d max_refs 65535 /* number of cross-references; must be less than 65536 */
@z
-@x [2.25] l.246
+@x [2.25] l.245
@d append_xref(c) if (xref_ptr==xmem_end) overflow("cross-reference");
@y
@d append_xref(c) if (xref_ptr==xmem_end) overflow(_("cross-reference"));
@z
-@x [2.30] l.337
+@x [2.30] l.336
@d max_toks 30000 /* number of symbols in \CEE/ texts being parsed;
must be less than 65536 */
@d max_texts 8000 /* number of phrases in \CEE/ texts being parsed;
@@ -156,14 +156,14 @@ turned on during the first phase---NOT!
must be less than 10240 */
@z
-@x [2.32] l.357
+@x [2.32] l.356
@ Here are the two procedures needed to complete |id_lookup|:
@y
@ Here are the two procedures needed to complete |id_lookup|:
@s perm_meaning int
@z
-@x [2.32] l.374
+@x [2.32] l.373
p->xref=(void *)xmem;
@y
p->xref=(void *)xmem;
@@ -177,7 +177,7 @@ turned on during the first phase---NOT!
}
@z
-@x [2.33] l.377
+@x [2.33] l.376
@ And here's a small helper function to simplify the code.
@d update_node(p) (p)->xref=(void *)xref_ptr
@@ -188,7 +188,7 @@ turned on during the first phase---NOT!
@d get_meaning(p) (p)-name_dir+cur_meaning
@z
-@x [2.34] l.436
+@x [2.34] l.435
id_lookup("extern",NULL,int_like);
@y
@#
@@ -196,7 +196,7 @@ ext_loc=id_lookup("extern",NULL,int_like)-name_dir;
@#
@z
-@x [2.34] l.449
+@x [2.34] l.448
id_lookup("int",NULL,raw_int);
@y
@#
@@ -204,7 +204,7 @@ int_loc=id_lookup("int",NULL,raw_int)-name_dir;
@#
@z
-@x [3.36] l.544
+@x [3.36] l.543
@d ord 0207 /* control code for `\.{@@'}' */
@d join 0210 /* control code for `\.{@@\&}' */
@d thin_space 0211 /* control code for `\.{@@,}' */
@@ -248,7 +248,7 @@ int_loc=id_lookup("int",NULL,raw_int)-name_dir;
@d new_section 0235 /* control code for `\.{@@\ }' and `\.{@@*}' */
@z
-@x [3.38] l.587
+@x [3.38] l.586
ccode['\'']=ord;
@y
ccode['\'']=ord;
@@ -256,7 +256,7 @@ ccode['$']=meaning; ccode['%']=temp_meaning; ccode['-']=suppress;
ccode['r']=ccode['R']=right_start;
@z
-@x [3.40] l.608
+@x [3.40] l.607
static eight_bits skip_TeX(void);
@ @c
@@ -269,7 +269,7 @@ part of all meanings.
@c
@z
-@x [3.41] l.614
+@x [3.41] l.613
if (loc>limit && get_line()==false) return;
@y
if (loc>limit && get_line()==false) return;
@@ -279,44 +279,44 @@ part of all meanings.
}
@z
-@x [4.43] l.670
+@x [4.43] l.669
\yskip\hang |xref_roman|, |xref_wildcard|, |xref_typewriter|, |TeX_string|,
@y
\yskip\hang |xref_roman|, |xref_wildcard|, |xref_typewriter|, |TeX_string|,
|meaning|, |suppress|,
@z
-@x [4.46] l.731
+@x [4.46] l.730
@d right_preproc 0217 /* ends a preprocessor command */
@y
@d right_preproc 0223 /* ends a preprocessor command */
@z
-@x [4.57] l.888
+@x [4.57] l.887
err_print("! String didn't end"); loc=limit; break;
@y
err_print(_("! String didn't end")); loc=limit; break;
@z
-@x [4.57] l.892
+@x [4.57] l.891
err_print("! Input ended in middle of string"); loc=buffer; break;
@y
err_print(_("! Input ended in middle of string")); loc=buffer; break;
@z
-@x [4.57] l.911
+@x [4.57] l.910
printf("%s","\n! String too long: ");
@y
printf("%s",_("\n! String too long: "));
@z
-@x [4.59] l.930
+@x [4.59] l.929
case translit_code: err_print("! Use @@l in limbo only"); continue;
@y
case translit_code: err_print(_("! Use @@l in limbo only")); continue;
@z
-@x [4.59] l.932
+@x [4.59] l.931
case underline: xref_switch=def_flag; continue;
@y
case underline: xref_switch=def_flag; continue;
@@ -324,38 +324,38 @@ part of all meanings.
case right_start: right_start_switch=true; continue;
@z
-@x [4.59] l.938
+@x [4.59] l.937
case xref_roman: case xref_wildcard: case xref_typewriter: case noop:
@y
case xref_roman: case xref_wildcard: case xref_typewriter: case noop:
case meaning: case suppress:
@z
-@x [4.62] l.968
+@x [4.62] l.967
err_print("! Input ended in section name");
@y
err_print(_("! Input ended in section name"));
@z
-@x [4.62] l.981
+@x [4.62] l.980
printf("%s","\n! Section name too long: ");
@y
printf("%s",_("\n! Section name too long: "));
@z
-@x [4.63] l.995
+@x [4.63] l.994
err_print("! Section name didn't end"); break;
@y
err_print(_("! Section name didn't end")); break;
@z
-@x [4.63] l.999
+@x [4.63] l.998
err_print("! Control codes are forbidden in section name"); break;
@y
err_print(_("! Control codes are forbidden in section name")); break;
@z
-@x [4.64] l.1009
+@x [4.64] l.1008
skip_restricted(void)
{
@y
@@ -363,19 +363,19 @@ skip_restricted(void)
{ int c=ccode[(eight_bits)*(loc-1)];
@z
-@x [4.64] l.1016
+@x [4.64] l.1015
err_print("! Control text didn't end"); loc=limit;
@y
err_print(_("! Control text didn't end")); loc=limit;
@z
-@x [4.64] l.1021
+@x [4.64] l.1020
err_print("! Control codes are forbidden in control text");
@y
err_print(_("! Control codes are forbidden in control text"));
@z
-@x [4.64] l.1022
+@x [4.64] l.1021
@.Control codes are forbidden...@>
@y
@.Control codes are forbidden...@>
@@ -383,68 +383,68 @@ skip_restricted(void)
else if (c==suppress && phase==2) @<Suppress mini-index entry@>@;
@z
-@x [4.66] l.1036
+@x [4.66] l.1035
if (loc>=limit) err_print("! Verbatim string didn't end");
@y
if (loc>=limit) err_print(_("! Verbatim string didn't end"));
@z
-@x [5.68] l.1061
+@x [5.68] l.1060
skip_limbo(); change_exists=false;
@y
skip_limbo();
@<Give a default title to the program, if necessary@>@;
@z
-@x [5.68] l.1064
+@x [5.68] l.1063
changed_section[section_count]=change_exists;
/* the index changes if anything does */
@y
@z
-@x [5.70] l.1073
+@x [5.70] l.1072
if (++section_count==max_sections) overflow("section number");
@y
if (++section_count==max_sections) overflow(_("section number"));
@z
-@x [5.70] l.1074
+@x [5.70] l.1073
changed_section[section_count]=changing;
/* it will become |true| if any line changes */
@y
@z
-@x [5.70] l.1083
+@x [5.70] l.1082
if (changed_section[section_count]) change_exists=true;
@y
@z
-@x [5.74] l.1162
+@x [5.74] l.1161
case translit_code: err_print("! Use @@l in limbo only"); continue;
@y
case translit_code: err_print(_("! Use @@l in limbo only")); continue;
@z
-@x [5.74] l.1167
+@x [5.74] l.1166
case xref_roman: case xref_wildcard: case xref_typewriter:
@y
case xref_roman: case xref_wildcard: case xref_typewriter:
case meaning: case suppress:
@z
-@x [5.79] l.1249
+@x [5.79] l.1248
err_print("! Missing left identifier of @@s");
@y
err_print(_("! Missing left identifier of @@s"));
@z
-@x [5.79] l.1254
+@x [5.79] l.1253
err_print("! Missing right identifier of @@s");
@y
err_print(_("! Missing right identifier of @@s"));
@z
-@x [5.82] l.1302
+@x [5.82] l.1301
printf("%s","\n! Never defined: <");
print_section_name(p); putchar('>'); mark_harmless();
@y
@@ -452,7 +452,7 @@ if (loc>=limit) err_print(_("! Verbatim string didn't end"));
print_section_name(p); putchar('>'); mark_harmless();
@z
-@x [5.82] l.1308
+@x [5.82] l.1307
printf("%s","\n! Never used: <");
print_section_name(p); putchar('>'); mark_harmless();
@y
@@ -460,13 +460,13 @@ if (loc>=limit) err_print(_("! Verbatim string didn't end"));
print_section_name(p); putchar('>'); mark_harmless();
@z
-@x [6.89] l.1397
+@x [6.89] l.1396
@ In particular, the |finish_line| procedure is called near the very
beginning of phase two. We initialize the output variables in a slightly
tricky way so that the first line of the output file will be
`\.{\\input cwebmac}'.
-@<Start \TEX/...@>=
+@<Start \9{t}\TEX/...@>=
out_ptr=out_buf+1; out_line=1; active_file=tex_file;
tex_printf("\\input cwebma"); *out_ptr='c';
@y
@@ -486,7 +486,7 @@ debugging mini-index entries.
@d proofing flags['P']
-@<Start \TEX/...@>=
+@<Start \9{t}\TEX/...@>=
out_ptr=out_buf+1; out_line=1; active_file=tex_file;
tex_puts("\\input ");
tex_printf(use_language);
@@ -494,7 +494,7 @@ tex_puts(proofing?"ctproofma":"ctwima");
*out_ptr='c';
@z
-@x [6.90] l.1414
+@x [6.90] l.1413
@d out(c) {if (out_ptr>=out_buf_end) break_out(); *(++out_ptr)=c;}
@y
@d out(c)
@@ -508,102 +508,102 @@ tex_puts(proofing?"ctproofma":"ctwima");
}
@z
-@x [6.94] l.1462
+@x [6.94] l.1461
printf("\n! Line had to be broken (output l. %d):\n",out_line);
@y
printf(_("\n! Line had to be broken (output l. %d):\n"),out_line);
@z
-@x [6.95] l.1471
+@x [6.95] l.1470
|def_flag|, so it cannot have more than five decimal digits. If
the section is changed, we output `\.{\\*}' just after the number.
@y
|def_flag|, so it cannot have more than five decimal digits.
@z
-@x [6.96] l.1485
+@x [6.96] l.1484
if (changed_section[n]) out_str("\\*");
@.\\*@>
@y
@z
-@x [7.99] l.1543
+@x [7.99] l.1542
default: err_print("! Double @@ should be used in limbo");
@y
case right_start: right_start_switch=true; break;
default: err_print(_("! Double @@ should be used in limbo"));
@z
-@x [7.101] l.1585
+@x [7.101] l.1584
@d app_tok(c) {if (tok_ptr+2>tok_mem_end) overflow("token"); *(tok_ptr++)=c;}
@y
@d app_tok(c) {if (tok_ptr+2>tok_mem_end) overflow(_("token")); *(tok_ptr++)=c;}
@z
-@x [7.101] l.1597
+@x [7.101] l.1596
err_print("! Input ended in mid-comment");
@y
err_print(_("! Input ended in mid-comment"));
@z
-@x [7.101] l.1602
+@x [7.101] l.1601
if (bal>1) err_print("! Missing } in comment");
@y
if (bal>1) err_print(_("! Missing } in comment"));
@z
-@x [7.101] l.1618
+@x [7.101] l.1617
else {err_print("! Extra } in comment");
@y
else {err_print(_("! Extra } in comment"));
@z
-@x [7.102] l.1630
+@x [7.102] l.1629
if (bal>1) err_print("! Missing } in comment");
@y
if (bal>1) err_print(_("! Missing } in comment"));
@z
-@x [7.103] l.1638
+@x [7.103] l.1637
err_print("! Illegal use of @@ in comment");
@y
err_print(_("! Illegal use of @@ in comment"));
@z
-@x [8.106] l.1755
+@x [8.106] l.1754
@d attr_head 69 /* denotes beginning of attribute */
@y
@d attr_head 69 /* denotes beginning of attribute */
@d title 70 /* program name or header name in a ``meaning'' */
@z
-@x [8.110] l.2129
+@x [8.110] l.2128
@i prod.w
@y
@i prod-twill.w
@z
-@x [9.113] l.2179
+@x [9.113] l.2178
static scrap scrap_info[max_scraps]; /* memory array for scraps */
@y
static scrap scrap_info[max_scraps]; /* memory array for scraps */
static scrap null_scrap; /* a scrap with empty translation */
@z
-@x [9.114] l.2188
+@x [9.114] l.2187
@ @<Set init...@>=
@y
@ @<Set init...@>=
null_scrap.trans=&tok_start[0];
@z
-@x [9.116] l.2249
+@x [9.116] l.2248
update_terminal();
@y
puts("|"); update_terminal();
@z
-@x [9.124] l.2560
+@x [9.124] l.2559
the |for| loop below.
@c
@@ -618,7 +618,7 @@ static token_pointer tok_loc; /* where the first identifier appears */
@#
@z
-@x [9.124] l.2568
+@x [9.124] l.2567
token_pointer tok_loc; /* pointer to |tok_value| */
if ((tok_loc=find_first_ident(p->trans))<=operator_found)
return; /* this should not happen */
@@ -626,12 +626,12 @@ static token_pointer tok_loc; /* where the first identifier appears */
if (tok_loc<=operator_found) return; /* this should not happen */
@z
-@x [9.125] l.2597
+@x [9.125] l.2596
token_pointer tok_loc; /* where the first identifier appears */
@y
@z
-@x [9.128] l.2651
+@x [9.128] l.2650
@<Cases for |exp|@>=
if (cat1==lbrace || cat1==int_like || cat1==decl) {
make_underlined(pp); big_app(dindent); big_app1(pp);
@@ -646,7 +646,7 @@ if(cat1==lbrace || cat1==int_like || cat1==decl) {
}
@z
-@x [9.128] l.2666
+@x [9.128] l.2665
make_underlined (pp); squash(pp,2,tag,-1,7);
@y
make_underlined (pp);
@@ -657,7 +657,7 @@ if(cat1==lbrace || cat1==int_like || cat1==decl) {
squash(pp,2,tag,-1,7);
@z
-@x [9.138] l.2755
+@x [9.138] l.2754
@ @<Cases for |decl_head|@>=
if (cat1==comma) {
big_app2(pp); app(opt); app('9'); reduce(pp,2,decl_head,-1,33);
@@ -700,7 +700,7 @@ else if (cat1==int_like || cat1==lbrace || cat1==decl) {
else if (cat1==semi) squash(pp,2,decl,-1,39);
@z
-@x [9.139] l.2777
+@x [9.139] l.2776
@ @<Cases for |decl|@>=
if (cat1==decl) {
big_app1_insert(pp,force); reduce(pp,2,decl,-1,40);
@@ -719,13 +719,13 @@ else if (cat1==stmt || cat1==function) {
}
@z
-@x [9.141] l.2803
+@x [9.141] l.2802
make_underlined(pp+1); make_reserved(pp+1);
@y
make_underlined(pp+1); make_reserved(pp+1); make_ministring(pp+1);
@z
-@x [9.143] l.2834
+@x [9.143] l.2833
@ @<Cases for |fn_decl|@>=
if (cat1==decl) {
big_app1_insert(pp,force); reduce(pp,2,fn_decl,0,51);
@@ -748,14 +748,14 @@ else if (cat1==stmt) {
}
@z
-@x [9.153] l.2938
+@x [9.153] l.2937
big_app1_insert(pp,dindent); reduce(pp,2,fn_decl,0,73);
@y
big_app1(pp); if (indent_param_decl) big_app(dindent);
big_app1(pp+1); reduce(pp,2,fn_decl,0,73);
@z
-@x [9.156] l.2964
+@x [9.156] l.2963
big_app1_insert(pp, (cat1==decl || cat1==function) ? big_force :
force_lines ? force : break_space); reduce(pp,2,cat1,-1,76);
@y
@@ -764,7 +764,7 @@ else if (cat1==stmt) {
( force_lines ? force : break_space ) ); reduce(pp,2,cat1,-1,76);
@z
-@x [9.158] l.2972
+@x [9.158] l.2971
if (cat1==define_like) make_underlined(pp+2);
@y
if (cat1==define_like) { /* \#\&{define} is analogous to \&{extern} */
@@ -776,7 +776,7 @@ if (cat1==define_like) { /* \#\&{define} is analogous to \&{extern} */
}
@z
-@x [9.167] l.3069
+@x [9.167] l.3068
if (cat1==prelangle) reduce(pp+1,0,langle,1,100);
else reduce(pp,0,exp,-2,101);
@y
@@ -784,13 +784,13 @@ if (cat1==prelangle) reduce(pp+1,0,langle,1,121);
else reduce(pp,0,exp,-2,122);
@z
-@x [9.168] l.3074
+@x [9.168] l.3073
big_app1_insert(pp,' '); reduce(pp,2,else_like,-2,102);
@y
big_app1_insert(pp,' '); reduce(pp,2,else_like,-2,123);
@z
-@x [9.173] l.3106
+@x [9.173] l.3105
@ @<Cases for |typedef_like|@>=
if ((cat1==int_like || cat1==cast) && (cat2==comma || cat2==semi))
reduce(pp+1,0,exp,-1,115);
@@ -826,56 +826,56 @@ if (cat1==decl_head) {
(cat3==semi || cat3==comma)) reduce(pp+2,0,exp,1,202);
@z
-@x [9.186] l.3293
+@x [9.186] l.3292
overflow("token");
@y
overflow(_("token"));
@z
-@x [9.186] l.3297
+@x [9.186] l.3296
overflow("text");
@y
overflow(_("text"));
@z
-@x [9.190] l.3352
+@x [9.190] l.3351
if (tok_ptr+6>tok_mem_end) overflow("token");
@y
if (tok_ptr+6>tok_mem_end) overflow(_("token"));
@z
-@x [9.191] l.3358
+@x [9.191] l.3357
printf("\nIrreducible scrap sequence in section %d:",(int)section_count);
@y
printf(_("\nIrreducible scrap sequence in section %d:"),(int)section_count);
@z
-@x [9.192] l.3368
+@x [9.192] l.3367
printf("\nTracing after l. %d:\n",cur_line); mark_harmless();
@y
printf(_("\nTracing after l. %d:\n"),cur_line); mark_harmless();
@z
-@x [10.196] l.3454
+@x [10.196] l.3453
case ignore: case xref_roman: case xref_wildcard:
@y
case ignore: case xref_roman: case xref_wildcard:
case meaning: case suppress:
@z
-@x [10.197] l.3500
+@x [10.197] l.3499
overflow("scrap/token/text");
@y
overflow(_("scrap/token/text"));
@z
-@x [10.199] l.3585
+@x [10.199] l.3584
else err_print("! Double @@ should be used in strings");
@y
else err_print(_("! Double @@ should be used in strings"));
@z
-@x [10.202] l.3650
+@x [10.202] l.3649
else app_scrap(p->ilk,maybe_math);
}
}
@@ -886,62 +886,62 @@ if (cat1==decl_head) {
@<Flag the usage of this identifier, for the mini-index@>@;
@z
-@x [10.203] l.3668
+@x [10.203] l.3667
if (next_control!='|') err_print("! Missing '|' after C text");
@y
if (next_control!='|') err_print(_("! Missing '|' after C text"));
@z
-@x [10.204] l.3683
+@x [10.204] l.3682
to \.{\\PB}. Although \.{cwebmac} ignores \.{\\PB}, other macro packages
@y
to \.{\\PB}. Although \.{ctwimac} ignores \.{\\PB}, other macro packages
@z
-@x [10.205] l.3713
+@x [10.205] l.3712
app(tok_flag+(int)(p-tok_start));
@y
app(tok_flag+(int)(p-tok_start));
app(inserted);
@z
-@x [11.211] l.3809
+@x [11.211] l.3808
if (stack_ptr==stack_end) overflow("stack");
@y
if (stack_ptr==stack_end) overflow(_("stack"));
@z
-@x [11.224] l.4132
+@x [11.224] l.4131
printf("%s","\n! Illegal control code in section name: <");
@y
printf("%s",_("\n! Illegal control code in section name: <"));
@z
-@x [11.225] l.4147
+@x [11.225] l.4146
printf("%s","\n! C text in section name didn't end: <");
@y
printf("%s",_("\n! C text in section name didn't end: <"));
@z
-@x [11.225] l.4159
+@x [11.225] l.4158
if (j>buffer+long_buf_size-3) overflow("buffer");
@y
if (j>buffer+long_buf_size-3) overflow(_("buffer"));
@z
-@x [11.226] l.4167
+@x [11.226] l.4166
if (j>buffer+long_buf_size-4) overflow("buffer");
@y
if (j>buffer+long_buf_size-4) overflow(_("buffer"));
@z
-@x [12.227] l.4182
+@x [12.227] l.4181
if (show_progress) printf("%s","\nWriting the output file...");
@y
if (show_progress) printf("%s",_("\nWriting the output file..."));
@z
-@x [12.227] l.4184
+@x [12.227] l.4183
section_count=0; format_visible=true; copy_limbo();
@y
temp_switch=false; temp_meaning_ptr=temp_meaning_stack;
@@ -949,10 +949,10 @@ temp_switch=false; temp_meaning_ptr=temp_meaning_stack;
section_count=0; format_visible=true; right_start_switch=false; copy_limbo();
@z
-@x [12.229] l.4213
+@x [12.229] l.4212
static boolean group_found=false; /* has a starred section occurred? */
-@ @<Translate the current section@>= {
+@ @<Translate the \9{c}current section@>= {
section_count++;
@y
static boolean group_found=false; /* has a starred section occurred? */
@@ -960,18 +960,18 @@ static boolean right_start_switch; /* has `\.{@@r}' occurred recently? */
static boolean temp_switch; /* has `\.{@@\%}' occurred recently? */
@ @d usage_sentinel (struct perm_meaning *)1
-@<Translate the current section@>= {
+@<Translate the \9{c}current section@>= {
section_count++;
temp_switch=false; temp_meaning_ptr=temp_meaning_stack;
top_usage=usage_sentinel;
@z
-@x [12.231] l.4231
+@x [12.231] l.4230
If the section has changed, we put \.{\\*} just after the section number.
@y
@z
-@x [12.231] l.4234
+@x [12.231] l.4233
if (*(loc-1)!='*') out_str("\\M");
@y
if (*(loc-1)!='*') {
@@ -983,7 +983,7 @@ if (*(loc-1)!='*') {
}
@z
-@x [12.231] l.4249
+@x [12.231] l.4248
@.\\N@>
@y
@.\\N@>
@@ -993,14 +993,14 @@ if (*(loc-1)!='*') {
}
@z
-@x [12.231] l.4256
+@x [12.231] l.4255
out('{'); out_section(section_count); out('}');
@y
out('{'); out_section(section_count); out('}');
flush_buffer(out_ptr,false,false);
@z
-@x [12.232] l.4264
+@x [12.232] l.4263
case '@@': out('@@'); break;
@y
case '@@': out('@@'); break;
@@ -1008,26 +1008,26 @@ flush_buffer(out_ptr,false,false);
case right_start: right_start_switch=true; break;
@z
-@x [12.232] l.4267
+@x [12.232] l.4266
case section_name: loc-=2; next_control=get_next(); /* skip to \.{@@>} */
@y
case meaning: case suppress:
case section_name: loc-=2; next_control=get_next(); /* reprocess */
@z
-@x [12.232] l.4269
+@x [12.232] l.4268
err_print("! TeX string should be in C text only"); break;
@y
err_print(_("! TeX string should be in C text only")); break;
@z
-@x [12.232] l.4275
+@x [12.232] l.4274
err_print("! You can't do that in TeX text"); break;
@y
err_print(_("! You can't do that in TeX text")); break;
@z
-@x [12.233] l.4289
+@x [12.233] l.4288
outer_parse(); finish_C(format_visible); format_visible=true;
doing_format=false;
}
@@ -1039,20 +1039,20 @@ flush_buffer(out_ptr,false,false);
}
@z
-@x [12.236] l.4341
-@<Start a macro...@>= {
+@x [12.236] l.4340
+@<Start \9{a}a macro...@>= {
@y
-@<Start a macro...@>= {
+@<Start \9{a}a macro...@>= {
is_macro=true;
@z
-@x [12.236] l.4347
+@x [12.236] l.4346
err_print("! Improper macro definition");
@y
err_print(_("! Improper macro definition"));
@z
-@x [12.236] l.4348
+@x [12.236] l.4347
@.Improper macro definition@>
else {
app('$'); app_cur_id(false);
@@ -1064,7 +1064,7 @@ flush_buffer(out_ptr,false,false);
def_diff=(*loc!='(');
@z
-@x [12.236] l.4360
+@x [12.236] l.4359
} @=/* otherwise fall through */@>@;
default: err_print("! Improper macro definition"); break;
@y
@@ -1072,34 +1072,34 @@ flush_buffer(out_ptr,false,false);
default: err_print(_("! Improper macro definition")); break;
@z
-@x [12.237] l.4370
-@ @<Start a format...@>= {
+@x [12.237] l.4369
+@ @<Start \9{a}a format...@>= {
doing_format=true;
@y
-@ @<Start a format...@>= {
+@ @<Start \9{a}a format...@>= {
doing_format=true;
is_macro=false;
@z
-@x [12.237] l.4387
+@x [12.237] l.4386
if (scrap_ptr!=scrap_info+2) err_print("! Improper format definition");
@y
if (scrap_ptr!=scrap_info+2) err_print(_("! Improper format definition"));
@z
-@x [12.240] l.4422
+@x [12.240] l.4421
err_print("! You need an = sign after the section name");
@y
err_print(_("! You need an = sign after the section name"));
@z
-@x [12.241] l.4444
+@x [12.241] l.4443
err_print("! You can't do that in C text");
@y
err_print(_("! You can't do that in C text"));
@z
-@x [12.246] l.4514
+@x [12.246] l.4513
out_str("\\fi"); finish_line();
@.\\fi@>
@y
@@ -1110,13 +1110,13 @@ out_str("}\\FI"); finish_line();
@.\\FI@>
@z
-@x [13.247] l.4535
+@x [13.247] l.4534
if (show_progress) printf("%s","\nWriting the index...");
@y
if (show_progress) printf("%s",_("\nWriting the index..."));
@z
-@x [13.247] l.4537
+@x [13.247] l.4536
if (change_exists) {
@<Tell about changed sections@>@;
finish_line(); flush_buffer(out_buf,false,false);
@@ -1125,32 +1125,32 @@ out_str("}\\FI"); finish_line();
@y
@z
-@x [13.247] l.4546
+@x [13.247] l.4545
fatal("! Cannot open index file ",idx_file_name);
@y
fatal(_("! Cannot open index file "),idx_file_name);
@z
-@x [13.247] l.4558
+@x [13.247] l.4557
fatal("! Cannot open section file ",scn_file_name);
@y
fatal(_("! Cannot open section file "),scn_file_name);
@z
-@x [13.247] l.4570
+@x [13.247] l.4569
fclose(active_file);
@y
fclose(active_file); active_file=tex_file=NULL;
if (check_for_change) @<Update the result when it has changed@>@;
@z
-@x [13.247] l.4573
+@x [13.247] l.4572
printf("%s","Done.");
@y
printf("%s",_("Done."));
@z
-@x [13.249] l.4581
+@x [13.249] l.4580
the index section itself.
@<Tell about changed sections@>=
@@ -1169,13 +1169,13 @@ out('.');
the index section itself---NOT!
@z
-@x [13.257] l.4725
+@x [13.257] l.4724
if (sort_ptr>=scrap_info_end) overflow("sorting");
@y
if (sort_ptr>=scrap_info_end) overflow(_("sorting"));
@z
-@x [13.262] l.4770
+@x [13.262] l.4769
@ @<Output the name...@>=
switch (cur_name->ilk) {@+char *p; /* index into |byte_mem| */@+@t}\6{\4@>
@y
@@ -1186,20 +1186,20 @@ rest of the job. Compare this code with section |@<Mini-output...@>|.
switch (cur_name->ilk) {
@z
-@x [13.262] l.4772
+@x [13.262] l.4771
case normal: case func_template:
@y
case normal:
@z
-@x [13.262] l.4774
+@x [13.262] l.4773
else {@+boolean all_caps=true;@+@t}\6{@>
@y
else {@+boolean all_caps=true;@+char *p;
/* index into |byte_mem| */ @+@t}\6{@>
@z
-@x [13.262] l.4789
+@x [13.262] l.4788
case roman: not_an_identifier: out_name(cur_name,false); goto name_done;
case custom:
out_str("$\\");
@@ -1214,13 +1214,13 @@ not_an_identifier: out_name(cur_name,false); goto name_done;
@.\\\$@>
@z
-@x [13.262] l.4799
+@x [13.262] l.4798
out_name(cur_name,true);
@y
out_name(cur_name,proofing);
@z
-@x [13.269] l.4862
+@x [13.269] l.4861
puts("\nMemory usage statistics:");
@.Memory usage statistics:@>
printf("%td names (out of %ld)\n",@^system dependencies@>
@@ -1269,7 +1269,7 @@ out_name(cur_name,proofing);
(ptrdiff_t)(max_sort_ptr-scrap_info),(long)max_scraps);
@z
-@x [14.270] l.4884
+@x [14.270] l.4883
@** Index.
@y
@q Section 270. @>
diff --git a/Build/source/texk/web2c/cwebdir/cweav-bs.ch b/Build/source/texk/web2c/cwebdir/cweav-bs.ch
index c2f3214c793..458a0a7e60c 100644
--- a/Build/source/texk/web2c/cwebdir/cweav-bs.ch
+++ b/Build/source/texk/web2c/cwebdir/cweav-bs.ch
@@ -20,12 +20,12 @@ This file contributed by Barry Schwartz, trashman@crud.mn.org, 28 Jun 94.
The ``banner line'' defined here should be changed whenever \.{CWEAVE}
is modified.
-@d banner "This is CWEAVE (Version 4.11)"
+@d banner "This is CWEAVE (Version 4.12)"
@y
The ``banner line'' defined here should be changed whenever \.{CWEAVE}
is modified.
-@d banner "This is CWEAVE (Version 4.11pc/big)"
+@d banner "This is CWEAVE (Version 4.12pc/big)"
@z
diff --git a/Build/source/texk/web2c/cwebdir/cweav-pc.ch b/Build/source/texk/web2c/cwebdir/cweav-pc.ch
index 165b8a54eeb..ae1fb9f45c7 100644
--- a/Build/source/texk/web2c/cwebdir/cweav-pc.ch
+++ b/Build/source/texk/web2c/cwebdir/cweav-pc.ch
@@ -11,9 +11,9 @@ that allows >64K arrays. (If you need lots more bytes, try the alternate
change files that have -bs in their name instead of -pc.)
@x section 1
-@d banner "This is CWEAVE (Version 4.11)"
+@d banner "This is CWEAVE (Version 4.12)"
@y
-@d banner "This is CWEAVE (Version 4.11pc)"
+@d banner "This is CWEAVE (Version 4.12pc)"
@z
@x section 17
diff --git a/Build/source/texk/web2c/cwebdir/cweav-ql.ch b/Build/source/texk/web2c/cwebdir/cweav-ql.ch
index 31bd6138a29..fd369064deb 100644
--- a/Build/source/texk/web2c/cwebdir/cweav-ql.ch
+++ b/Build/source/texk/web2c/cwebdir/cweav-ql.ch
@@ -7,15 +7,15 @@ ex <dev_>cc;"-v -h -c =500000 cweave_c"
@x
-\def\title{CWEAVE (Version 4.11)}
+\def\title{CWEAVE (Version 4.12)}
@y
-\def\title{CWEAVE (QL Version 4.11)}
+\def\title{CWEAVE (QL Version 4.12)}
@z
@x section 1
-@d banner "This is CWEAVE (Version 4.11)"
+@d banner "This is CWEAVE (Version 4.12)"
@y
-@d banner "This is CWEAVE (QL Version 4.11)"
+@d banner "This is CWEAVE (QL Version 4.12)"
@z
@x
diff --git a/Build/source/texk/web2c/cwebdir/cweav-vms.ch b/Build/source/texk/web2c/cwebdir/cweav-vms.ch
index b944f838153..f4630f98661 100644
--- a/Build/source/texk/web2c/cwebdir/cweav-vms.ch
+++ b/Build/source/texk/web2c/cwebdir/cweav-vms.ch
@@ -13,9 +13,9 @@ modified:
(also modified by Don Knuth to keep version numbers uptodate)
@x section 1 (01-FEB-1992 ST)
-@d banner "This is CWEAVE (Version 4.11)"
+@d banner "This is CWEAVE (Version 4.12)"
@y
-@d banner "This is CWEAVE (VAX/VMS Version 4.11)"
+@d banner "This is CWEAVE (VAX/VMS Version 4.12)"
@z
@x section 5 (01-FEB-1992 ST)
diff --git a/Build/source/texk/web2c/cwebdir/cweav-w2c.ch b/Build/source/texk/web2c/cwebdir/cweav-w2c.ch
index 73b9ab4b2c9..b0adceab75a 100644
--- a/Build/source/texk/web2c/cwebdir/cweav-w2c.ch
+++ b/Build/source/texk/web2c/cwebdir/cweav-w2c.ch
@@ -16,62 +16,62 @@
@q Please send comments, suggestions, etc. to tex-k@@tug.org. @>
-@x [0.0] l.35
-\def\title{CWEAVE (Version 4.11)}
+@x [0.0] l.34
+\def\title{CWEAVE (Version 4.12)}
@y
-\def\title{CWEAVE (Version 4.11 [\TeX~Live])}
+\def\title{CWEAVE (Version 4.12 [\TeX~Live])}
@z
-@x [0.0] l.39
- \centerline{(Version 4.11)}
+@x [0.0] l.38
+ \centerline{(Version 4.12)}
@y
- \centerline{(Version 4.11 [\TeX~Live])}
+ \centerline{(Version 4.12 [\TeX~Live])}
@z
-@x [0.0] l.41
+@x [0.0] l.40
\def\botofcontents{\vfill
@y
\def\covernote{\vbox{%
@z
-@x [0.0] l.54
+@x [0.0] l.53
}
@y
}}\datecontentspage
@z
-@x [1.1] l.70
-@d banner "This is CWEAVE (Version 4.11)"
+@x [1.1] l.69
+@d banner "This is CWEAVE (Version 4.12)"
@y
-@d banner "This is CWEAVE, Version 4.11"
+@d banner "This is CWEAVE, Version 4.12"
/* will be extended by the \TeX~Live |versionstring| */
@z
-@x [1.2] l.98
+@x [1.2] l.97
if (show_banner) puts(banner); /* print a ``banner line'' */
@y
if (show_banner) cb_show_banner(); /* print a ``banner line'' */
@z
-@x [1.3] l.112
+@x [1.3] l.111
@i common.h
@y
@i comm-w2c.h
@z
-@x [2.23] l.217
+@x [2.23] l.216
@ @d max_refs 30000 /* number of cross-references; must be less than 65536 */
@y
@ @d max_refs 65535 /* number of cross-references; must be less than 65536 */
@z
-@x [2.25] l.246
+@x [2.25] l.245
@d append_xref(c) if (xref_ptr==xmem_end) overflow("cross-reference");
@y
@d append_xref(c) if (xref_ptr==xmem_end) overflow(_("cross-reference"));
@z
-@x [2.30] l.337
+@x [2.30] l.336
@d max_toks 30000 /* number of symbols in \CEE/ texts being parsed;
must be less than 65536 */
@d max_texts 8000 /* number of phrases in \CEE/ texts being parsed;
@@ -83,97 +83,97 @@
must be less than 10240 */
@z
-@x [4.57] l.888
+@x [4.57] l.887
err_print("! String didn't end"); loc=limit; break;
@y
err_print(_("! String didn't end")); loc=limit; break;
@z
-@x [4.57] l.892
+@x [4.57] l.891
err_print("! Input ended in middle of string"); loc=buffer; break;
@y
err_print(_("! Input ended in middle of string")); loc=buffer; break;
@z
-@x [4.57] l.911
+@x [4.57] l.910
printf("%s","\n! String too long: ");
@y
printf("%s",_("\n! String too long: "));
@z
-@x [4.59] l.930
+@x [4.59] l.929
case translit_code: err_print("! Use @@l in limbo only"); continue;
@y
case translit_code: err_print(_("! Use @@l in limbo only")); continue;
@z
-@x [4.62] l.968
+@x [4.62] l.967
err_print("! Input ended in section name");
@y
err_print(_("! Input ended in section name"));
@z
-@x [4.62] l.981
+@x [4.62] l.980
printf("%s","\n! Section name too long: ");
@y
printf("%s",_("\n! Section name too long: "));
@z
-@x [4.63] l.995
+@x [4.63] l.994
err_print("! Section name didn't end"); break;
@y
err_print(_("! Section name didn't end")); break;
@z
-@x [4.63] l.999
+@x [4.63] l.998
err_print("! Control codes are forbidden in section name"); break;
@y
err_print(_("! Control codes are forbidden in section name")); break;
@z
-@x [4.64] l.1016
+@x [4.64] l.1015
err_print("! Control text didn't end"); loc=limit;
@y
err_print(_("! Control text didn't end")); loc=limit;
@z
-@x [4.64] l.1021
+@x [4.64] l.1020
err_print("! Control codes are forbidden in control text");
@y
err_print(_("! Control codes are forbidden in control text"));
@z
-@x [4.66] l.1036
+@x [4.66] l.1035
if (loc>=limit) err_print("! Verbatim string didn't end");
@y
if (loc>=limit) err_print(_("! Verbatim string didn't end"));
@z
-@x [5.70] l.1073
+@x [5.70] l.1072
if (++section_count==max_sections) overflow("section number");
@y
if (++section_count==max_sections) overflow(_("section number"));
@z
-@x [5.74] l.1162
+@x [5.74] l.1161
case translit_code: err_print("! Use @@l in limbo only"); continue;
@y
case translit_code: err_print(_("! Use @@l in limbo only")); continue;
@z
-@x [5.79] l.1249
+@x [5.79] l.1248
err_print("! Missing left identifier of @@s");
@y
err_print(_("! Missing left identifier of @@s"));
@z
-@x [5.79] l.1254
+@x [5.79] l.1253
err_print("! Missing right identifier of @@s");
@y
err_print(_("! Missing right identifier of @@s"));
@z
-@x [5.82] l.1302
+@x [5.82] l.1301
printf("%s","\n! Never defined: <");
print_section_name(p); putchar('>'); mark_harmless();
@y
@@ -181,7 +181,7 @@ if (loc>=limit) err_print(_("! Verbatim string didn't end"));
print_section_name(p); putchar('>'); mark_harmless();
@z
-@x [5.82] l.1308
+@x [5.82] l.1307
printf("%s","\n! Never used: <");
print_section_name(p); putchar('>'); mark_harmless();
@y
@@ -189,13 +189,13 @@ if (loc>=limit) err_print(_("! Verbatim string didn't end"));
print_section_name(p); putchar('>'); mark_harmless();
@z
-@x [6.89] l.1397
+@x [6.89] l.1396
@ In particular, the |finish_line| procedure is called near the very
beginning of phase two. We initialize the output variables in a slightly
tricky way so that the first line of the output file will be
`\.{\\input cwebmac}'.
-@<Start \TEX/...@>=
+@<Start \9{t}\TEX/...@>=
out_ptr=out_buf+1; out_line=1; active_file=tex_file;
tex_printf("\\input cwebma"); *out_ptr='c';
@y
@@ -210,7 +210,7 @@ prepended to `\.{cwebmac.tex}', e.g., if you call \.{CWEAVE} with
Without this option the first line of the output file will be
`\.{\\input cwebmac}'.
-@<Start \TEX/...@>=
+@<Start \9{t}\TEX/...@>=
out_ptr=out_buf+1; out_line=1; active_file=tex_file;
tex_puts("\\input ");
tex_printf(use_language);
@@ -218,61 +218,61 @@ tex_puts("cwebma");
*out_ptr='c';
@z
-@x [6.94] l.1462
+@x [6.94] l.1461
printf("\n! Line had to be broken (output l. %d):\n",out_line);
@y
printf(_("\n! Line had to be broken (output l. %d):\n"),out_line);
@z
-@x [7.99] l.1543
+@x [7.99] l.1542
default: err_print("! Double @@ should be used in limbo");
@y
default: err_print(_("! Double @@ should be used in limbo"));
@z
-@x [7.101] l.1585
+@x [7.101] l.1584
@d app_tok(c) {if (tok_ptr+2>tok_mem_end) overflow("token"); *(tok_ptr++)=c;}
@y
@d app_tok(c) {if (tok_ptr+2>tok_mem_end) overflow(_("token")); *(tok_ptr++)=c;}
@z
-@x [7.101] l.1597
+@x [7.101] l.1596
err_print("! Input ended in mid-comment");
@y
err_print(_("! Input ended in mid-comment"));
@z
-@x [7.101] l.1602
+@x [7.101] l.1601
if (bal>1) err_print("! Missing } in comment");
@y
if (bal>1) err_print(_("! Missing } in comment"));
@z
-@x [7.101] l.1618
+@x [7.101] l.1617
else {err_print("! Extra } in comment");
@y
else {err_print(_("! Extra } in comment"));
@z
-@x [7.102] l.1630
+@x [7.102] l.1629
if (bal>1) err_print("! Missing } in comment");
@y
if (bal>1) err_print(_("! Missing } in comment"));
@z
-@x [7.103] l.1638
+@x [7.103] l.1637
err_print("! Illegal use of @@ in comment");
@y
err_print(_("! Illegal use of @@ in comment"));
@z
-@x [8.110] l.2129
+@x [8.110] l.2128
@i prod.w
@y
@i prod-cweave.w
@z
-@x [9.128] l.2651
+@x [9.128] l.2650
@<Cases for |exp|@>=
if (cat1==lbrace || cat1==int_like || cat1==decl) {
make_underlined(pp); big_app(dindent); big_app1(pp);
@@ -286,7 +286,7 @@ if(cat1==lbrace || cat1==int_like || cat1==decl) {
}
@z
-@x [9.138] l.2755
+@x [9.138] l.2754
@ @<Cases for |decl_head|@>=
if (cat1==comma) {
big_app2(pp); app(opt); app('9'); reduce(pp,2,decl_head,-1,33);
@@ -328,7 +328,7 @@ else if (cat1==int_like || cat1==lbrace || cat1==decl) {
else if (cat1==semi) squash(pp,2,decl,-1,39);
@z
-@x [9.139] l.2777
+@x [9.139] l.2776
@ @<Cases for |decl|@>=
if (cat1==decl) {
big_app1_insert(pp,force); reduce(pp,2,decl,-1,40);
@@ -347,7 +347,7 @@ else if (cat1==stmt || cat1==function) {
}
@z
-@x [9.143] l.2834
+@x [9.143] l.2833
@ @<Cases for |fn_decl|@>=
if (cat1==decl) {
big_app1_insert(pp,force); reduce(pp,2,fn_decl,0,51);
@@ -370,14 +370,14 @@ else if (cat1==stmt) {
}
@z
-@x [9.153] l.2938
+@x [9.153] l.2937
big_app1_insert(pp,dindent); reduce(pp,2,fn_decl,0,73);
@y
big_app1(pp); if (indent_param_decl) big_app(dindent);
big_app1(pp+1); reduce(pp,2,fn_decl,0,73);
@z
-@x [9.156] l.2964
+@x [9.156] l.2963
big_app1_insert(pp, (cat1==decl || cat1==function) ? big_force :
force_lines ? force : break_space); reduce(pp,2,cat1,-1,76);
@y
@@ -386,109 +386,109 @@ else if (cat1==stmt) {
( force_lines ? force : break_space ) ); reduce(pp,2,cat1,-1,76);
@z
-@x [9.186] l.3293
+@x [9.186] l.3292
overflow("token");
@y
overflow(_("token"));
@z
-@x [9.186] l.3297
+@x [9.186] l.3296
overflow("text");
@y
overflow(_("text"));
@z
-@x [9.190] l.3352
+@x [9.190] l.3351
if (tok_ptr+6>tok_mem_end) overflow("token");
@y
if (tok_ptr+6>tok_mem_end) overflow(_("token"));
@z
-@x [9.191] l.3358
+@x [9.191] l.3357
printf("\nIrreducible scrap sequence in section %d:",(int)section_count);
@y
printf(_("\nIrreducible scrap sequence in section %d:"),(int)section_count);
@z
-@x [9.192] l.3368
+@x [9.192] l.3367
printf("\nTracing after l. %d:\n",cur_line); mark_harmless();
@y
printf(_("\nTracing after l. %d:\n"),cur_line); mark_harmless();
@z
-@x [10.197] l.3500
+@x [10.197] l.3499
overflow("scrap/token/text");
@y
overflow(_("scrap/token/text"));
@z
-@x [10.199] l.3585
+@x [10.199] l.3584
else err_print("! Double @@ should be used in strings");
@y
else err_print(_("! Double @@ should be used in strings"));
@z
-@x [10.203] l.3668
+@x [10.203] l.3667
if (next_control!='|') err_print("! Missing '|' after C text");
@y
if (next_control!='|') err_print(_("! Missing '|' after C text"));
@z
-@x [11.211] l.3809
+@x [11.211] l.3808
if (stack_ptr==stack_end) overflow("stack");
@y
if (stack_ptr==stack_end) overflow(_("stack"));
@z
-@x [11.224] l.4132
+@x [11.224] l.4131
printf("%s","\n! Illegal control code in section name: <");
@y
printf("%s",_("\n! Illegal control code in section name: <"));
@z
-@x [11.225] l.4147
+@x [11.225] l.4146
printf("%s","\n! C text in section name didn't end: <");
@y
printf("%s",_("\n! C text in section name didn't end: <"));
@z
-@x [11.225] l.4159
+@x [11.225] l.4158
if (j>buffer+long_buf_size-3) overflow("buffer");
@y
if (j>buffer+long_buf_size-3) overflow(_("buffer"));
@z
-@x [11.226] l.4167
+@x [11.226] l.4166
if (j>buffer+long_buf_size-4) overflow("buffer");
@y
if (j>buffer+long_buf_size-4) overflow(_("buffer"));
@z
-@x [12.227] l.4182
+@x [12.227] l.4181
if (show_progress) printf("%s","\nWriting the output file...");
@y
if (show_progress) printf("%s",_("\nWriting the output file..."));
@z
-@x [12.232] l.4269
+@x [12.232] l.4268
err_print("! TeX string should be in C text only"); break;
@y
err_print(_("! TeX string should be in C text only")); break;
@z
-@x [12.232] l.4275
+@x [12.232] l.4274
err_print("! You can't do that in TeX text"); break;
@y
err_print(_("! You can't do that in TeX text")); break;
@z
-@x [12.236] l.4347
+@x [12.236] l.4346
err_print("! Improper macro definition");
@y
err_print(_("! Improper macro definition"));
@z
-@x [12.236] l.4360
+@x [12.236] l.4359
} @=/* otherwise fall through */@>@;
default: err_print("! Improper macro definition"); break;
@y
@@ -496,62 +496,62 @@ if (show_progress) printf("%s",_("\nWriting the output file..."));
default: err_print(_("! Improper macro definition")); break;
@z
-@x [12.237] l.4387
+@x [12.237] l.4386
if (scrap_ptr!=scrap_info+2) err_print("! Improper format definition");
@y
if (scrap_ptr!=scrap_info+2) err_print(_("! Improper format definition"));
@z
-@x [12.240] l.4422
+@x [12.240] l.4421
err_print("! You need an = sign after the section name");
@y
err_print(_("! You need an = sign after the section name"));
@z
-@x [12.241] l.4444
+@x [12.241] l.4443
err_print("! You can't do that in C text");
@y
err_print(_("! You can't do that in C text"));
@z
-@x [13.247] l.4535
+@x [13.247] l.4534
if (show_progress) printf("%s","\nWriting the index...");
@y
if (show_progress) printf("%s",_("\nWriting the index..."));
@z
-@x [13.247] l.4546
+@x [13.247] l.4545
fatal("! Cannot open index file ",idx_file_name);
@y
fatal(_("! Cannot open index file "),idx_file_name);
@z
-@x [13.247] l.4558
+@x [13.247] l.4557
fatal("! Cannot open section file ",scn_file_name);
@y
fatal(_("! Cannot open section file "),scn_file_name);
@z
-@x [13.247] l.4570
+@x [13.247] l.4569
fclose(active_file);
@y
fclose(active_file); active_file=tex_file=NULL;
if (check_for_change) @<Update the result when it has changed@>@;
@z
-@x [13.247] l.4573
+@x [13.247] l.4572
printf("%s","Done.");
@y
printf("%s",_("Done."));
@z
-@x [13.257] l.4725
+@x [13.257] l.4724
if (sort_ptr>=scrap_info_end) overflow("sorting");
@y
if (sort_ptr>=scrap_info_end) overflow(_("sorting"));
@z
-@x [13.269] l.4862
+@x [13.269] l.4861
puts("\nMemory usage statistics:");
@.Memory usage statistics:@>
printf("%td names (out of %ld)\n",@^system dependencies@>
@@ -595,7 +595,7 @@ if (check_for_change) @<Update the result when it has changed@>@;
(ptrdiff_t)(max_sort_ptr-scrap_info),(long)max_scraps);
@z
-@x [14.270] l.4884
+@x [14.270] l.4883
@** Index.
@y
@** Extensions to {\tentex CWEB}. The following sections introduce new or
diff --git a/Build/source/texk/web2c/cwebdir/cweav-w32.ch b/Build/source/texk/web2c/cwebdir/cweav-w32.ch
index 1bf1471e18e..2f9a2127ac5 100644
--- a/Build/source/texk/web2c/cwebdir/cweav-w32.ch
+++ b/Build/source/texk/web2c/cwebdir/cweav-w32.ch
@@ -2,9 +2,9 @@ This is the change file for CWEB's CWEAVE under Win32
(Contributed by Fabrice Popineau, February 2002)
@x section 1
-@d banner "This is CWEAVE (Version 4.11)"
+@d banner "This is CWEAVE (Version 4.12)"
@y
-@d banner "This is CWEAVE (Version 4.11win32)"
+@d banner "This is CWEAVE (Version 4.12win32)"
@z
@x section 32
diff --git a/Build/source/texk/web2c/cwebdir/cweave.w b/Build/source/texk/web2c/cwebdir/cweave.w
index 808d46cc7eb..015a3183b8e 100644
--- a/Build/source/texk/web2c/cwebdir/cweave.w
+++ b/Build/source/texk/web2c/cwebdir/cweave.w
@@ -2,7 +2,7 @@
% This program by Silvio Levy and Donald E. Knuth
% is based on a program by Knuth.
% It is distributed WITHOUT ANY WARRANTY, express or implied.
-% Version 4.11 --- December 2023
+% Version 4.12 --- August 2024
% Copyright (C) 1987,1990,1993,2000 Silvio Levy and Donald E. Knuth
@@ -27,16 +27,15 @@
\def\dleft{[\![} \def\dright{]\!]} % double brackets
\mathchardef\RA="3221 % right arrow
\mathchardef\BA="3224 % double arrow
-\def\({} % ) kludge for alphabetizing certain section names
\def\TeXxstring{\\{\TEX/\_string}}
\def\skipxTeX{\\{skip\_\TEX/}}
\def\copyxTeX{\\{copy\_\TEX/}}
-\def\title{CWEAVE (Version 4.11)}
+\def\title{CWEAVE (Version 4.12)}
\def\topofcontents{\null\vfill
\centerline{\titlefont The {\ttitlefont CWEAVE} processor}
\vskip 15pt
- \centerline{(Version 4.11)}
+ \centerline{(Version 4.12)}
\vfill}
\def\botofcontents{\vfill
\noindent
@@ -67,7 +66,7 @@ Crusius, and others who have contributed improvements.
The ``banner line'' defined here should be changed whenever \.{CWEAVE}
is modified.
-@d banner "This is CWEAVE (Version 4.11)"
+@d banner "This is CWEAVE (Version 4.12)"
@c
@<Include files@>@/
@@ -94,7 +93,7 @@ char **av) /* argument values */
program=cweave;
@<Set initial values@>@;
common_init();
- @<Start \TEX/ output@>@;
+ @<Start \9{t}\TEX/ output@>@;
if (show_banner) puts(banner); /* print a ``banner line'' */
@<Store all the reserved words@>@;
phase_one(); /* read all the user's text and store the cross-references */
@@ -1077,9 +1076,9 @@ phase_one(void) {
printf("*%d",(int)section_count);
update_terminal(); /* print a progress report */
}
- @<Store cross-references in the \TEX/ part of a section@>@;
- @<Store cross-references in the definition part of a section@>@;
- @<Store cross-references in the \CEE/ part of a section@>@;
+ @<Store cross-references in the \9{t}\TEX/ part of a section@>@;
+ @<Store cross-references in the \9{d}definition part of a section@>@;
+ @<Store cross-references in the \9{c}\CEE/ part of a section@>@;
if (changed_section[section_count]) change_exists=true;
}
@@ -1156,7 +1155,7 @@ the identifiers in \CEE/ texts enclosed in \pb, or for control texts
enclosed in \.{@@\^}$\,\ldots\,$\.{@@>} or \.{@@.}$\,\ldots\,$\.{@@>}
or \.{@@:}$\,\ldots\,$\.{@@>}.
-@<Store cross-references in the \T...@>=
+@<Store cross-references in the \9{t}\T...@>=
while (true) {
switch (next_control=skip_TeX()) {
case translit_code: err_print("! Use @@l in limbo only"); continue;
@@ -1205,7 +1204,7 @@ static name_pointer res_wd_end; /* pointer to the first nonreserved identifier *
@ When we get to the following code we have |next_control>=format_code|.
-@<Store cross-references in the d...@>=
+@<Store cross-references in the \9{d}d...@>=
while (next_control<=definition) { /* |format_code| or |definition| */
if (next_control==definition) {
xref_switch=def_flag; /* implied \.{@@!} */
@@ -1262,7 +1261,7 @@ else {
@ Finally, when the \TEX/ and definition parts have been treated, we have
|next_control>=begin_C|.
-@<Store cross-references in the \CEE/...@>=
+@<Store cross-references in the \9{c}\CEE/...@>=
if (next_control<=section_name) { /* |begin_C| or |section_name| */
if (next_control==begin_C) section_xref_switch=0;
else {
@@ -1399,7 +1398,7 @@ beginning of phase two. We initialize the output variables in a slightly
tricky way so that the first line of the output file will be
`\.{\\input cwebmac}'.
-@<Start \TEX/...@>=
+@<Start \9{t}\TEX/...@>=
out_ptr=out_buf+1; out_line=1; active_file=tex_file;
tex_printf("\\input cwebma"); *out_ptr='c';
@@ -3421,10 +3420,10 @@ switch (next_control) {
app_scrap(section_scrap,maybe_math);@+
app_scrap(exp,yes_math);@+break;
case string: case constant: case verbatim:
- @<Append a string or constant@>@;@+break;
+ @<Append a \9{s}string or constant@>@;@+break;
case identifier: app_cur_id(true);@+break;
case TeX_string:
- @<Append a \TEX/ string, without forming a scrap@>@;@+break;
+ @<Append a \9{t}\TEX/ string, without forming a scrap@>@;@+break;
case '/': case '.':
app(next_control);@+app_scrap(binop,yes_math);@+break;
case '<': app_str("\\langle");@+app_scrap(prelangle,yes_math);@+break;
@@ -3546,7 +3545,7 @@ Many of the special characters in a string must be prefixed by `\.\\' so that
\TEX/ will print them properly.
@^special string characters@>
-@<Append a string or...@>={@+ int count=-1; /* characters remaining before string break */
+@<Append a \9{s}string or...@>={@+ int count=-1; /* characters remaining before string break */
switch (next_control) {
case constant: app_str("\\T{"@q}@>); break;
@.\\T@>
@@ -3612,7 +3611,7 @@ this bug is probably to enclose the \.{@@t...@@>} in \.{@@[...@@]} so that
the \TEX/ string is treated as an expression.
@^bug, known@>
-@<Append a \TEX/ string, without forming a scrap@>=
+@<Append a \9{t}\TEX/ string, without forming a scrap@>=
app_str("\\hbox{"@q}@>);
@^high-bit character handling@>
while (id_first<id_loc) {
@@ -4184,7 +4183,7 @@ if (show_progress) printf("%s","\nWriting the output file...");
section_count=0; format_visible=true; copy_limbo();
finish_line(); flush_buffer(out_buf,false,false);
/* insert a blank line, it looks nice */
-while (!input_has_ended) @<Translate the current section@>@;
+while (!input_has_ended) @<Translate the \9{c}current section@>@;
}
@ @<Predecl...@>=@+static void phase_two(void);
@@ -4212,13 +4211,13 @@ static boolean format_visible; /* should the next format declaration be output?
static boolean doing_format=false; /* are we outputting a format declaration? */
static boolean group_found=false; /* has a starred section occurred? */
-@ @<Translate the current section@>= {
+@ @<Translate the \9{c}current section@>= {
section_count++;
@<Output the code for the beginning of a new section@>@;
save_position();
- @<Translate the \TEX/ part of the current section@>@;
- @<Translate the definition part of the current section@>@;
- @<Translate the \CEE/ part of the current section@>@;
+ @<Translate the \9{t}\TEX/ part of the current section@>@;
+ @<Translate the \9{d}definition part of the current section@>@;
+ @<Translate the \9{c}\CEE/ part of the current section@>@;
@<Show cross-references to this section@>@;
@<Output the code for the end of a section@>@;
}
@@ -4258,7 +4257,7 @@ out('{'); out_section(section_count); out('}');
@ In the \TEX/ part of a section, we simply copy the source text, except that
index entries are not copied and \CEE/ text within \pb\ is translated.
-@<Translate the \T...@>= do
+@<Translate the \9{t}\T...@>= do
switch (next_control=copy_TeX()) {
case '|': init_stack(); output_C(); break;
case '@@': out('@@'); break;
@@ -4280,12 +4279,12 @@ while (next_control<format_code);
@ When we get to the following code we have |next_control>=format_code|, and
the token memory is in its initial empty state.
-@<Translate the d...@>=
+@<Translate the \9{d}d...@>=
space_checked=false;
while (next_control<=definition) { /* |format_code| or |definition| */
init_stack();
- if (next_control==definition) @<Start a macro definition@>@;
- else @<Start a format definition@>@;
+ if (next_control==definition) @<Start \9{a}a macro definition@>@;
+ else @<Start \9{a}a format definition@>@;
outer_parse(); finish_C(format_visible); format_visible=true;
doing_format=false;
}
@@ -4338,7 +4337,7 @@ if the identifier is not followed by `\.(' at all, the replacement
text starts immediately after the identifier. In the former case,
it starts after we scan the matching `\.)'.
-@<Start a macro...@>= {
+@<Start \9{a}a macro...@>= {
if (save_line!=out_line || save_place!=out_ptr || space_checked) app(backup);
if(!space_checked){emit_space_if_needed();save_position();}
app_str("\\D"); /* this will produce `\#\&{define }' */
@@ -4367,7 +4366,7 @@ it starts after we scan the matching `\.)'.
}
}
-@ @<Start a format...@>= {
+@ @<Start \9{a}a format...@>= {
doing_format=true;
if(*(loc-1)=='s' || *(loc-1)=='S') format_visible=false;
if(!space_checked){emit_space_if_needed();save_position();}
@@ -4395,7 +4394,7 @@ point to the current section name, if it has a name.
@<Private...@>=
static name_pointer this_section; /* the current section name, or zero */
-@ @<Translate the \CEE/...@>=
+@ @<Translate the \9{c}\CEE/...@>=
this_section=name_dir;
if (next_control<=section_name) {
emit_space_if_needed(); init_stack();
diff --git a/Build/source/texk/web2c/cwebdir/cwebacromac.tex b/Build/source/texk/web2c/cwebdir/cwebacromac.tex
index f8462d06a37..1743ddd95d6 100644
--- a/Build/source/texk/web2c/cwebdir/cwebacromac.tex
+++ b/Build/source/texk/web2c/cwebdir/cwebacromac.tex
@@ -1,6 +1,8 @@
% hypertext macros for CWEB listings (in addition to cwebmac.tex)
-% Version 2.0 --- May 2024
+% Used in cwebmac.tex, pdfctwimac.tex, pdfproofmac.tex
+% Version 3.0 --- August 2024
\ifx\ifacrohint\undefined\endinput\fi % these are not standalone macros
+
\ifluatex % LuaTeX is almost like pdfTeX
\protected\def\pdfliteral {\pdfextension literal}
\protected\def\pdfstartlink {\pdfextension startlink\space}
@@ -15,7 +17,16 @@
\let\pdfoutput=\outputmode
\fi % end of LuaTeX->pdfTeX redirection macros
-% Used in cwebmac.tex, pdfctwimac.tex, pdfproofmac.tex
+\def\pdfURL#1#2{\ifpdf \ifpdflua\pdfannotlink height \theheight depth \thedepth
+ attr {/Border [0 0 0]} user { /Type /Annot /Subtype /Link /A
+ << /S /URI /URI (#2) >>}\Blue #1\Black \pdfendlink % changed in 3.69
+ \else {\setbox0=\hbox{\special{pdf: bc [ \pdflinkcolor ]}{#1}%
+ \special{pdf: ec}}\special{pdf: ann width \thewidth height \theheight
+ depth \thedepth << /Border [0 0 0] /Type /Annot /Subtype /Link /A
+ << /S /URI /URI (#2) >> >>}\box0\relax}\fi
+ \else #1 ({\tt#2})\fi}
+{\catcode`\~=12 \gdef\TILDE/{~}} % ~ in a URL
+{\catcode`\_=12 \gdef\UNDER/{_}} % _ in a URL
\def\tocpages{1} % Most programs have one-page Table-of-Contents
\def\startpdf{\def\pagemode{/PageMode /UseOutlines}
\ifnum\contentspagenumber=0 \advance \pageno by -\tocpages
@@ -24,9 +35,52 @@
\else \twodigits=\pageno \pageno=\contentspagenumber
\advance \pageno by \tocpages \advance \twodigits by -\pageno
\def\pagelabels{/PageLabels << /Nums [ 0 << /S/D/St \the\pageno >>
- \the\twodigits << /S/D/St \contentspagenumber >> ] >>} \fi
+ \the\twodigits << /S/D/St \contentspagenumber >> ] >>}
+ \fi
\ifpdflua\pdfcatalog{\pagemode\space\pagelabels}
- \else \special{pdf: docview << \pagemode\space\pagelabels >>}\fi}
+ \else\special{pdf: docview << \pagemode\space\pagelabels >>}\fi}
+\ifacrohint\def\digits{\pdflink{\the\countA}\scan}
+\else\def\digits{{\the\countA}\scan}\fi
+\def\scan#1{\begingroup
+ \ifx!#1% exit on exclamation point
+ \else\ifx,#1,\space\aftergroup\scan % insert ,\space}\scan ...
+ \else\ifx\[#1\aftergroup#1% insert }\[...
+ \else\ifx\*#1\aftergroup\lapstar\aftergroup\scan % insert }\lapstar\scan ...
+ \else\ifnum`#1>`9#1\aftergroup\scan % insert #1}\scan ...
+ \else\ifnum`#1<`0#1\aftergroup\scan % insert #1}\scan ...
+ \else
+ \afterassignment\digits \aftergroup\countA
+ \aftergroup#1% insert }\countA=#1...\digits ...
+ \fi\fi\fi\fi\fi \fi
+ \endgroup}
+
+% Local variables; there are a few externals like '\toksA' and '\countA'
+\newtoks\toksB \newtoks\toksC \newtoks\toksD \newtoks\toksF \newtoks\sanitizer
+\newif\iftokprocessed \newif\ifTnum \newif\ifinstr \newif\ifhavenum
+
+\ifacrohint % Common macros for \ifacro and \ifhint to create \pdflink's
+\def\pdfnote#1.{\setbox0=\hbox{\toksA={#1.}\toksB={}\maketoks}\the\toksA}
+\def\firstsecno#1.{\setbox0=\hbox{\toksA={#1.}\toksB={}%
+ \def\makenote{\addtokens\toksB{\the\toksC}\def\makenote{\let\space\empty
+ \toksC={}\toksD={}}\makenote}\maketoks}}
+\def\addtokens#1#2{\edef\addtoks{\noexpand#1={\the#1#2}}\addtoks}
+\def\poptoks#1#2|ENDTOKS|{\let\first=#1\toksD={#1}\toksA={#2}%
+ \ifcat\noexpand\first0\countB=`#1\else\countB=0\fi}
+\def\maketoks{\expandafter\poptoks\the\toksA|ENDTOKS|%
+ \let\next=\maketoks % continue processing by default
+ \ifnum\countB>`9 \countB=0 \fi
+ \ifnum\countB<`0
+ \ifhavenum\makenote\fi
+ \ifx\first.\let\next=\maketoksdone % fullstop
+ \else \addtokens\toksB{\the\toksD} \ifx\first,\addtokens\toksB{\space}\fi
+ \fi
+ \else \addtokens\toksC{\the\toksD}\global\havenumtrue
+ \fi
+ \next}
+\def\makenote{\addtokens\toksB
+ {\noexpand\pdflink{\the\toksC}}\toksC={}\global\havenumfalse}
+\def\maketoksdone{\edef\st{\global\noexpand\toksA={\the\toksB}}\st}
+\fi % End of common pdf/hint macros
\ifacro % The following are general pdf macros
\def\thewidth{\the\wd0 \space}
@@ -34,13 +88,14 @@
\def\thedepth{\the\dp\strutbox\space}
\ifpdflua
\ifx\pdfannotlink\undefined\let\pdfannotlink\pdfstartlink\fi% for pdfTeX 0.14
- \def\pdflink#1#2{\hbox{\pdfannotlink height \theheight depth \thedepth
+ \def\pdflink#1{\hbox{\pdfannotlink height \theheight depth \thedepth
attr{/Border [0 0 0]} goto num #1 \Blue #1\Black\pdfendlink}} % changed 3.69
-\else\def\pdflink#1#2{\setbox0=\hbox{\special{pdf: bc [ \pdflinkcolor ]}{#1}%
+\else\def\pdflink#1{\setbox0=\hbox{\special{pdf: bc [ \pdflinkcolor ]}{#1}%
\special{pdf: ec}}\special{pdf: ann width \thewidth height \theheight
- depth \thedepth << /Type /Annot /Subtype /Link
- /Border [0 0 0] /A << /S /GoTo /D (#2) >> >>}\box0\relax}\fi
-\def\sanitizecommand#1#2{\addtokens\usersanitizer
+ depth \thedepth << /Type /Annot /Subtype /Link /Border [0 0 0]
+ /A << /S /GoTo /D (\romannumeral#1) >> >>}\box0\relax}
+\fi
+\def\sanitizecommand#1#2{\addtokens\sanitizer
{\noexpand\dosanitizecommand\noexpand#1{#2}}}
\def\dosanitizecommand#1#2{\ifx\nxt#1\addF{#2}\fi}
@@ -49,9 +104,11 @@
\catcode`\[=12 \catcode`\]=12 \catcode`\{=1 \catcode`\}=2
\catcode`\~=12 \def\tildechar{~} \catcode`\~=13
\catcode`\|=0 |catcode`|\=12 |def|bschar{\} |catcode`|\=0 \catcode`\|=12
-\def\makeoutlinetoks{\Tnumfalse\afterassignment\makeolproctok\let\nxt= }
+\def\makeoutlinetoks{\toksF={}\Tnumfalse
+ \afterassignment\makeolproctok\let\nxt= }
\def\makeolnexttok{\afterassignment\makeolproctok\let\nxt= }
\def\makeolgobbletok{\afterassignment\makeolnexttok\let\nxt= }
+\def\makeolgobbletokk{\afterassignment\makeolgobbletok\let\nxt= }
\def\addF#1{\addtokens\toksF{#1}\tokprocessedtrue}
% now comes a routine to "sanitize" section names, for pdf outlines
\def\makeolproctok{\tokprocessedfalse
@@ -62,9 +119,8 @@
\else\ifx^\nxt \addF^\else\ifx_\nxt \addF_% sanitize ^ and _
\else\ifx\nxt\spacechar \addF\space
\else\if\noexpand\nxt\relax % we have a control sequence; is it one we know?
- \ifx\nxt~\addF\space
- \else\ifx\nxt\onespace\addF\space
- \else\the\usersanitizer
+ \ifx\nxt~\addF\space \else\ifx\nxt\onespace\addF\space
+ \else\the\sanitizer
\iftokprocessed\else\makeolproctokctli
\iftokprocessed\else\makeolproctokctlii
\iftokprocessed\else\makeolproctokctliii % if not recognised, skip it
@@ -77,8 +133,7 @@
\else\expandafter\makeolproctokchar\meaning\nxt
\fi\fi\fi\fi\fi
\fi\fi\fi\fi\fi \fi\fi\fi
- \next
-}
+ \next}
\def\makeolproctokchar#1 #2 #3{\addF{#3}}
\def\makeolproctokctli{%
\ifx\nxt\CEE\addF{C}\let\next\makeolgobbletok % \CEE/
@@ -86,8 +141,9 @@
\else\ifx\nxt\TEX\addF{TeX}\let\next\makeolgobbletok % \TEX/
\else\ifx\nxt\TeX\addF{TeX}\else\ifx\nxt\LaTeX\addF{LaTeX}%
\else\ifx\nxt\CPLUSPLUS\addF{C++}\let\next\makeolgobbletok % \CPLUSPLUS/
- \else\ifx\nxt\Cee\addF{C}%
+ \else\ifx\nxt\Cee\addF{C}\else\ifx\nxt\dots\addF{...\space}%
\else\ifx\nxt\PB \let\next\makeolgobbletok \tokprocessedtrue % \PB{...}
+ \else\ifx\nxt\9 \expandafter\nxt\relax \let\next\makeolgobbletokk % \9{#1}
\else\ifx\nxt\.\tokprocessedtrue\instrtrue % \.{...}
% skip \|
\else\ifx\nxt\\\ifinstr\addF{\bschar\bschar}\else\tokprocessedtrue\fi
@@ -102,7 +158,7 @@
\else\ifx\nxt\PP\addF{++}\else\ifx\nxt\MM\addF{--}%
\fi\fi\fi\fi\fi \fi\fi\fi\fi\fi
\fi\fi\fi\fi\fi \fi\fi\fi\fi\fi
- \fi\fi
+ \fi\fi\fi\fi
}
\def\makeolproctokctlii{%
\ifx\nxt\MG\addF{->}\else\ifx\nxt\GG\addF{>>}%
@@ -141,48 +197,19 @@
\fi\fi\fi\fi\fi
}
\def\outlinedone{\edef\outlinest{\global\noexpand\toksE={\the\toksF}}%
- \outlinest\let\outlinedone=\relax}
+ \outlinest\let\outlinedone=\relax\outlinedone}
\fi % End of pdf macros
\ifhint
-% The following uses HINT primitives to implement:
+% The following top level macros use HINT primitives to implement:
% \HINThome to mark the position of the home page
% \HINTlabel to attach a label to the top of a section
% \HINTlink to link a section number to the section label
% \HINTcontents to produce an 'outline' item
-% These are the top level HINT macros that are used in this file
-% No HINT primitives are used directly.
\def\HINThome{\HINTdest name {HINT.home}%
\HINToutline goto name {HINT.home} depth 1 {Table of Contents}}
\def\HINTlabel{{\let\*=\empty\HINTdest num \secstar top}}
-\def\HINTlink#1#2{\HINTstartlink goto num #1 {#1}\HINTendlink}% #2 not used ???
+\def\HINTlink#1{\HINTstartlink goto num #1 {#1}\HINTendlink}
\def\HINTcontents#1#2#3{\HINToutline goto num #3 depth #2 {#1}}
\let\pdflink=\HINTlink
\fi % End of HINT macros
-
-% Common macros for \ifacro and \ifhint
-\ifacrohint
-\def\pdfnote#1.{\setbox0=\hbox{\toksA={#1.}\toksB={}\maketoks}\the\toksA}
-\def\firstsecno#1.{\setbox0=\hbox{\toksA={#1.}\toksB={}%
- \def\makenote{\addtokens\toksB{\the\toksC}\def\makenote{\toksD={}
- \toksC={}\let\space\empty}\makenote}\maketoks}}
-\def\addtokens#1#2{\edef\addtoks{\noexpand#1={\the#1#2}}\addtoks}
-\def\poptoks#1#2|ENDTOKS|{\let\first=#1\toksD={#1}%
- \ifcat\noexpand\first0\countB=`#1\else\countB=0\fi\toksA={#2}}
-\def\maketoks{\expandafter\poptoks\the\toksA|ENDTOKS|%
- \ifnum\countB>`9 \countB=0 \fi
- \ifnum\countB<`0
- \ifnum0=\countC\else\makenote\fi
- \ifx\first.\let\next=\maketoksdone\else
- \let\next=\maketoks
- \addtokens\toksB{\the\toksD}
- \ifx\first,\addtokens\toksB{\space}\fi
- \fi
- \else \addtokens\toksC{\the\toksD}\global\countC=1\let\next=\maketoks
- \fi
- \next
-}
-\def\makenote{\addtokens\toksB
- {\noexpand\pdflink{\the\toksC}{\romannumeral\the\toksC}}\toksC={}\global\countC=0}
-\def\maketoksdone{\edef\st{\global\noexpand\toksA={\the\toksB}}\st}
-\fi % End of common macros
diff --git a/Build/source/texk/web2c/cwebdir/cwebmac.tex b/Build/source/texk/web2c/cwebdir/cwebmac.tex
index 8685608528b..53454300a9a 100644
--- a/Build/source/texk/web2c/cwebdir/cwebmac.tex
+++ b/Build/source/texk/web2c/cwebdir/cwebmac.tex
@@ -1,12 +1,9 @@
% standard macros for CWEB listings (in addition to plain.tex)
-% Version 4.12 --- May 2024
+% Version 4.12 --- August 2024
\ifx\renewenvironment\undefined\else\endinput\fi % LaTeX will use other macros
\xdef\fmtversion{\fmtversion+CWEB4.12}
\chardef\cwebversion=4 \chardef\cwebrevision=12
-\let\:=\. % preserve a way to get the dot accent
- % (all other accents will still work as usual)
-
\parskip 0pt % no stretch between paragraphs
\parindent 1em % for paragraphs and for the first line of C text
@@ -28,9 +25,10 @@
\def\CPLUSPLUS/{{\mc C\PP\spacefactor1000}}
\def\Cee{\CEE/} % for backward compatibility
\def\Cpp{\CPLUSPLUS/} % for backward compatibility
-\def\9#1{}
- % with this definition of \9 you can say @:sort key}{TeX code@>
- % to alphabetize an index entry by the sort key but format with the TeX code
+\def\9#1{} % with this definition of \9 you can say @:sort key}{TeX code@>
+ % to alphabetize an index entry by the sort key but format with the TeX code
+\let\:=\. % preserve a way to get the dot accent
+ % (all other accents will still work as usual)
\def\\#1{\leavevmode\hbox{\it#1\/\kern.05em}} % italic type for identifiers
\def\|#1{\leavevmode\hbox{$#1$}} % one-letter identifiers look better this way
@@ -113,14 +111,10 @@
\let\yskip=\smallskip
\def\?{\mathrel?}
\def\,{\relax\ifmmode\mskip\thinmuskip\else\thinspace\fi}
-\def\note#1#2.{\Y\noindent{\hangindent2em%
- \baselineskip10pt\eightrm#1~\ifacrohint{\pdfnote#2.}\else#2\fi.\par}}
-\newtoks\toksA \newtoks\toksB \newtoks\toksC \newtoks\toksD
-\newtoks\toksE \newtoks\toksF \newtoks\usersanitizer
+\newtoks\toksA \newtoks\toksE
\newcount\countA \countA=0 \newcount\countB \countB=0
-\newcount\countC \countC=0 \newcount\countD \countD=0
-\newif\iftokprocessed \newif\ifTnum \newif\ifinstr
+\newcount\countNOS \countNOS=0
{\def\\{\global\let\spacechar= }\\ }
% Here we decide the output format, depending on the TeX engine in use:
@@ -137,17 +131,7 @@
\def\Black{\pdfliteral{0 g 0 G}} % use rgb colors for direct PDF output too
\def\Blue{\pdfliteral{\pdflinkcolor\space rg \pdflinkcolor\space RG}}
\fi
-\ifacrohint \input cwebacromac \fi % load hypertext macros
-\def\pdfURL#1#2{\ifpdf \ifpdflua\pdfannotlink height \theheight depth \thedepth
- attr {/Border [0 0 0]} user { /Type /Annot /Subtype /Link /A
- << /S /URI /URI (#2) >>}\Blue #1\Black \pdfendlink % changed in 3.69
- \else {\setbox0=\hbox{\special{pdf: bc [ \pdflinkcolor ]}{#1}%
- \special{pdf: ec}}\special{pdf: ann width \thewidth height \theheight
- depth \thedepth << /Border [0 0 0] /Type /Annot /Subtype /Link /A
- << /S /URI /URI (#2) >> >>}\box0\relax}\fi
- \else #1 ({\tt#2})\fi}
-{\catcode`\~=12 \gdef\TILDE/{~}} % ~ in a URL
-{\catcode`\_=12 \gdef\UNDER/{_}} % _ in a URL
+\input cwebacromac % load hypertext macros
\def\lapstar{\rlap{*}}
\def\stsec{\rightskip=0pt % get out of C mode (cf. \B)
@@ -160,6 +144,8 @@
[ @thispage /FitH @ypos ]}\fi}}\fi}
\let\startsection=\stsec
\def\defin#1{\global\advance\ind by 2 \1\&{#1 }} % begin `define' or `format'
+\def\note#1#2.{\Y\noindent{\hangindent2em\baselineskip10pt%
+ \eightrm#1~\ifacrohint{\pdfnote#2.}\else#2\fi.\par}}
\def\A{\note{See also section}} % xref for doubly defined section name
\def\As{\note{See also sections}} % xref for multiply defined section name
\def\B{\rightskip=0pt plus 100pt minus 10pt % go into C mode
@@ -187,7 +173,7 @@
\outer\def\M#1{\MN{#1}\ifon\vfil\penalty-100\vfilneg % beginning of section
\vskip\intersecskip\startsection\ignorespaces}
\outer\def\N#1#2#3.{% beginning of starred section
- \ifacro{\toksF={}\makeoutlinetoks#3\outlinedone\outlinedone}\fi
+ \ifacro{\makeoutlinetoks#3\outlinedone}\fi
\gdepth=#1\gtitle={#3}\MN{#2}%
\ifon\ifnum#1<\secpagedepth \vfil\eject % force page break if depth is small
\else\vfil\penalty-100\vfilneg\vskip\intersecskip\fi\fi
@@ -241,15 +227,13 @@
\def\oct{\hbox{$^\circ$\kern-.1em\it\aftergroup\?\aftergroup}} % CWEB style
\def\hex{\hbox{$^{\scriptscriptstyle\#}$\tt\aftergroup}} % CWEB style
\def\bin{\hbox{$^{\scriptscriptstyle b}$\tt\aftergroup}} % new in CWEB 4.3
-\def\vb#1{\leavevmode\hbox{\kern2pt\vrule\vtop{\vbox{\hrule
- \hbox{\strut\kern2pt\.{#1}\kern2pt}}
- \hrule}\vrule\kern2pt}} % verbatim string
+\def\vb#1{\leavevmode\hbox{\kern2pt\vrule\vtop{\vbox{\hrule\hbox{\strut
+ \kern2pt\.{#1}\kern2pt}}\hrule}\vrule\kern2pt}} % verbatim string
\def\p#1{\cdot 2^{#1}} % power of two (hex exponent)
\def\onmaybe{\let\ifon=\maybe} \let\maybe=\iftrue
-\newif\ifon \newif\iftitle \newif\ifpagesaved
+\newif\ifon \newif\iftitle \newif\ifpagesaved \newif\ifheader
-\newif\ifheader
\def\lheader{\headertrue\mainfont\the\pageno\eightrm\qquad\grouptitle
\hfill\title\qquad\mainfont\topsecno} % top line on left-hand pages
\def\rheader{\headertrue\mainfont\topsecno\eightrm\qquad\title\hfill
@@ -318,9 +302,7 @@
\vsize=\pageheight \advance\vsize by -\ht\sbox % the remaining height
\hsize=.5\pagewidth \advance\hsize by -10pt
% column width for the index (20pt between cols)
- \ifhint\else
- \parfillskip 0pt plus .6\hsize % try to avoid almost empty lines
- \fi
+ \ifhint\else\parfillskip 0pt plus .6\hsize\fi % avoid almost empty lines
\def\lr{L} % this tells whether the left or right column is next
\output{\if L\lr\global\setbox\lbox=\page \gdef\lr{R}
\else\normaloutput{\vbox to\pageheight{\box\sbox\vss
@@ -329,9 +311,9 @@
\message{Index:}
\parskip 0pt plus .5pt
\outer\def\I##1, ##2.{\par\hangindent2em\noindent##1:\kern1em
- \ifacrohint\pdfnote##2.\else##2\fi.} % index entry
- \def\[##1]{$\underline{##1}$} % underlined index item
- \rm \rightskip0pt plus 2.5em \tolerance 10000 \let\*=\lapstar
+ \scan##2!.} % index entry
+ \def\[##1]{$\underline{\scan##1!}$\scan} % underlined index item
+ \rm \rightskip0pt plus 2.5em \tolerance 10000
\hyphenpenalty 10000 \parindent0pt
\readindex}
\def\fin{\par\vfill\eject % this is done when we are ending the index
@@ -354,23 +336,21 @@
\ifacro \def\outsecname{Names of the sections} \let\Xpdf\X
% \ifpdflua \makebookmarks \pdfdest name {NOS} fitb % in versions < 3.68
\ifpdflua \pdfdest name {NOS} fith % changed in version 3.69
- \pdfoutline goto name {NOS} count -\the\countD {\outsecname}
- \def\X##1:##2\X{\Xpdf##1:##2\X \firstsecno##1.%
- {\toksF={}\makeoutlinetoks##2\outlinedone\outlinedone}%
- \pdfoutline goto num \the\toksA \expandafter{\the\toksE}}
+ \pdfoutline goto name {NOS} count -\the\countNOS {\outsecname}
\else \special{pdf: outline -1 << /Title (\outsecname)
- /Dest [ @thispage /FitH @ypos ] >>}
- \def\X##1:##2\X{\Xpdf##1:##2\X \firstsecno##1.%
- {\toksF={}\makeoutlinetoks##2\outlinedone\outlinedone}%
- \special{pdf: outline 0 << /Title (\the\toksE)
- /A << /S /GoTo /D (\romannumeral\the\toksA) >> >>}}
- \fi\fi
+ /Dest [ @thispage /FitH @ypos ] >>}\fi
+ \def\X##1:##2\X{\Xpdf##1:##2\X \firstsecno##1.%
+ {\makeoutlinetoks##2\outlinedone}%
+ \ifpdflua \pdfoutline goto num \the\toksA \expandafter{\the\toksE}
+ \else \special{pdf: outline 0 << /Title (\the\toksE)
+ /A << /S /GoTo /D (\romannumeral\the\toksA) >> >>}\fi}
+ \fi % \ifacro
\readsections}
\def\makebookmarks{\let\ZZ=\writebookmarkline \readcontents\relax}
\def\countsections{\message{Number of named sections:}
- {\def\I{\global\advance\countD by 1}\def\X##1\X{\relax}
+ {\def\I{\global\advance\countNOS by 1}\def\X##1\X{\relax}
\def\Q##1.{\relax}\def\Qs##1.{\relax}\def\U##1.{\relax}\def\Us##1.{\relax}
- \readsections\relax}\message{\the\countD}}
+ \readsections\relax}\message{\the\countNOS}}
\def\expnumber#1{\expandafter\ifx\csname#1\endcsname\relax 0%
\else \csname#1\endcsname \fi} % Petr Olsak's macros from texinfo.tex
\def\advancenumber#1{\countA=\expnumber{#1}\relax \advance\countA by1
@@ -396,9 +376,9 @@
\line{\consetup{#2}#1
\rm\leaders\hbox to .5em{.\hfil}\hfil
\ \ifhint
- \HINTlink{#3}{\romannumeral#3}% No page numbers in HINT
+ \HINTlink{#3}% No page numbers in HINT
\HINTcontents{#1}{#2}{#3}%
- \else\ifacro\pdflink{#3}{\romannumeral#3}\else#3\fi
+ \else\ifacro\pdflink{#3}\else#3\fi
\hbox to3em{\hss#4}\fi}}
\def\consetup#1{\ifcase#1 \bf % depth -1 (@**)
\or % depth 0 (@*)
diff --git a/Build/source/texk/web2c/cwebdir/cwebman-w2c.ch b/Build/source/texk/web2c/cwebdir/cwebman-w2c.ch
index 9e7bceb20e2..ad478b81019 100644
--- a/Build/source/texk/web2c/cwebdir/cwebman-w2c.ch
+++ b/Build/source/texk/web2c/cwebdir/cwebman-w2c.ch
@@ -94,17 +94,17 @@
@z
@x l.34
-\def\runninghead{{\ninett CWEB} USER MANUAL (VERSION 4.11)}
+\def\runninghead{{\ninett CWEB} USER MANUAL (VERSION 4.12)}
@y
\def\Kpathsea/{{\mc KPATHSEA\spacefactor1000}}
-\def\runninghead{{\ninett CWEB} USER MANUAL (Version 4.11 [\TeX~Live])}
+\def\runninghead{{\ninett CWEB} USER MANUAL (Version 4.12 [\TeX~Live])}
@z
@x l.48
-\vskip 18pt\centerline{(Version 4.11 --- December 2023)}
+\vskip 18pt\centerline{(Version 4.12 --- August 2024)}
@y
-\vskip 18pt\centerline{(Version 4.11 --- December 2023)%
-\footnote*{This document describes the extended \.{CWEB} (Version 4.11 [\TeX~Live]).}}
+\vskip 18pt\centerline{(Version 4.12 --- August 2024)%
+\footnote*{This document describes the extended \.{CWEB} (Version 4.12 [\TeX~Live]).}}
@z
@x l.79
@@ -347,9 +347,9 @@ And here's what the same excerpt looks like when typeset.
@z
@x l.1613
- \.{ { }\\vskip 15pt \\centerline\{(Version 4.11)\}{ }\\vfill\}}\cr}$$
+ \.{ { }\\vskip 15pt \\centerline\{(Version 4.12)\}{ }\\vfill\}}\cr}$$
@y
- \.{ { }\\vskip 15pt \\centerline\{(Version 4.11)\}{ }\\vfill\}}\cr}$$
+ \.{ { }\\vskip 15pt \\centerline\{(Version 4.12)\}{ }\\vfill\}}\cr}$$
@z
@x l.1728
diff --git a/Build/source/texk/web2c/cwebdir/cwebman.tex b/Build/source/texk/web2c/cwebdir/cwebman.tex
index c4bc660df3b..25654a53172 100644
--- a/Build/source/texk/web2c/cwebdir/cwebman.tex
+++ b/Build/source/texk/web2c/cwebdir/cwebman.tex
@@ -31,7 +31,7 @@
\def\lheader{\mainfont\the\pageno\hfill\sc\runninghead\hfill}
\def\rheader{\hfill\sc\runninghead\hfill\mainfont\the\pageno}
-\def\runninghead{{\ninett CWEB} USER MANUAL (VERSION 4.11)}
+\def\runninghead{{\ninett CWEB} USER MANUAL (VERSION 4.12)}
% This verbatim mode assumes that ! marks are !! in the text being copied.
\def\verbatim{\begingroup
@@ -45,7 +45,7 @@
\null\vfill
\centerline{\titlefont The {\ttitlefont CWEB} System of
Structured Documentation}
-\vskip 18pt\centerline{(Version 4.11 --- December 2023)}
+\vskip 18pt\centerline{(Version 4.12 --- August 2024)}
\vskip 24pt
\centerline{\authorfont Donald E. Knuth and Silvio Levy}
\vfill
@@ -1527,9 +1527,9 @@ features needed by the output of \.{CWEAVE}.
\printmacs{\empty}
\vskip6pt \baselineskip12pt
-\noindent And here comes the file that extends \.{cwebmac.tex} in order to
-support the ``hypertext'' features needed by the enhanced \TEX/ engines when
-processing the output of \.{CWEAVE}.
+\noindent And here comes the file \.{cwebacromac.tex} that extends
+\.{cwebmac.tex} in order to support the ``hypertext'' features needed
+by the enhanced \TEX/ engines when processing the output of \.{CWEAVE}.
\vskip6pt \baselineskip9pt
\printmacs{acro}
@@ -1610,7 +1610,7 @@ $$\lpile{\.{\\def\\topofcontents\{\\null\\vfill}\cr
\.{ { }\\titlefalse \% include headline on the contents page}\cr
\.{ { }\\def\\rheader\{\\mainfont The \{\\tt CWEAVE\}{ }processor\\hfil\}}\cr
\.{ { }\\centerline\{\\titlefont The \{\\ttitlefont CWEAVE\}{ }processor\}}\cr
- \.{ { }\\vskip 15pt \\centerline\{(Version 4.11)\}{ }\\vfill\}}\cr}$$
+ \.{ { }\\vskip 15pt \\centerline\{(Version 4.12)\}{ }\\vfill\}}\cr}$$
Redefining \.{\\rheader}, which is the headline for right-hand pages,
suffices in this case to put the desired information at the top of the
contents page.
diff --git a/Build/source/texk/web2c/cwebdir/po/cweb-tl.pot b/Build/source/texk/web2c/cwebdir/po/cweb-tl.pot
index 19c348da56a..9ba80115a93 100644
--- a/Build/source/texk/web2c/cwebdir/po/cweb-tl.pot
+++ b/Build/source/texk/web2c/cwebdir/po/cweb-tl.pot
@@ -6,9 +6,9 @@
#, fuzzy
msgid ""
msgstr ""
-"Project-Id-Version: CWEBbin 2023\n"
+"Project-Id-Version: CWEBbin 2024\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2023-12-02 13:08+0100\n"
+"POT-Creation-Date: 2024-08-12 13:10+0200\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
@@ -30,15 +30,15 @@ msgid "%s: Need one to three file arguments.\n"
msgstr ""
#: ctang-texlive.ch:39
-msgid "This is CTANGLE, Version 4.11"
+msgid "This is CTANGLE, Version 4.12"
msgstr ""
#: ctwill-texlive.ch:39
-msgid "This is CTWILL, Version 4.11"
+msgid "This is CTWILL, Version 4.12"
msgstr ""
#: cweav-texlive.ch:39
-msgid "This is CWEAVE, Version 4.11"
+msgid "This is CWEAVE, Version 4.12"
msgstr ""
#: comm-texlive.ch:512
diff --git a/Build/source/texk/web2c/cwebdir/po/cweb.pot b/Build/source/texk/web2c/cwebdir/po/cweb.pot
index 8db3c5a3168..f451101937c 100644
--- a/Build/source/texk/web2c/cwebdir/po/cweb.pot
+++ b/Build/source/texk/web2c/cwebdir/po/cweb.pot
@@ -523,15 +523,15 @@ msgid "Sorting:"
msgstr ""
#: ctang-i18n.ch:17
-msgid "This is CTANGLE (Version 4.11 [CWEBbin 2023])"
+msgid "This is CTANGLE (Version 4.12 [CWEBbin 2024])"
msgstr ""
#: cweav-twill.ch:61
-msgid "This is CTWILL (Version 4.11 [CWEBbin 2023])"
+msgid "This is CTWILL (Version 4.12 [CWEBbin 2024])"
msgstr ""
#: cweav-i18n.ch:17 cweav-twill.ch:42
-msgid "This is CWEAVE (Version 4.11 [CWEBbin 2023])"
+msgid "This is CWEAVE (Version 4.12 [CWEBbin 2024])"
msgstr ""
#: cweav-i18n.ch:301 cweav-i18n.ch:309
diff --git a/Build/source/texk/web2c/cwebdir/po/de/cweb-tl.po b/Build/source/texk/web2c/cwebdir/po/de/cweb-tl.po
index 7d875bdd177..b1412747cbf 100644
--- a/Build/source/texk/web2c/cwebdir/po/de/cweb-tl.po
+++ b/Build/source/texk/web2c/cwebdir/po/de/cweb-tl.po
@@ -1,14 +1,14 @@
# International version of CWEB (CWEBbin, TeXLive).
-# Copyright (C) 2023 Andreas Scherer et al.
+# Copyright (C) 2024 Andreas Scherer et al.
# This file is distributed under the same license as the CWEB package.
# Andreas Scherer <https://ascherer.github.io>.
#
msgid ""
msgstr ""
-"Project-Id-Version: CWEBbin 2023\n"
+"Project-Id-Version: CWEBbin 2024\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2023-12-02 13:10+0100\n"
-"PO-Revision-Date: 2023-12-02 13:10+0100\n"
+"POT-Creation-Date: 2024-08-12 13:10+0200\n"
+"PO-Revision-Date: 2024-08-12 13:10+0200\n"
"Last-Translator: Andreas Scherer <andreas_github@freenet.de>\n"
"Language-Team: German\n"
"Language: de\n"
@@ -33,16 +33,16 @@ msgid "%s: Need one to three file arguments.\n"
msgstr "%s: Benötige ein bis drei Dateiargumente.\n"
#: ctang-texlive.ch:39
-msgid "This is CTANGLE, Version 4.11"
-msgstr "Dies ist CTANGLE, Version 4.11"
+msgid "This is CTANGLE, Version 4.12"
+msgstr "Dies ist CTANGLE, Version 4.12"
#: ctwill-texlive.ch:39
-msgid "This is CTWILL, Version 4.11"
-msgstr "Dies ist CTWILL, Version 4.11"
+msgid "This is CTWILL, Version 4.12"
+msgstr "Dies ist CTWILL, Version 4.12"
#: cweav-texlive.ch:39
-msgid "This is CWEAVE, Version 4.11"
-msgstr "Dies ist CWEAVE, Version 4.11"
+msgid "This is CWEAVE, Version 4.12"
+msgstr "Dies ist CWEAVE, Version 4.12"
#: comm-texlive.ch:512
#, c-format
diff --git a/Build/source/texk/web2c/cwebdir/po/de/cweb.po b/Build/source/texk/web2c/cwebdir/po/de/cweb.po
index f93952f9a06..0b459e1b01b 100644
--- a/Build/source/texk/web2c/cwebdir/po/de/cweb.po
+++ b/Build/source/texk/web2c/cwebdir/po/de/cweb.po
@@ -603,16 +603,16 @@ msgid "Sorting:"
msgstr "Sortieren:"
#: ctang-i18n.ch:17
-msgid "This is CTANGLE (Version 4.11 [CWEBbin 2023])"
-msgstr "Dies ist CTANGLE (Version 4.11 [CWEBbin 2023])"
+msgid "This is CTANGLE (Version 4.12 [CWEBbin 2024])"
+msgstr "Dies ist CTANGLE (Version 4.12 [CWEBbin 2024])"
#: cweav-twill.ch:61
-msgid "This is CTWILL (Version 4.11 [CWEBbin 2023])"
-msgstr "Dies ist CTWILL (Version 4.11 [CWEBbin 2023])"
+msgid "This is CTWILL (Version 4.12 [CWEBbin 2024])"
+msgstr "Dies ist CTWILL (Version 4.12 [CWEBbin 2024])"
#: cweav-i18n.ch:17 cweav-twill.ch:42
-msgid "This is CWEAVE (Version 4.11 [CWEBbin 2023])"
-msgstr "Dies ist CWEAVE (Version 4.11 [CWEBbin 2023])"
+msgid "This is CWEAVE (Version 4.12 [CWEBbin 2024])"
+msgstr "Dies ist CWEAVE (Version 4.12 [CWEBbin 2024])"
#: cweav-i18n.ch:301 cweav-i18n.ch:309
msgid "buffer"
diff --git a/Build/source/texk/web2c/cwebdir/po/it/cweb.po b/Build/source/texk/web2c/cwebdir/po/it/cweb.po
index a171f020d28..2cf95a994d3 100644
--- a/Build/source/texk/web2c/cwebdir/po/it/cweb.po
+++ b/Build/source/texk/web2c/cwebdir/po/it/cweb.po
@@ -1,14 +1,14 @@
# International version of CWEB (CWEBbin, TeXLive).
-# Copyright (C) 2023 Andreas Scherer et al.
+# Copyright (C) 2024 Andreas Scherer et al.
# This file is distributed under the same license as the CWEB package.
# Andreas Scherer <https://ascherer/github.io>.
#
msgid ""
msgstr ""
-"Project-Id-Version: CWEBbin 2023\n"
+"Project-Id-Version: CWEBbin 2024\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2023-12-02 13:10+0100\n"
-"PO-Revision-Date: 2023-12-02 13:10+0100\n"
+"POT-Creation-Date: 2024-08-12 13:10+0200\n"
+"PO-Revision-Date: 2024-08-12 13:10+0200\n"
"Last-Translator: Andreas Scherer <andreas_github@freenet.de>\n"
"Language-Team: Italian\n"
"Language: it\n"
@@ -598,16 +598,16 @@ msgid "Sorting:"
msgstr "Ordinamento:"
#: ctang-i18n.ch:17
-msgid "This is CTANGLE (Version 4.11 [CWEBbin 2023])"
-msgstr "Questo è CTANGLE (Versione 4.11 [CWEBbin 2023])"
+msgid "This is CTANGLE (Version 4.12 [CWEBbin 2024])"
+msgstr "Questo è CTANGLE (Versione 4.12 [CWEBbin 2024])"
#: cweav-i18n.ch:17
-msgid "This is CWEAVE (Version 4.11 [CWEBbin 2023])"
-msgstr "Questo è CWEAVE (Versione 4.11 [CWEBbin 2023])"
+msgid "This is CWEAVE (Version 4.12 [CWEBbin 2024])"
+msgstr "Questo è CWEAVE (Versione 4.12 [CWEBbin 2024])"
#: cweav-twill.ch:68
-msgid "This is CTWILL (Version 4.11 [CWEBbin 2023])"
-msgstr "Questo è CTWILL (Versione 4.11 [CWEBbin 2023])"
+msgid "This is CTWILL (Version 4.12 [CWEBbin 2024])"
+msgstr "Questo è CTWILL (Versione 4.12 [CWEBbin 2024])"
#: cweav-i18n.ch:301 cweav-i18n.ch:309
msgid "buffer"
diff --git a/Build/source/texk/web2c/cwebdir/prod-cweave.w b/Build/source/texk/web2c/cwebdir/prod-cweave.w
index 6c75760fa2f..e7939716b72 100644
--- a/Build/source/texk/web2c/cwebdir/prod-cweave.w
+++ b/Build/source/texk/web2c/cwebdir/prod-cweave.w
@@ -2,7 +2,7 @@
% This program by Silvio Levy and Donald E. Knuth
% is based on a program by Knuth.
% It is distributed WITHOUT ANY WARRANTY, express or implied.
-% Version 4.11 --- December 2023
+% Version 4.12 --- August 2024
%
@ Here is a table of all the productions. Each production that
combines two or more consecutive scraps implicitly inserts a {\tt \$}
diff --git a/Build/source/texk/web2c/cwebdir/prod-twill.w b/Build/source/texk/web2c/cwebdir/prod-twill.w
index 63ebdb4893d..c1b67aedc90 100644
--- a/Build/source/texk/web2c/cwebdir/prod-twill.w
+++ b/Build/source/texk/web2c/cwebdir/prod-twill.w
@@ -2,7 +2,7 @@
% This program by Silvio Levy and Donald E. Knuth
% is based on a program by Knuth.
% It is distributed WITHOUT ANY WARRANTY, express or implied.
-% Version 4.11 --- December 2023
+% Version 4.12 --- August 2024
%
@ Here is a table of all the productions. Each production that
combines two or more consecutive scraps implicitly inserts a {\tt \$}
diff --git a/Build/source/texk/web2c/cwebdir/prod.w b/Build/source/texk/web2c/cwebdir/prod.w
index b0da3ddce5f..851c5ffe234 100644
--- a/Build/source/texk/web2c/cwebdir/prod.w
+++ b/Build/source/texk/web2c/cwebdir/prod.w
@@ -2,7 +2,7 @@
% This program by Silvio Levy and Donald E. Knuth
% is based on a program by Knuth.
% It is distributed WITHOUT ANY WARRANTY, express or implied.
-% Version 4.11 --- December 2023
+% Version 4.12 --- August 2024
%
@ Here is a table of all the productions. Each production that
combines two or more consecutive scraps implicitly inserts a {\tt \$}
diff --git a/Build/source/texk/web2c/cwebdir/refsort.ch b/Build/source/texk/web2c/cwebdir/refsort.ch
index 67175b030f7..2ebedfca696 100644
--- a/Build/source/texk/web2c/cwebdir/refsort.ch
+++ b/Build/source/texk/web2c/cwebdir/refsort.ch
@@ -45,12 +45,24 @@ int main()
item *x, **y;
@z
+@x [4] l.83
+ @<Copy |buf| to item |x|@>;
+@y
+ @<Copy \9{b}|buf| to item |x|@>;
+@z
+
@x [5] l.89 variable not used
{ register int k;
@y
{
@z
+@x [6] l.99
+@<Copy |buf|...@>=
+@y
+@<Copy \9{b}|buf|...@>=
+@z
+
@x [9] l.149 'register' removed in C++17
{ register int toggle=0;
@y
diff --git a/Build/source/texk/web2c/cwebdir/texinputs/dcwebmac.tex b/Build/source/texk/web2c/cwebdir/texinputs/dcwebmac.tex
index 09e222d3c3a..dd8b43bea93 100644
--- a/Build/source/texk/web2c/cwebdir/texinputs/dcwebmac.tex
+++ b/Build/source/texk/web2c/cwebdir/texinputs/dcwebmac.tex
@@ -28,7 +28,7 @@
% Version 3.64 [p21] --- Andreas Scherer, Octobre 2005
% Version 3.64 [p22] --- Andreas Scherer, March 2016
% Version 4.9 --- Andreas Scherer, June 2022
-% Version 4.12 --- Andreas Scherer, May 2024
+% Version 4.12 --- Andreas Scherer, August 2024
\ifx\undefined\botofcontents\input cwebmac.tex\fi
diff --git a/Build/source/texk/web2c/cwebdir/texinputs/fcwebmac.tex b/Build/source/texk/web2c/cwebdir/texinputs/fcwebmac.tex
index 82d67edb1a5..ff0c0b30bd9 100644
--- a/Build/source/texk/web2c/cwebdir/texinputs/fcwebmac.tex
+++ b/Build/source/texk/web2c/cwebdir/texinputs/fcwebmac.tex
@@ -28,7 +28,7 @@
% Version 3.64 [p21] --- Andreas Scherer, October 2005
% Version 3.64 [p22] --- Andreas Scherer, March 2016
% Version 4.9 --- Andreas Scherer, June 2022
-% Version 4.12 --- Andreas Scherer, May 2024
+% Version 4.12 --- Andreas Scherer, August 2024
% french translations --- Denis Roegel, 18 September 1994
diff --git a/Build/source/texk/web2c/cwebdir/texinputs/icwebmac.tex b/Build/source/texk/web2c/cwebdir/texinputs/icwebmac.tex
index 5f2e7d3a45a..cbb5696984e 100644
--- a/Build/source/texk/web2c/cwebdir/texinputs/icwebmac.tex
+++ b/Build/source/texk/web2c/cwebdir/texinputs/icwebmac.tex
@@ -28,7 +28,7 @@
% Version 3.64 [p21] --- Andreas Scherer, October 2005
% Version 3.64 [p22] --- Andreas Scherer, March 2016
% Version 4.9 --- Andreas Scherer, June 2022
-% Version 4.12 --- Andreas Scherer, May 2024
+% Version 4.12 --- Andreas Scherer, August 2024
\ifx\undefined\botofcontents\input cwebmac.tex\fi
@@ -73,7 +73,7 @@
\def\inx{\par\vskip6pt plus 1fil % we are beginning the index
\def\page{\box255 } \normalbottom
\write\cont{} % ensure that the contents file isn't empty
- \write\cont{\catcode `\noexpand\@=12\relax} % \makeatother
+ \write\cont{\catcode `\noexpand\@=12\relax} % \makeatother
\closeout\cont % the contents information has been fully gathered
\output{\ifpagesaved\normaloutput{\box\sbox}\lheader\rheader\fi
\global\setbox\sbox=\page \global\pagesavedtrue \mark{\topmark}}
@@ -82,9 +82,7 @@
\vsize=\pageheight \advance\vsize by -\ht\sbox % the remaining height
\hsize=.5\pagewidth \advance\hsize by -10pt
% column width for the index (20pt between cols)
- \ifhint\else
- \parfillskip 0pt plus .6\hsize % try to avoid almost empty lines
- \fi
+ \ifhint\else\parfillskip 0pt plus .6\hsize\fi % avoid almost empty lines
\def\lr{L} % this tells whether the left or right column is next
\output{\if L\lr\global\setbox\lbox=\page \gdef\lr{R}
\else\normaloutput{\vbox to\pageheight{\box\sbox\vss
@@ -93,9 +91,9 @@
\message{Indice Analitico:}
\parskip 0pt plus .5pt
\outer\def\I##1, ##2.{\par\hangindent2em\noindent##1:\kern1em
- \ifacrohint\pdfnote##2.\else##2\fi.} % index entry
- \def\[##1]{$\underline{##1}$} % underlined index item
- \rm \rightskip0pt plus 2.5em \tolerance 10000 \let\*=\lapstar
+ \scan##2!.} % index entry
+ \def\[##1]{$\underline{\scan##1!}$\scan} % underlined index item
+ \rm \rightskip0pt plus 2.5em \tolerance 10000
\hyphenpenalty 10000 \parindent0pt
\readindex}
\def\fin{\par\vfill\eject % this is done when we are ending the index
diff --git a/Build/source/texk/web2c/cwebdir/texinputs/pdfctproofmac.tex b/Build/source/texk/web2c/cwebdir/texinputs/pdfctproofmac.tex
index f41ad4d6334..4079d5f704b 100644
--- a/Build/source/texk/web2c/cwebdir/texinputs/pdfctproofmac.tex
+++ b/Build/source/texk/web2c/cwebdir/texinputs/pdfctproofmac.tex
@@ -1,5 +1,5 @@
% standard macros for CWEB listings (in addition to plain.tex)
-% Version 4.12 --- May 2024
+% Version 4.12 --- August 2024
% modified for proofs in CTWILL
% modified for PDF/HINT output with 'tex "\let\pdf+' or '{hi|lua|pdf|xe}tex'
% This file is not copyrighted and can be used freely.
@@ -14,15 +14,6 @@
\font\ninerm=cmr9
\let\mc=\ninerm % medium caps
-\def\CEE/{{\mc C\spacefactor1000}}
-\def\UNIX/{{\mc U\kern-.05emNIX\spacefactor1000}}
-\def\TEX/{\TeX}
-\def\CPLUSPLUS/{{\mc C\PP\spacefactor1000}}
-\def\Cee{\CEE/} % for backward compatibility
-\def\Cpp{\CPLUSPLUS/} % for backward compatibility
-\def\9#1{}
- % with this definition of \9 you can say @:sort key}{TeX code@>
- % to alphabetize an index entry by the sort key but format with the TeX code
\font\eightrm=cmr8
\let\sc=\eightrm % small caps (NOT a caps-and-small-caps font)
\let\mainfont=\tenrm
@@ -33,6 +24,16 @@
\font\tentex=cmtex10 % TeX extended character set (used in strings)
\fontdimen7\tentex=0pt % no double space after sentences
+\def\CEE/{{\mc C\spacefactor1000}}
+\def\UNIX/{{\mc U\kern-.05emNIX\spacefactor1000}}
+\def\TEX/{\TeX}
+\def\CPLUSPLUS/{{\mc C\PP\spacefactor1000}}
+\def\Cee{\CEE/} % for backward compatibility
+\def\Cpp{\CPLUSPLUS/} % for backward compatibility
+\def\9#1{}
+ % with this definition of \9 you can say @:sort key}{TeX code@>
+ % to alphabetize an index entry by the sort key but format with the TeX code
+
\def\\#1{\leavevmode\hbox{\it#1\/\kern.05em}} % italic type for identifiers
\def\|#1{\leavevmode\hbox{$#1$}} % one-letter identifiers look better this way
\def\&#1{\leavevmode\hbox{\bf
@@ -116,11 +117,9 @@
\def\note#1#2.{\Y\noindent{\hangindent2em%
\baselineskip10pt\eightrm#1~\ifacrohint{\pdfnote#2.}\else#2\fi.\par}}
-\newtoks\toksA \newtoks\toksB \newtoks\toksC \newtoks\toksD
-\newtoks\toksE \newtoks\toksF \newtoks\usersanitizer
+\newtoks\toksA \newtoks\toksE
\newcount\countA \countA=0 \newcount\countB \countB=0
-\newcount\countC \countC=0 \newcount\countD \countD=0
-\newif\iftokprocessed \newif\ifTnum \newif\ifinstr
+\newcount\countNOS \countNOS=0
{\def\\{\global\let\spacechar= }\\ }
% Here we decide the output format, depending on the TeX engine in use:
@@ -137,7 +136,7 @@
\def\Black{\pdfliteral{0 g 0 G}} % use rgb colors for direct PDF output too
\def\Blue{\pdfliteral{\pdflinkcolor\space rg \pdflinkcolor\space RG}}
\fi
-\ifacrohint \input cwebacromac \fi % load hypertext macros
+\input cwebacromac % load hypertext macros
\def\pdfURL#1#2{\ifpdf \ifpdflua\pdfannotlink height \theheight depth \thedepth
attr {/Border [0 0 0]} user { /Type /Annot /Subtype /Link /A
<< /S /URI /URI (#2) >>}\Blue #1\Black \pdfendlink % changed in 3.69
@@ -187,7 +186,7 @@
\outer\def\M#1{\MN{#1}\ifon\vfil\penalty-100\vfilneg % beginning of section
\vskip\intersecskip\startsection\ignorespaces}
\outer\def\N#1#2#3.{% beginning of starred section
- \ifpdf{\toksF={}\makeoutlinetoks#3\outlinedone\outlinedone}\fi
+ \ifpdf{\makeoutlinetoks#3\outlinedone}\fi
\gdepth=#1\gtitle={#3}\MN{#2}%
\ifon\ifnum#1<\secpagedepth \vfil\eject % force page break if depth is small
\else\vfil\penalty-100\vfilneg\vskip\intersecskip\fi\fi
@@ -327,18 +326,16 @@
\newbox\lbox % lefthand column in the index
\def\inx{\par\vskip6pt plus 1fil % we are beginning the index
\write\cont{} % ensure that the contents file isn't empty
- \write\cont{\catcode `\noexpand\@=12\relax} % \makeatother
+ \write\cont{\catcode `\noexpand\@=12\relax} % \makeatother
\closeout\cont % the contents information has been fully gathered
\output{\ifpagesaved\normaloutput{\box\sbox}\lheader\rheader\fi
- \global\setbox\sbox=\page \global\pagesavedtrue}
+ \global\setbox\sbox=\page \global\pagesavedtrue \mark{\topmark}}
\pagesavedfalse \eject % eject the page-so-far and predecessors
\setbox\sbox\vbox{\unvbox\sbox} % take it out of its box
\vsize=\pageheight \advance\vsize by -\ht\sbox % the remaining height
\hsize=.5\pagewidth \advance\hsize by -10pt
% column width for the index (20pt between cols)
- \ifhint\else
- \parfillskip 0pt plus .6\hsize % try to avoid almost empty lines
- \fi
+ \ifhint\else\parfillskip 0pt plus .6\hsize\fi % avoid almost empty lines
\def\lr{L} % this tells whether the left or right column is next
\output{\if L\lr\global\setbox\lbox=\page \gdef\lr{R}
\else\normaloutput{\vbox to\pageheight{\box\sbox\vss
@@ -347,9 +344,9 @@
\message{Index:}
\parskip 0pt plus .5pt
\outer\def\I##1, ##2.{\par\hangindent2em\noindent##1:\kern1em
- \ifacrohint\pdfnote##2.\else##2\fi.} % index entry
- \def\[##1]{$\underline{##1}$} % underlined index item
- \rm \rightskip0pt plus 2.5em \tolerance 10000 \let\*=\lapstar
+ \scan##2!.} % index entry
+ \def\[##1]{$\underline{\scan##1!}$\scan} % underlined index item
+ \rm \rightskip0pt plus 2.5em \tolerance 10000
\hyphenpenalty 10000 \parindent0pt
\readindex}
\def\fin{\par\vfill\eject % this is done when we are ending the index
@@ -370,23 +367,21 @@
\def\I{\par\hangindent 2em}\let\*=*
\ifpdf \def\outsecname{Names of the sections} \let\Xpdf\X
\ifpdflua \pdfdest name {NOS} fith
- \pdfoutline goto name {NOS} count -\the\countD {\outsecname}
- \def\X##1:##2\X{\Xpdf##1:##2\X \firstsecno##1.%
- {\toksF={}\makeoutlinetoks##2\outlinedone\outlinedone}%
- \pdfoutline goto num \the\toksA \expandafter{\the\toksE}}
+ \pdfoutline goto name {NOS} count -\the\countNOS {\outsecname}
\else \special{pdf: outline -1 << /Title (\outsecname)
- /Dest [ @thispage /FitH @ypos ] >>}
- \def\X##1:##2\X{\Xpdf##1:##2\X \firstsecno##1.%
- {\toksF={}\makeoutlinetoks##2\outlinedone\outlinedone}%
- \special{pdf: outline 0 << /Title (\the\toksE)
- /A << /S /GoTo /D (\romannumeral\the\toksA) >> >>}}
- \fi\fi
+ /Dest [ @thispage /FitH @ypos ] >>}\fi
+ \def\X##1:##2\X{\Xpdf##1:##2\X \firstsecno##1.%
+ {\makeoutlinetoks##2\outlinedone}%
+ \ifpdflua \pdfoutline goto num \the\toksA \expandafter{\the\toksE}
+ \else \special{pdf: outline 0 << /Title (\the\toksE)
+ /A << /S /GoTo /D (\romannumeral\the\toksA) >> >>}\fi}
+ \fi % \ifacro
\readsections}
\def\makebookmarks{\let\ZZ=\writebookmarkline \readcontents\relax}
\def\countsections{\message{Number of named sections:}
- {\def\I{\global\advance\countD by 1}\def\X##1\X{\relax}
+ {\def\I{\global\advance\countNOS by 1}\def\X##1\X{\relax}
\def\Q##1.{\relax}\def\Qs##1.{\relax}\def\U##1.{\relax}\def\Us##1.{\relax}
- \readsections\relax}\message{\the\countD}}
+ \readsections\relax}\message{\the\countNOS}}
\def\expnumber#1{\expandafter\ifx\csname#1\endcsname\relax 0%
\else \csname#1\endcsname \fi} % Petr Olsak's macros from texinfo.tex
\def\advancenumber#1{\countA=\expnumber{#1}\relax \advance\countA by1
@@ -412,17 +407,14 @@
\line{\consetup{#2}#1
\rm\leaders\hbox to .5em{.\hfil}\hfil
\ \ifhint
- \HINTlink{#3}{\romannumeral#3}% No page numbers in HINT
+ \HINTlink{#3}% No page numbers in HINT
\HINTcontents{#1}{#2}{#3}%
- \else\ifacro\pdflink{#3}{\romannumeral#3}\else#3\fi
+ \else\ifacro\pdflink{#3}\else#3\fi
\hbox to3em{\hss#4}\fi}}
\def\consetup#1{\ifcase#1 \bf % depth -1 (@**)
\or % depth 0 (@*)
\or \hskip2em % depth 1 (@*1)
- \or \hskip4em % depth 2 (@*2)
- \or \hskip6em % depth 3 (@*3)
- \or \hskip8em % depth 4 (@*4)
- \or \hskip10em % depth 5 (@*5)
+ \or \hskip4em \or \hskip6em \or \hskip8em \or \hskip10em % depth 2,3,4,5
\else \hskip12em \fi} % depth 6 or more
\def\noinx{\let\inx=\end} % no indexes or table of contents
\def\nosecs{\let\FIN=\fin \def\fin{\let\parfillskip=\end \FIN}}
diff --git a/Build/source/texk/web2c/cwebdir/texinputs/pdfctwimac.tex b/Build/source/texk/web2c/cwebdir/texinputs/pdfctwimac.tex
index 2a0499949e6..d1278cac3bf 100644
--- a/Build/source/texk/web2c/cwebdir/texinputs/pdfctwimac.tex
+++ b/Build/source/texk/web2c/cwebdir/texinputs/pdfctwimac.tex
@@ -1,5 +1,5 @@
% standard macros for CWEB listings (in addition to plain.tex)
-% Version 4.12 --- May 2024
+% Version 4.12 --- August 2024
% modified for pages produced by CTWILL
% further modified for page size of the MMIXware book
% modified for PDF output with 'tex "\let\pdf+' or '{lua|pdf|xe}tex'
@@ -26,15 +26,6 @@
\font\ninerm=cmr9
\let\mc=\ninerm % medium caps
-\def\CEE/{{\mc C\spacefactor1000}}
-\def\UNIX/{{\mc U\kern-.05emNIX\spacefactor1000}}
-\def\TEX/{\TeX}
-\def\CPLUSPLUS/{{\mc C\PP\spacefactor1000}}
-\def\Cee{\CEE/} % for backward compatibility
-\def\Cpp{\CPLUSPLUS/} % for backward compatibility
-\def\9#1{}
- % with this definition of \9 you can say @:sort key}{TeX code@>
- % to alphabetize an index entry by the sort key but format with the TeX code
\font\eightrm=cmr8
\font\sixrm=cmr6
\font\ninei=cmmi9
@@ -146,6 +137,16 @@
\font\titlefont=cmr7 scaled\magstep4 % title on the contents page
\font\ttitlefont=cmtt10 scaled\magstep2 % typewriter type in title
+\def\CEE/{{\mc C\spacefactor1000}}
+\def\UNIX/{{\mc U\kern-.05emNIX\spacefactor1000}}
+\def\TEX/{\TeX}
+\def\CPLUSPLUS/{{\mc C\PP\spacefactor1000}}
+\def\Cee{\CEE/} % for backward compatibility
+\def\Cpp{\CPLUSPLUS/} % for backward compatibility
+\def\9#1{}
+ % with this definition of \9 you can say @:sort key}{TeX code@>
+ % to alphabetize an index entry by the sort key but format with the TeX code
+
\def\\#1{\leavevmode\hbox{\it#1\/\kern.05em}} % italic type for identifiers
\def\|#1{\leavevmode\hbox{$#1$}} % one-letter identifiers look better this way
\def\&#1{\leavevmode\hbox{\bf
@@ -228,11 +229,9 @@
\Y\noindent{\hangindent2\em\baselineskip10pt%
\eightrm#1~\ifacro{\pdfnote#2.}\else#2\fi.\par}}
-\newtoks\toksA \newtoks\toksB \newtoks\toksC \newtoks\toksD
-\newtoks\toksE \newtoks\toksF \newtoks\usersanitizer
+\newtoks\toksA \newtoks\toksE
\newcount\countA \countA=0 \newcount\countB \countB=0
-\newcount\countC \countC=0 \newcount\countD \countD=0
-\newif\iftokprocessed \newif\ifTnum \newif\ifinstr
+\newcount\countNOS \countNOS=0
{\def\\{\global\let\spacechar= }\\ }
% Here we decide the output format, depending on the TeX engine in use:
@@ -253,7 +252,7 @@
\def\Black{\pdfliteral{0 g 0 G}} % use rgb colors for direct PDF output too
\def\Blue{\pdfliteral{\pdflinkcolor\space rg \pdflinkcolor\space RG}}
\fi
-\ifacrohint \input cwebacromac \fi % load hypertext macros
+\input cwebacromac % load hypertext macros
\def\lapstar{\rlap{*}}
\def\stsec{\tenpoint\rightskip=0pt % get out of C mode (cf. \B)
@@ -301,7 +300,7 @@
\outer\def\NN{\ifvoid\partialpage\leftfalse
\else\ifdim\ht\partialpage<\pageht \lefttrue\else\leftfalse\fi\fi\NNN}
\outer\def\NNN#1#2#3.{% beginning of starred section
- \ifacro{\toksF={}\makeoutlinetoks#3\outlinedone\outlinedone}\fi
+ \ifacro{\makeoutlinetoks#3\outlinedone}\fi
\gdepth=#1\gtitle={#3}\def\secno{#2}\xdef\secstar{#2}
\ifleft \flushout
\gdef\rlhead{\let\i=I\uppercase{\ignorespaces#3}} % running left headline
@@ -334,7 +333,7 @@
\def\T#1{\leavevmode % octal, hex or decimal constant
\hbox{$\def\?{\kern.2em}%$%
\let\ \, % C++ digit separator becomes a little white space
- \def\$##1{\egroup_{\rm##1}\bgroup}% suffix to constant
+ \def\$##1{\egroup_{\rm##1}\bgroup}%$% suffix to constant
\def\_{\cdot 10^{\aftergroup}}% power of ten (via dirty trick)
\let\~=\oct \let\^=\hex \let\\=\bin {#1}$}}%$%
\def\U{\note{This code is used in section}} % xref for use of a section
@@ -543,22 +542,20 @@
\def\outsecname{Names of the sections}
\ifpdflua \makebookmarks \countsections
\pdfdest name {NOS} fith
- \pdfoutline goto name {NOS} count -\the\countD {\outsecname}
- \def\X##1:##2\X{\firstsecno##1.%
- {\toksF={}\makeoutlinetoks##2\outlinedone\outlinedone}%
- \pdfoutline goto num \the\toksA \expandafter{\the\toksE}}
+ \pdfoutline goto name {NOS} count -\the\countNOS {\outsecname}
\else \special{pdf: outline -1 << /Title (\outsecname)
- /Dest [ @thispage /FitH @ypos ] >>}
- \def\X##1:##2\X{\firstsecno##1.%
- {\toksF={}\makeoutlinetoks##2\outlinedone\outlinedone}%
- \special{pdf: outline 0 << /Title (\the\toksE)
- /A << /S /GoTo /D (\romannumeral\the\toksA) >> >>}}
- \fi \readsections \fi}
+ /Dest [ @thispage /FitH @ypos ] >>}\fi
+ \def\X##1:##2\X{\firstsecno##1.%
+ {\makeoutlinetoks##2\outlinedone}%
+ \ifpdflua \pdfoutline goto num \the\toksA \expandafter{\the\toksE}
+ \else \special{pdf: outline 0 << /Title (\the\toksE)
+ /A << /S /GoTo /D (\romannumeral\the\toksA) >> >>}\fi}
+ \readsections \fi}
\def\makebookmarks{\let\ZZ=\writebookmarkline \readcontents\relax}
\def\countsections{\message{Number of named sections:}
- {\def\I{\global\advance\countD by 1}\def\X##1\X{\relax}
+ {\def\I{\global\advance\countNOS by 1}\def\X##1\X{\relax}
\def\Q##1.{\relax}\def\Qs##1.{\relax}\def\U##1.{\relax}\def\Us##1.{\relax}
- \readsections\relax}\message{\the\countD}}
+ \readsections\relax}\message{\the\countNOS}}
\def\writebookmarkline#1#2#3#4#5{{%
\let\(=\let \let\)=\let \let\[=\let \let\]=\let \let\/=\let
\pdfoutline goto num #3 count -\expnumber{chunk#2.#3} {#5}}}
@@ -579,7 +576,7 @@
%\def\contentsline#1#2#3#4#5{\ifnum#2=0 \smallbreak\fi
%\line{\consetup{#2}#1
%\rm\leaders\hbox to .5em{.\hfil}\hfil
- %\ \ifacro\pdflink{#3}{\romannumeral#3}\else#3\fi\hbox to3em{\hss#4}}}
+ %\ \ifacro\pdflink{#3}\else#3\fi\hbox to3em{\hss#4}}}
%\def\consetup#1{\ifcase#1 \bf % depth -1 (@**)
%\or % depth 0 (@*)
%\or \hskip2em % depth 1 (@*1)
diff --git a/Build/source/texk/web2c/dvicopy.ch b/Build/source/texk/web2c/dvicopy.ch
index 5b4ebbc5438..07b3261641b 100644
--- a/Build/source/texk/web2c/dvicopy.ch
+++ b/Build/source/texk/web2c/dvicopy.ch
@@ -8,45 +8,45 @@
% Mar 20 91 THD Updated to dvicopy 1.2
% (more recent changes in the ChangeLog)
-@x [0] l.59 WEAVE: print changes only
+@x [0] l.60 WEAVE: print changes only
\pageno=\contentspagenumber \advance\pageno by 1
@y
\pageno=\contentspagenumber \advance\pageno by 1
\let\maybe=\iffalse
@z
-@x [1] l.97 Define my_name
+@x [1] l.98 Define my_name
@d banner=='This is DVIcopy, Version 1.6' {printed when the program starts}
@y
@d my_name=='dvicopy'
@d banner=='This is DVIcopy, Version 1.6' {printed when the program starts}
@z
-@x [2] l.123 No random reading on stdin, may be not seekable.
+@x [2] l.124 No random reading on stdin, may be not seekable.
@d random_reading==true {should we skip around in the file?}
@y
@<Globals in the outer block@>=
@!random_reading:boolean; {should we skip around in the file?}
@z
-@x [3] l.134 Purge non-local 'goto'.
+@x [3] l.135 Purge non-local 'goto'.
calls the `|jump_out|' procedure, which goes to the label |final_end|.
@d final_end = 9999 {go here to wrap it up}
@y
calls the `|jump_out|' procedure.
@z
-@x [3] l.140
+@x [3] l.141
label final_end;
@y
@z
-@x [3] l.145 Set up kpathsea.
+@x [3] l.146 Set up kpathsea.
procedure initialize; {this procedure gets things started properly}
var @<Local variables for initialization@>@/
begin print_ln(banner);@/
@y
-@<Define |parse_arguments|@>
+@<Define \(|parse_arguments|@>
procedure initialize; {this procedure gets things started properly}
var @<Local variables for initialization@>@/
begin
@@ -55,7 +55,7 @@ procedure initialize; {this procedure gets things started properly}
print (banner); print_ln (version_string);
@z
-@x [5] l.177 Big enough arrays to convert the dvilj sample font document.
+@x [5] l.178 Big enough arrays to convert the dvilj sample font document.
@<Constants...@>=
@!max_fonts=100; {maximum number of distinct fonts}
@!max_chars=10000; {maximum number of different characters among all fonts}
@@ -85,7 +85,7 @@ procedure initialize; {this procedure gets things started properly}
% [7] Rename the integer types, as they collide with names used by C99.
% Rather than change the code all over the place, we use macros to do
% the renaming. This could also be done at C preprocessor level.
-@x l.244
+@x l.245
@d int_32 == integer {signed 32~bit integers}
@y
@d int_32 == integer {signed 32~bit integers}
@@ -103,7 +103,7 @@ procedure initialize; {this procedure gets things started properly}
% [11] Redirect output, so it can go to either stdout or stderr,
% depending on where the output dvi file is going.
-@x l.310
+@x l.311
@d print(#)==write(output,#)
@d print_ln(#)==write_ln(output,#)
@d new_line==write_ln(output) {start new line}
@@ -113,7 +113,7 @@ procedure initialize; {this procedure gets things started properly}
@d new_line==write_ln(term_out) {start new line}
@z
-@x [14] l.379 Permissive input.
+@x [14] l.380 Permissive input.
@!ASCII_code=" ".."~"; {a subrange of the integers}
@y
@!ASCII_code=0..255; {a subrange of the integers}
@@ -122,7 +122,7 @@ procedure initialize; {this procedure gets things started properly}
% [15] The text_char type is used as an array index into xord. The
% default type `char' produces signed integers, which are bad array
% indices in C.
-@x l.399
+@x l.400
@d text_char == char {the data type of characters in text files}
@d first_text_char=0 {ordinal number of the smallest element of |text_char|}
@d last_text_char=127 {ordinal number of the largest element of |text_char|}
@@ -132,7 +132,7 @@ procedure initialize; {this procedure gets things started properly}
@d last_text_char=255 {ordinal number of the largest element of |text_char|}
@z
-@x [23] l.572 Remove non-local goto, declare jump_out as noreturn
+@x [23] l.573 Remove non-local goto, declare jump_out as noreturn
so a procedure called |jump_out| has been introduced. This procedure, which
transfers control to the label |final_end| at the end of the program,
contains the only non-local |@!goto| statement in \.{\title}.
@@ -143,7 +143,7 @@ by a call on some system procedure that quietly terminates the program.
@y
so a procedure called |jump_out| has been introduced.
@z
-@x [23] l.581
+@x [23] l.582
@d abort(#)==begin print_ln(' ',#,'.'); jump_out;
end
@@ -170,31 +170,31 @@ uexit(1);
end;
@z
-@x [24] l.596 Declare confusion as noreturn
+@x [24] l.597 Declare confusion as noreturn
says |confusion(|indication of where we are|)|.
@y
says |confusion|(indication of where we are).
@z
-@x [24] l.599
+@x [24] l.600
procedure confusion(@!p:pckt_pointer);
@y
noreturn procedure confusion(@!p:pckt_pointer);
@z
-@x [25] l.608 Declare overflow as noreturn
+@x [25] l.609 Declare overflow as noreturn
procedure overflow(@!p:pckt_pointer;@!n:int_16u);
@y
noreturn procedure overflow(@!p:pckt_pointer;@!n:int_16u);
@z
-@x [62] l.1235 cur_name is no longer a fixed-size array.
+@x [62] l.1236 cur_name is no longer a fixed-size array.
@!cur_name:packed array[1..name_length] of char; {external name,
with no lower case letters}
@y
@!cur_name:^char;
@z
-@x [63] l.1240 Ignore arguments to make_font_name.
+@x [63] l.1241 Ignore arguments to make_font_name.
@ For \.{TFM} and \.{VF} files we just append the appropriate extension
to the file name packet; in addition a system dependent area part
(usually different for \.{TFM} and \.{VF} files) is prepended if
@@ -229,24 +229,24 @@ to |make_font_name|.
% [67] No conversion of filenames in lower case, and initialize and
% terminate for C strings. Eliminate now unused variable.
-@x l.1310
+@x l.1311
@!c:char; {a character to be appended to |cur_name|}
@y
@z
-@x [67] l.1312
+@x [67] l.1313
cur_loc:=pckt_start[n]; cur_limit:=pckt_start[n+1];
@y
cur_name := xmalloc_array (char, pckt_length (n) + pckt_length (e));
cur_loc:=pckt_start[n]; cur_limit:=pckt_start[n+1];
@z
-@x [67] l.1317
+@x [67] l.1318
if (b>="a")and(b<="z") then Decr(b)(("a"-"A")); {convert to upper case}
@y
@z
-@x [67] l.1320
+@x [67] l.1321
cur_loc:=pckt_start[e]; cur_limit:=pckt_start[e+1];
while cur_loc<cur_limit do
begin pckt_extract(b); append_res_to_name(xchr[b]);
@@ -258,13 +258,13 @@ while l_cur_name<name_length do
cur_name[l_cur_name] := 0;
@z
-@x [91] l.1716 Lower case file name extensions.
+@x [91] l.1717 Lower case file name extensions.
id4(".")("T")("F")("M")(tfm_ext); {file name extension for \.{TFM} files}
@y
id4(".")("t")("f")("m")(tfm_ext); {file name extension for \.{TFM} files}
@z
-@x [92] l.1718 Set default directory name
+@x [92] l.1719 Set default directory name
@ If no font directory has been specified, \.{\title} is supposed to use
the default \.{TFM} directory, which is a system-dependent place where
the \.{TFM} files for standard fonts are kept.
@@ -280,26 +280,26 @@ The string variable |TFM_default_area| contains the name of this area.
@ If no font directory has been specified, we search paths.
@z
-@x [93] l.1730 Remove initialization of now-defunct array
+@x [93] l.1731 Remove initialization of now-defunct array
@ @<Set init...@>=
TFM_default_area:=TFM_default_area_name;
@y
@ (No initialization to be done. Keep this module to preserve numbering.)
@z
-@x [94] l.1738 Declare bad_tfm as noreturn
+@x [94] l.1739 Declare bad_tfm as noreturn
procedure bad_tfm;
@y
noreturn procedure bad_tfm;
@z
-@x [94] l.1745 Declare bad_font as noreturn
+@x [94] l.1746 Declare bad_font as noreturn
procedure bad_font;
@y
noreturn procedure bad_font;
@z
-@x [95] l.1757 Open TFM file
+@x [95] l.1758 Open TFM file
@<TFM: Open |tfm_file|@>=
make_font_name(TFM_default_area_name_length)(TFM_default_area)(tfm_ext);
reset(tfm_file,cur_name);
@@ -318,7 +318,7 @@ if full_name then begin
end else
@z
-@x [104] l.1990 Compute alpha and beta.
+@x [104] l.1991 Compute alpha and beta.
alpha:=16;
while z>=@'40000000 do
@y
@@ -327,20 +327,20 @@ if z>=@'1000000000 then abort('Character size is too large!');
while z>=@'40000000 do
@z
-@x [108] l.2060 Declare full_name.
+@x [108] l.2061 Declare full_name.
@!dvi_loc:int_32; {where we are about to look, in |dvi_file|}
@y
@!dvi_loc:int_32; {where we are about to look, in |dvi_file|}
@!full_name: ^char;
@z
-@x [109] l.2067 Declare bad_dvi as noreturn
+@x [109] l.2068 Declare bad_dvi as noreturn
procedure bad_dvi;
@y
noreturn procedure bad_dvi;
@z
-@x [110] l.2076 Fix up opening the binary files
+@x [110] l.2077 Fix up opening the binary files
@ To prepare |dvi_file| for input, we |reset| it.
@<Open input file(s)@>=
@@ -353,7 +353,7 @@ dvi_loc:=0;
dvi_loc:=0;
@z
-@x [112] l.2114 Make dvi_length() and dvi_move() work.
+@x [112] l.2115 Make dvi_length() and dvi_move() work.
@p function dvi_length:int_32;
begin set_pos(dvi_file,-1); dvi_length:=cur_pos(dvi_file);
end;
@@ -374,13 +374,13 @@ dvi_loc:=n;
end;
@z
-@x [135] l.2467 Lower case file name extensions.
+@x [135] l.2468 Lower case file name extensions.
id3(".")("V")("F")(vf_ext); {file name extension for \.{VF} files}
@y
id3(".")("v")("f")(vf_ext); {file name extension for \.{VF} files}
@z
-@x [137] l.2481 Set default directory name
+@x [137] l.2482 Set default directory name
@ If no font directory has been specified, \.{\title} is supposed to use
the default \.{VF} directory, which is a system-dependent place where
the \.{VF} files for standard fonts are kept.
@@ -407,7 +407,7 @@ by setting the \.{VFFONTS} environment variable.
@ (No initialization to be done. Keep this module to preserve numbering.)
@z
-@x [139] l.2498 Open VF file
+@x [139] l.2499 Open VF file
@<VF: Open |vf_file| or |goto not_found|@>=
make_font_name(VF_default_area_name_length)(VF_default_area)(vf_ext);
reset(vf_file,cur_name);
@@ -426,7 +426,7 @@ if full_name then begin
end else
@z
-@x [163] l.2937 copy elements of array piece by piece
+@x [163] l.2938 copy elements of array piece by piece
@ @<VF: Start a new level@>=
append_one(push);
vf_move[vf_ptr]:=vf_move[vf_ptr-1];
@@ -444,19 +444,19 @@ append_one(push);
vf_move_assign;
@z
-@x [170] l.3033 and again...
+@x [170] l.3034 and again...
vf_move[vf_ptr]:=vf_move[vf_ptr-1];
@y
vf_move_assign;
@z
-@x [176] l.3138 break is fflush.
+@x [176] l.3139 break is fflush.
@d update_terminal == break(output) {empty the terminal output buffer}
@y
@d update_terminal == fflush(stdout) {empty the terminal output buffer}
@z
-@x [176] l.3148
+@x [176] l.3149
procedure input_ln; {inputs a line from the terminal}
var k:0..terminal_line_length;
begin if n_opt=0 then
@@ -484,42 +484,42 @@ while (k<terminal_line_length)and not eoln(input) do
end;
@z
-@x [241] l.4070 No dialog, remove unused final label.
+@x [241] l.4071 No dialog, remove unused final label.
dialog; {get options}
@y
@z
-@x [241] l.4075
+@x [241] l.4076
final_end:end.
@y
end.
@z
-@x [246] l.4124 Do this later, to avoid creating empty files.
+@x [246] l.4125 Do this later, to avoid creating empty files.
@<Open output file(s)@>=
rewrite(out_file); {prepares to write packed bytes to |out_file|}
@y
@<Open output file(s)@>=
@z
-@x [248] l.4141 Use external routine to output bytes.
+@x [248] l.4142 Use external routine to output bytes.
@d out_byte(#) == write(out_file,#) {write next \.{DVI} byte}
@y
@d out_byte(#) == put_byte(#,out_file) {write next \.{DVI} byte}
@z
-@x [260] l.4259 String declaration.
+@x [260] l.4260 String declaration.
@!comment:packed array[1..comm_length] of char; {preamble comment prefix}
@y
@!comment:const_c_string; {preamble comment prefix}
@z
-@x [261] l.4270 Output the string from 0 to len-1, not 1 to len.
+@x [261] l.4271 Output the string from 0 to len-1, not 1 to len.
for k:=1 to comm_length do append_byte(xord[comment[k]]);
@y
for k:=0 to comm_length - 1 do append_byte(xord[ucharcast(comment[k])]);
@z
-@x [293] l.4480 System-dependent changes.
+@x [293] l.4481 System-dependent changes.
This section should be replaced, if necessary, by changes to the program
that are necessary to make \.{DVIcopy} work at a particular installation.
It is usually best to design your change file so that all changes to
@@ -536,7 +536,7 @@ by the index variable |option_index|.
@d argument_is (#) == (strcmp (long_options[option_index].name, #) = 0)
-@<Define |parse_arguments|@> =
+@<Define \(|parse_arguments|@> =
procedure parse_arguments;
const n_options = 5; {Pascal won't count array lengths for us.}
var @!long_options: array[0..n_options] of getopt_struct;
diff --git a/Build/source/texk/web2c/dvicopy.web b/Build/source/texk/web2c/dvicopy.web
index e24ad85b03a..8220a18ff02 100644
--- a/Build/source/texk/web2c/dvicopy.web
+++ b/Build/source/texk/web2c/dvicopy.web
@@ -22,6 +22,7 @@
% bug fix (not for Web2C) and some typos (May 2014)
% from Udo Wermuth (u.wermuth@@icloud.com).
% 17 July 2018, still version 1.6: typos corrected, no code changes.
+% 02 Sep 2024, still version 1.6: fix sorting order of names-of-sections.
% Here is TeX material that gets inserted after \input webmac
\def\hang{\hangindent 3em\indent\ignorespaces}
@@ -32,7 +33,7 @@
\def\MF{{\logo META}\-{\logo FONT}}
\mathchardef\RA="3221 % right arrow
-\def\(#1){} % this is used to make section names sort themselves better
+\def\({} % this is used to make section names sort themselves better
\def\9#1{} % this is used for sort keys in the index
\def\title{DVI\lowercase{copy}} % don't change this line!
@@ -1747,7 +1748,7 @@ begin new_line;
case font_type(cur_fnt) of
defined_font: confusion(str_fonts);
loaded_font: bad_tfm;
- @<Cases for |bad_font|@>@;@/
+ @<Cases for \(|bad_font|@>@;@/
othercases abort('internal error');
endcases;
end;
@@ -2470,7 +2471,7 @@ id3(".")("V")("F")(vf_ext); {file name extension for \.{VF} files}
gives an error message which refers the user to \.{VFtoVP} and \.{VPtoVF},
and terminates \.{\title}.
-@<Cases for |bad_font|@>=
+@<Cases for \(|bad_font|@>=
vf_font_type: begin print('Bad VF file'); print_font(cur_fnt);
@.Bad VF file@>
print_ln(' loc=',vf_loc:1);
@@ -3523,7 +3524,7 @@ routine determines whether a page shall be processed or skipped and sets
the variable |type_setting| accordingly.
@p procedure do_bop;@/
-@<OUT: Declare additional local variables |do_bop|@>@;
+@<OUT: Declare additional local variables for |do_bop|@>@;
@!i,@!j:0..9; {indices into |count|}
begin @<Determine whether this page should be processed or skipped@>;
print('DVI: ');
@@ -3642,7 +3643,7 @@ be updated (\\{set} commands).
The |do_rule| procedure is called in order to typeset a rule.
@p procedure do_rule;@/
-@<OUT: Declare additional local variables |do_rule|@>@;
+@<OUT: Declare additional local variables for |do_rule|@>@;
@!visible:boolean;
begin h_rule_pixels@;
if (cur_h_dimen>0)and(cur_v_dimen>0) then
@@ -3663,7 +3664,7 @@ character~|cur_res| with extension~|cur_ext| from the real font~|cur_fnt|.
@p procedure do_char;@/
@<OUT: Declare local variables (if any) for |do_char|@>@;
-begin @<OUT: Typeset a |char|@>@;
+begin @<OUT: Typeset a \(|char|@>@;
if cur_upd then
begin h_upd_char(widths[cur_wp])(char_pixels[cur_cp]);
@<OUT: Move right@>@;
@@ -4284,7 +4285,7 @@ for r:=p to q-1 do out_one(bo(byte_mem[r]));
@ These are the additional local variables (if any) needed for |do_bop|;
the variables |@!i| and |@!j| are already declared.
-@<OUT: Declare additional local variables |do_bop|@>=
+@<OUT: Declare additional local variables for |do_bop|@>=
var
@ And here is the device dependent code for |do_bop|.
@@ -4378,7 +4379,7 @@ out_four(width_dimen); out_four(cur_h_dimen);
@ These are the additional local variables (if any) needed for |do_rule|;
the variable |@!visible| is already declared.
-@<OUT: Declare additional local variables |do_rule|@>=
+@<OUT: Declare additional local variables for |do_rule|@>=
var
@ And here is the device dependent code for |do_rule|.
@@ -4421,7 +4422,7 @@ out_fnt_def(cur_fnt);
@ And here is the device dependent code for |do_char|.
-@<OUT: Typeset a |char|@>=
+@<OUT: Typeset a \(|char|@>=
@!debug if font_type(cur_fnt)<>out_font_type then confusion(str_fonts);
gubed @;
if cur_fnt<>out_fnt then
diff --git a/Build/source/texk/web2c/dvitype.ch b/Build/source/texk/web2c/dvitype.ch
index c22c080a6de..d17904ff001 100644
--- a/Build/source/texk/web2c/dvitype.ch
+++ b/Build/source/texk/web2c/dvitype.ch
@@ -20,6 +20,12 @@
% 01/16/90 (SR) To version 3.2.
% (more recent changes in the ChangeLog)
+@x [0] l.29
+\def\(#1){} % this is used to make section names sort themselves better
+@y
+\def\({} % this is used to make section names sort themselves better
+@z
+
@x [0] l.32
\def\title{DVI$\,$\lowercase{type}}
@y
@@ -55,7 +61,7 @@ procedure initialize; {this procedure gets things started properly}
var i:integer; {loop index for initializations}
begin print_ln(banner);@/
@y
-@<Define |parse_arguments|@>
+@<Define \(|parse_arguments|@>
procedure initialize; {this procedure gets things started properly}
var i:integer; {loop index for initializations}
begin
@@ -535,7 +541,7 @@ Parse a Unix-style command line.
@d argument_is (#) == (strcmp (long_options[option_index].name, #) = 0)
-@<Define |parse_arguments|@> =
+@<Define \(|parse_arguments|@> =
procedure parse_arguments;
const n_options = 8; {Pascal won't count array lengths for us.}
var @!long_options: array[0..n_options] of getopt_struct;
diff --git a/Build/source/texk/web2c/euptexdir/ChangeLog b/Build/source/texk/web2c/euptexdir/ChangeLog
index 2f494fb0d90..5ec9d17d0a9 100644
--- a/Build/source/texk/web2c/euptexdir/ChangeLog
+++ b/Build/source/texk/web2c/euptexdir/ChangeLog
@@ -1,3 +1,29 @@
+2024-09-30 TANAKA Takuji <ttk@t-lab.opal.ne.jp>
+
+ * eptex.ech: kcatcode (latin_ucs(14)) is removed
+ since it will be supported in upTeX-2.00.
+
+2024-09-28 TANAKA Takuji <ttk@t-lab.opal.ne.jp>
+
+ * euptex-{ctrlsym,kinsoku,prntknj}.test:
+ Skip tests with updvitype if updvitype are not built.
+
+2024-09-21 TANAKA Takuji <ttk@t-lab.opal.ne.jp>
+
+ * eptex.ech, pdfutils.ch:
+ Better support for kcatcode=20 (modifier).
+ * euptex-{ctrlsym,kinsoku,prntknj,ucs}.test,
+ tests/{control-symbol,ctrlsym.tex,endlinechar,free_{ixsp,pena},
+ inhibitglue,kinsoku_{search,table},postpena,printkanji,ucs}.tex,
+ tests/eup-{endline,inhibitglue,jctrlsym,pknj-{e,s,u}}a.typ,
+ tests/{endline,free_{ixsp,pena},jctrlsym,kinsoku_{search,table},
+ pknj-{e,s,u},postpena,ucs-{e,s,u}}.log:
+ Add regression tests by using Kitagawa-san's and
+ Yamashita-san's sample files.
+ * tests/{free_{ixsp,pena},inhibitglue,kinsoku_{search,table},
+ postpena}.tex: Move from ../ptexdir/tests .
+ * am/euptex.am: Adjust.
+
2024-06-04 Akira Kakuto <kakuto@jcom.zaq.ne.jp>
* unbalanced-braces-eptex.ch, suppresserrors.ch: sync with the upstream
diff --git a/Build/source/texk/web2c/euptexdir/am/euptex.am b/Build/source/texk/web2c/euptexdir/am/euptex.am
index f961b447fcc..0ee6b050f71 100644
--- a/Build/source/texk/web2c/euptexdir/am/euptex.am
+++ b/Build/source/texk/web2c/euptexdir/am/euptex.am
@@ -98,16 +98,21 @@ EXTRA_DIST += \
euptex_tests = \
euptexdir/euptriptest.test euptexdir/eptriptest.test \
euptexdir/pdfprimitive.test \
- euptexdir/euptex-ctrlsym.test \
euptexdir/eupver.test \
euptexdir/upkcat.test \
- euptexdir/wcfname.test euptexdir/wcfname0.test
+ euptexdir/wcfname.test euptexdir/wcfname0.test \
+ euptexdir/euptex-ctrlsym.test \
+ euptexdir/euptex-prntknj.test \
+ euptexdir/euptex-kinsoku.test \
+ euptexdir/euptex-ucs.test
euptexdir/euptriptest.log euptexdir/eptriptest.log: \
euptex$(EXEEXT) dvitype$(EXEEXT) pltotf$(EXEEXT) tftopl$(EXEEXT)
euptexdir/pdfprimitive.log: euptex$(EXEEXT)
-euptexdir/euptex-ctrlsym.log \
- euptexdir/eupver.log euptexdir/upkcat.log \
- euptexdir/wcfname.log euptexdir/wcfname0.log: euptex$(EXEEXT)
+euptexdir/eupver.log euptexdir/upkcat.log \
+ euptexdir/wcfname.log euptexdir/wcfname0.log \
+ euptexdir/euptex-ctrlsym.log euptexdir/euptex-prntknj.log \
+ euptexdir/euptex-kinsoku.log \
+ euptexdir/euptex-ucs.log: euptex$(EXEEXT)
EXTRA_DIST += $(euptex_tests)
@@ -125,11 +130,6 @@ EXTRA_DIST += \
euptexdir/eptrip/eptrip.tex \
euptexdir/euptrip/euptrip.diffs \
euptexdir/euptrip/texmf.cnf
-euptrip.diffs: euptex$(EXEEXT) dvitype$(EXEEXT) pltotf$(EXEEXT) tftopl$(EXEEXT)
- $(triptrap_diffs) $@
-.PHONY: euptrip-clean
-euptrip-clean:
- rm -rf euptripdir
DISTCLEANFILES += euptrip.diffs
## euptexdir/pdfprimitive.test
EXTRA_DIST += \
@@ -139,7 +139,35 @@ EXTRA_DIST += \
DISTCLEANFILES += pdfprimitive-euptex.*
## euptex-ctrlsym.test
-EXTRA_DIST += euptexdir/tests/ctrlsym.log euptexdir/tests/ctrlsym.tex
-DISTCLEANFILES += eup-ctrlsym.fmt eup-ctrlsym.log eup-ctrlsym.out eup-ctrlsym.tex
+EXTRA_DIST += euptexdir/tests/ctrlsym.tex euptexdir/tests/ctrlsym.log \
+ euptexdir/tests/endlinechar.tex euptexdir/tests/endline.log \
+ euptexdir/tests/control-symbol.tex euptexdir/tests/jctrlsym.log \
+ euptexdir/tests/eup-endlinea.typ euptexdir/tests/eup-jctrlsyma.typ
+DISTCLEANFILES += eup-ctrlsym.log eup-ctrlsym.out eup-ctrlsym.tex \
+ eup-endline*.* eup-jctrlsym*.*
+## euptex-prntknj.test
+EXTRA_DIST += euptexdir/tests/printkanji.tex euptexdir/tests/pknj-s.log \
+ euptexdir/tests/pknj-e.log euptexdir/tests/pknj-u.log \
+ euptexdir/tests/eup-pknj-sa.typ euptexdir/tests/eup-pknj-ea.typ \
+ euptexdir/tests/eup-pknj-ua.typ
+DISTCLEANFILES += eup-pknj-*.*
+## euptex-kinsoku.test
+EXTRA_DIST += euptexdir/tests/postpena.tex euptexdir/tests/postpena.log \
+ euptexdir/tests/free_ixsp.tex euptexdir/tests/free_ixsp.log \
+ euptexdir/tests/free_pena.tex euptexdir/tests/free_pena.log \
+ euptexdir/tests/inhibitglue.tex euptexdir/tests/eup-inhibitgluea.typ \
+ euptexdir/tests/kinsoku-table.tex euptexdir/tests/kinsoku-table.log \
+ euptexdir/tests/kinsoku-search.tex euptexdir/tests/kinsoku-search.log
+DISTCLEANFILES += eup-postpena*.* eup-free_*.* eup-inhibitglue*.* eup-kinsoku*.*
+## euptex-ucs.test
+EXTRA_DIST += euptexdir/tests/ucs.tex euptexdir/tests/ucs-s.log \
+ euptexdir/tests/ucs-e.log euptexdir/tests/ucs-u.log
+DISTCLEANFILES += eup-ucs*.*
+
+euptrip.diffs: euptex$(EXEEXT) dvitype$(EXEEXT) pltotf$(EXEEXT) tftopl$(EXEEXT)
+ $(triptrap_diffs) $@
+.PHONY: euptrip-clean
+euptrip-clean:
+ rm -rf euptripdir
# (end of euptex.am)
diff --git a/Build/source/texk/web2c/euptexdir/eptex.ech b/Build/source/texk/web2c/euptexdir/eptex.ech
index a33ecad2bf3..348d3a3e306 100644
--- a/Build/source/texk/web2c/euptexdir/eptex.ech
+++ b/Build/source/texk/web2c/euptexdir/eptex.ech
@@ -309,6 +309,14 @@ end
@d if_tdir_code=if_case_code+4 { `\.{\\iftdir}' }
@z
+@x procedure conditional
+@!m,@!n:integer; {to be tested against the second operand}
+@!p,@!q:pointer; {for traversing token lists in \.{\\ifx} tests}
+@y
+@!m,@!n,@!s,@!v,@!nn,@!jj:integer; {to be tested against the second operand}
+@!p,@!q:pointer; {for traversing token lists in \.{\\ifx} tests}
+@z
+
@x [30.???] |scan_font_ident|: recognize \lastnodefont
else begin print_err("Missing font identifier");
@y
@@ -721,7 +729,6 @@ if j=1 then
if (multistrlen(ustringcast(buffer), limit+1,loc)>1)and
check_kcat_code(cur_tok,cur_chr) then
begin if (cur_tok=not_cjk) then cur_tok:=other_kchar;
- if (cur_tok=latin_ucs) then cur_tok:=other_token;
cur_tok:=cur_chr+cur_tok*max_cjk_val;
loc:=loc+multistrlen(ustringcast(buffer), limit+1,loc);
end
@@ -736,13 +743,35 @@ if j=1 then
@y
if check_kanji(info(p)) then {|wchar_token|}
begin
- if BYTE1(toBUFF(info(p) mod max_cjk_val))<>0 then
- begin buffer[m]:=BYTE1(toBUFF(info(p) mod max_cjk_val)); buffer2[m]:=1; incr(m); end;
- if BYTE2(toBUFF(info(p) mod max_cjk_val))<>0 then
- begin buffer[m]:=BYTE2(toBUFF(info(p) mod max_cjk_val)); buffer2[m]:=1; incr(m); end;
- if BYTE3(toBUFF(info(p) mod max_cjk_val))<>0 then
- begin buffer[m]:=BYTE3(toBUFF(info(p) mod max_cjk_val)); buffer2[m]:=1; incr(m); end;
- buffer[m]:=BYTE4(toBUFF(info(p) mod max_cjk_val)); buffer2[m]:=1; incr(m);
+ if (isinternalUPTEX) then begin
+ s:=toUCS(ktokentochr(info(p)));
+ nn:=UVSgetcodepointlength(s);
+ jj:=1;
+ while jj<=nn do begin
+ v:=UVSgetcodepointinsequence(s,jj);
+ if (v>0) then begin
+ v:=UCStoUTF8(v);
+ if BYTE1(v)<>0 then
+ begin buffer[m]:=BYTE1(v); buffer2[m]:=1; incr(m); end;
+ if BYTE2(v)<>0 then
+ begin buffer[m]:=BYTE2(v); buffer2[m]:=1; incr(m); end;
+ if BYTE3(v)<>0 then
+ begin buffer[m]:=BYTE3(v); buffer2[m]:=1; incr(m); end;
+ buffer[m]:=BYTE4(v); buffer2[m]:=1; incr(m);
+ end;
+ incr(jj);
+ end
+ end
+ else begin
+ v:=toBUFF(info(p) mod max_cjk_val);
+ if BYTE1(v)<>0 then
+ begin buffer[m]:=BYTE1(v); buffer2[m]:=1; incr(m); end;
+ if BYTE2(v)<>0 then
+ begin buffer[m]:=BYTE2(v); buffer2[m]:=1; incr(m); end;
+ if BYTE3(v)<>0 then
+ begin buffer[m]:=BYTE3(v); buffer2[m]:=1; incr(m); end;
+ buffer[m]:=BYTE4(v); buffer2[m]:=1; incr(m);
+ end;
p:=link(p);
end
else
diff --git a/Build/source/texk/web2c/euptexdir/euptex-ctrlsym.test b/Build/source/texk/web2c/euptexdir/euptex-ctrlsym.test
index 294e0cf0bf1..7284d8c6c35 100755
--- a/Build/source/texk/web2c/euptexdir/euptex-ctrlsym.test
+++ b/Build/source/texk/web2c/euptexdir/euptex-ctrlsym.test
@@ -5,24 +5,59 @@
BinDir=${BinDir:-.}
ExeExt=${ExeExt:-}
_euptex=$BinDir/euptex$ExeExt
+_updvitype=$BinDir/updvitype$ExeExt
LC_ALL=C; export LC_ALL; LANGUAGE=C; export LANGUAGE
TEXMFCNF=$srcdir/../kpathsea;export TEXMFCNF
TEXINPUTS=".;$srcdir/tests"; export TEXINPUTS
+TEXFONTS=".;$srcdir//tests"; export TEXFONTS
TEXFORMATS=.; export TEXFORMATS
-# get same filename in log
-rm -f eup-ctrlsym.tex
-$LN_S $srcdir/euptexdir/tests/ctrlsym.tex ./eup-ctrlsym.tex
-
-#exit 77
+rm -f eup-ctrlsym.* eup-endline*.* eup-jctrlsym*.*
-$_euptex -ini -etex eup-ctrlsym || exit 1
+if test ! -f ./eup-basic.tex; then
+ cp $srcdir/tests/dump-basic.tex ./eup-basic.tex
+fi
+if test ! -f ./eup-basic.fmt; then
+ $_euptex -ini -etex eup-basic || exit 1
+fi
-$_euptex -etex -fmt=eup-ctrlsym eup-ctrlsym || exit 2
+# ctrlsym.tex
+$LN_S $srcdir/euptexdir/tests/ctrlsym.tex ./eup-ctrlsym.tex
+$_euptex -etex -fmt=eup-basic eup-ctrlsym || exit 2
sed 1d eup-ctrlsym.log >eup-ctrlsym.out
diff $srcdir/euptexdir/tests/ctrlsym.log eup-ctrlsym.out || exit 3
+
+# endlinchar.tex
+$LN_S $srcdir/euptexdir/tests/endlinechar.tex ./eup-endline.tex
+$_euptex -fmt=eup-basic -interaction=batchmode eup-endline.tex || :
+
+sed 1d eup-endline.log >eup-endline.out
+
+diff $srcdir/euptexdir/tests/endline.log eup-endline.out || exit 4
+
+if test -f $_updvitype; then
+ $_updvitype ./eup-endline.dvi >euptests/xeup-endline.typ
+ sed "1d;s/TeX output 20[23].\...\...:.*'/TeX output 20XX.XX.XX'/" euptests/xeup-endline.typ >euptests/xeup-endlinea.typ
+ diff $srcdir/euptexdir/tests/eup-endlinea.typ euptests/xeup-endlinea.typ || exit 5
+fi
+
+
+# control-symbol.tex
+$LN_S $srcdir/euptexdir/tests/control-symbol.tex ./eup-jctrlsym.tex
+$_euptex -fmt=eup-basic -interaction=batchmode eup-jctrlsym.tex || :
+
+sed 1d eup-jctrlsym.log >eup-jctrlsym.out
+
+diff $srcdir/euptexdir/tests/jctrlsym.log eup-jctrlsym.out || exit 6
+
+if test -f $_updvitype; then
+ $_updvitype ./eup-jctrlsym.dvi >euptests/xeup-jctrlsym.typ
+ sed "1d;s/TeX output 20[23].\...\...:.*'/TeX output 20XX.XX.XX'/" euptests/xeup-jctrlsym.typ >euptests/xeup-jctrlsyma.typ
+ diff $srcdir/euptexdir/tests/eup-jctrlsyma.typ euptests/xeup-jctrlsyma.typ || exit 7
+fi
+
diff --git a/Build/source/texk/web2c/euptexdir/euptex-kinsoku.test b/Build/source/texk/web2c/euptexdir/euptex-kinsoku.test
new file mode 100755
index 00000000000..307b1dd6d5a
--- /dev/null
+++ b/Build/source/texk/web2c/euptexdir/euptex-kinsoku.test
@@ -0,0 +1,69 @@
+#! /bin/sh -vx
+# Copyright 2024 Japanese TeX Development Community <issue@texjp.org>
+# You may freely use, modify and/or distribute this file.
+
+BinDir=${BinDir:-.}
+ExeExt=${ExeExt:-}
+_euptex=$BinDir/euptex$ExeExt
+_updvitype=$BinDir/updvitype$ExeExt
+
+LC_ALL=C; export LC_ALL; LANGUAGE=C; export LANGUAGE
+
+TEXMFCNF=$srcdir/../kpathsea;export TEXMFCNF
+TEXINPUTS=".;$srcdir/tests"; export TEXINPUTS
+TEXFONTS=".;$srcdir//tests"; export TEXFONTS
+TEXFORMATS=.; export TEXFORMATS
+
+rm -f eup-postpena*.* eup-free_*.* eup-inhibitglue*.* eup-kinsoku*.*
+
+if test ! -f ./eup-basic.tex; then
+ cp $srcdir/tests/dump-basic.tex ./eup-basic.tex
+fi
+if test ! -f ./eup-basic.fmt; then
+ $_euptex -ini -etex eup-basic || exit 1
+fi
+
+# postpena.tex
+$LN_S $srcdir/euptexdir/tests/postpena.tex ./eup-postpena.tex
+$_euptex -fmt=eup-basic -interaction=batchmode eup-postpena.tex || :
+
+sed 1d eup-postpena.log >eup-postpena.out
+
+diff $srcdir/euptexdir/tests/postpena.log eup-postpena.out || exit 3
+
+
+# free_ixsp.tex, free_pena.tex
+$LN_S $srcdir/euptexdir/tests/free_ixsp.tex ./eup-free_ixsp.tex
+$LN_S $srcdir/euptexdir/tests/free_pena.tex ./eup-free_pena.tex
+$_euptex -fmt=eup-basic -interaction=batchmode eup-free_ixsp.tex || :
+$_euptex -fmt=eup-basic -interaction=batchmode eup-free_pena.tex || :
+
+sed 1d eup-free_ixsp.log >eup-free_ixsp.out
+sed 1d eup-free_pena.log >eup-free_pena.out
+
+diff $srcdir/euptexdir/tests/free_ixsp.log eup-free_ixsp.out || exit 4
+diff $srcdir/euptexdir/tests/free_pena.log eup-free_pena.out || exit 5
+
+
+# inhibitglue.tex
+$LN_S $srcdir/euptexdir/tests/inhibitglue.tex ./eup-inhibitglue.tex
+$_euptex -fmt=eup-basic -interaction=batchmode eup-inhibitglue.tex || :
+
+if test -f $_updvitype; then
+ $_updvitype ./eup-inhibitglue.dvi >euptests/xeup-inhibitglue.typ
+ sed "1d;s/TeX output 20[23].\...\...:.*'/TeX output 20XX.XX.XX'/" euptests/xeup-inhibitglue.typ >euptests/xeup-inhibitgluea.typ
+ diff $srcdir/euptexdir/tests/eup-inhibitgluea.typ euptests/xeup-inhibitgluea.typ || exit 7
+fi
+
+# kinsoku_table.tex, kinsoku_search.tex
+$LN_S $srcdir/euptexdir/tests/kinsoku_table.tex ./eup-kinsoku_table.tex
+$LN_S $srcdir/euptexdir/tests/kinsoku_search.tex ./eup-kinsoku_search.tex
+$_euptex -fmt=eup-basic -interaction=batchmode eup-kinsoku_table.tex || :
+$_euptex -fmt=eup-basic -interaction=batchmode eup-kinsoku_search.tex || :
+
+sed 1d eup-kinsoku_table.log >eup-kinsoku_table.out
+sed 1d eup-kinsoku_search.log >eup-kinsoku_search.out
+
+diff $srcdir/euptexdir/tests/kinsoku_table.log eup-kinsoku_table.out || exit 8
+diff $srcdir/euptexdir/tests/kinsoku_search.log eup-kinsoku_search.out || exit 9
+
diff --git a/Build/source/texk/web2c/euptexdir/euptex-prntknj.test b/Build/source/texk/web2c/euptexdir/euptex-prntknj.test
new file mode 100755
index 00000000000..a8dfa84c271
--- /dev/null
+++ b/Build/source/texk/web2c/euptexdir/euptex-prntknj.test
@@ -0,0 +1,61 @@
+#! /bin/sh -vx
+# Copyright 2024 Japanese TeX Development Community <issue@texjp.org>
+# You may freely use, modify and/or distribute this file.
+
+BinDir=${BinDir:-.}
+ExeExt=${ExeExt:-}
+_euptex=$BinDir/euptex$ExeExt
+_updvitype=$BinDir/updvitype$ExeExt
+
+LC_ALL=C; export LC_ALL; LANGUAGE=C; export LANGUAGE
+
+TEXMFCNF=$srcdir/../kpathsea;export TEXMFCNF
+OFMFONTS=".;$srcdir/tests"; export OFMFONTS
+TFMFONTS=".;$srcdir//tests"; export TFMFONTS
+TEXINPUTS=".;$srcdir/tests"; export TEXINPUTS
+TEXFORMATS=.; export TEXFORMATS
+
+rm -f eup-pknj-*.*
+$LN_S $srcdir/euptexdir/tests/printkanji.tex ./printkanji.tex
+
+if test ! -f ./eup-basic.tex; then
+ cp $srcdir/tests/dump-basic.tex ./eup-basic.tex
+fi
+if test ! -f ./eup-basic-sjis.fmt; then
+ $_euptex -ini -etex -kanji-internal=sjis -jobname=eup-basic-sjis eup-basic || exit 1
+fi
+if test ! -f ./eup-basic-euc.fmt; then
+ $_euptex -ini -etex -kanji-internal=euc -jobname=eup-basic-euc eup-basic || exit 2
+fi
+if test ! -f ./eup-basic.fmt; then
+ $_euptex -ini -etex -kanji-internal=uptex -jobname=eup-basic eup-basic || exit 3
+fi
+
+$_euptex -fmt=eup-basic-sjis -kanji-internal=sjis -jobname=eup-pknj-s -interaction=batchmode printkanji || :
+$_euptex -fmt=eup-basic-euc -kanji-internal=euc -jobname=eup-pknj-e -interaction=batchmode printkanji || :
+$_euptex -fmt=eup-basic -jobname=eup-pknj-u -interaction=batchmode printkanji || :
+
+sed "1d;s/strings out of 497[67]..$/strings out of 497XXX/;s/characters out of 6215[789]..$/characters out of 6215XXX/" eup-pknj-s.log >eup-pknj-s.out
+sed "1d;s/strings out of 497[67]..$/strings out of 497XXX/;s/characters out of 6215[789]..$/characters out of 6215XXX/" eup-pknj-e.log >eup-pknj-e.out
+sed "1d;s/strings out of 497[67]..$/strings out of 497XXX/;s/characters out of 6215[789]..$/characters out of 6215XXX/" eup-pknj-u.log >eup-pknj-u.out
+
+diff $srcdir/euptexdir/tests/pknj-s.log eup-pknj-s.out || exit 10
+diff $srcdir/euptexdir/tests/pknj-e.log eup-pknj-e.out || exit 11
+diff $srcdir/euptexdir/tests/pknj-u.log eup-pknj-u.out || exit 12
+
+if test ! -f $_updvitype; then
+ exit 0
+fi
+
+$_updvitype ./eup-pknj-s.dvi >euptests/xeup-pknj-s.typ
+$_updvitype ./eup-pknj-e.dvi >euptests/xeup-pknj-e.typ
+$_updvitype ./eup-pknj-u.dvi >euptests/xeup-pknj-u.typ
+
+sed "1d;s/TeX output 20[23].\...\...:.*'/TeX output 20XX.XX.XX'/" euptests/xeup-pknj-s.typ >euptests/xeup-pknj-sa.typ
+sed "1d;s/TeX output 20[23].\...\...:.*'/TeX output 20XX.XX.XX'/" euptests/xeup-pknj-e.typ >euptests/xeup-pknj-ea.typ
+sed "1d;s/TeX output 20[23].\...\...:.*'/TeX output 20XX.XX.XX'/" euptests/xeup-pknj-u.typ >euptests/xeup-pknj-ua.typ
+
+diff $srcdir/euptexdir/tests/eup-pknj-sa.typ euptests/xeup-pknj-sa.typ || exit 20
+diff $srcdir/euptexdir/tests/eup-pknj-ea.typ euptests/xeup-pknj-ea.typ || exit 21
+diff $srcdir/euptexdir/tests/eup-pknj-ua.typ euptests/xeup-pknj-ua.typ || exit 22
+
diff --git a/Build/source/texk/web2c/euptexdir/euptex-ucs.test b/Build/source/texk/web2c/euptexdir/euptex-ucs.test
new file mode 100755
index 00000000000..764f6e93a9d
--- /dev/null
+++ b/Build/source/texk/web2c/euptexdir/euptex-ucs.test
@@ -0,0 +1,43 @@
+#! /bin/sh -vx
+# Copyright 2024 Japanese TeX Development Community <issue@texjp.org>
+# You may freely use, modify and/or distribute this file.
+
+BinDir=${BinDir:-.}
+ExeExt=${ExeExt:-}
+_euptex=$BinDir/euptex$ExeExt
+
+LC_ALL=C; export LC_ALL; LANGUAGE=C; export LANGUAGE
+
+TEXMFCNF=$srcdir/../kpathsea;export TEXMFCNF
+OFMFONTS=".;$srcdir/tests"; export OFMFONTS
+TEXINPUTS=".;$srcdir/tests"; export TEXINPUTS
+TEXFORMATS=.; export TEXFORMATS
+
+rm -f eup-ucs*.*
+$LN_S $srcdir/euptexdir/tests/ucs.tex ./eup-ucs.tex
+
+if test ! -f ./eup-basic.tex; then
+ cp $srcdir/tests/dump-basic.tex ./eup-basic.tex
+fi
+if test ! -f ./eup-basic-sjis.fmt; then
+ $_euptex -ini -etex -kanji-internal=sjis -jobname=eup-basic-sjis eup-basic || exit 1
+fi
+if test ! -f ./eup-basic-euc.fmt; then
+ $_euptex -ini -etex -kanji-internal=euc -jobname=eup-basic-euc eup-basic || exit 2
+fi
+if test ! -f ./eup-basic.fmt; then
+ $_euptex -ini -etex -kanji-internal=uptex -jobname=eup-basic eup-basic || exit 3
+fi
+
+$_euptex -fmt=eup-basic-sjis -kanji-internal=sjis -jobname=eup-ucs-s -interaction=batchmode eup-ucs || exit 4
+$_euptex -fmt=eup-basic-euc -kanji-internal=euc -jobname=eup-ucs-e -interaction=batchmode eup-ucs || exit 5
+$_euptex -fmt=eup-basic -jobname=eup-ucs-u -interaction=batchmode eup-ucs || exit 6
+
+sed 1d eup-ucs-s.log >eup-ucs-s.out
+sed 1d eup-ucs-e.log >eup-ucs-e.out
+sed 1d eup-ucs-u.log >eup-ucs-u.out
+
+diff $srcdir/euptexdir/tests/ucs-s.log eup-ucs-s.out || exit 10
+diff $srcdir/euptexdir/tests/ucs-e.log eup-ucs-e.out || exit 11
+diff $srcdir/euptexdir/tests/ucs-u.log eup-ucs-u.out || exit 12
+
diff --git a/Build/source/texk/web2c/euptexdir/pdfutils.ch b/Build/source/texk/web2c/euptexdir/pdfutils.ch
index f62a48be121..8d22773233b 100644
--- a/Build/source/texk/web2c/euptexdir/pdfutils.ch
+++ b/Build/source/texk/web2c/euptexdir/pdfutils.ch
@@ -1176,7 +1176,7 @@ Ucharcat_convert_code:
and (illegal_Ucharcat_wchar_catcode(cur_val)) then
begin print_err("Invalid code ("); print_int(cur_val);
@.Invalid code@>
- print("), should be in the ranges 1..4, 6..8, 10..13, 16..19");
+ print("), should be in the ranges 1..4, 6..8, 10..13, 16..20");
help1("I'm going to use 12 instead of that illegal code value.");@/
error; cat:=12;
end else cat:=cur_val;
@@ -1184,7 +1184,7 @@ Ucharcat_convert_code:
begin if illegal_Ucharcat_wchar_catcode(cur_val) then
begin print_err("Invalid code ("); print_int(cur_val);
@.Invalid code@>
- print("), should be in the ranges 16..19");
+ print("), should be in the ranges 16..20");
help1("I'm going to use 18 instead of that illegal code value.");@/
error; cat:=other_kchar;
end else cat:=cur_val;
diff --git a/Build/source/texk/web2c/euptexdir/tests/control-symbol.tex b/Build/source/texk/web2c/euptexdir/tests/control-symbol.tex
new file mode 100644
index 00000000000..be05906602d
--- /dev/null
+++ b/Build/source/texk/web2c/euptexdir/tests/control-symbol.tex
@@ -0,0 +1,108 @@
+\scrollmode
+% plain TeX ソース
+\font\cmr=cmr10\cmr
+\ifx\kanjiskip\undefined\else
+ \ifnum\jis"2121="3000
+ \jfont\jpy=umin10 at 10pt\jpy
+ \else
+ \jfont\jpy=min10 at 10pt\jpy
+ \fi
+\fi
+
+\def\+{A}
+\def\+{A}
+\def\X{\+}
+\def\Y{\+}
+
+\message{■control symbol}
+% 記号類扱いの単文字命令 → control symbol
+% (この時の和文文字の挙動が tex-jp-build#37 の問題)
+\ifx\kanjiskip\undefined
+ \catcode`\+=12
+ \catcode`\+=12
+\else
+ \catcode`\+=12
+ \kcatcode`+=18
+\fi
+\show\X\relax\message{(\meaning\X)}
+\show\Y\relax\message{(\meaning\Y)}
+
+\message{■control word}
+% 普通の文字扱いの単文字命令 → control word
+\ifx\kanjiskip\undefined
+ \catcode`\+=11
+ \catcode`\+=11
+\else
+ \catcode`\+=11
+ \kcatcode`+=17
+\fi
+\show\X\relax\message{(\meaning\X)}
+\show\Y\relax\message{(\meaning\Y)}
+
+\message{■control word}
+% 複数文字命令 → control word
+\def\ABC{A}
+\def\あいう{A}
+\def\X{\ABC}
+\def\Y{\あいう}
+\show\X\relax\message{(\meaning\X)}
+\show\Y\relax\message{(\meaning\Y)}
+
+% csname
+\ifx\protected\undefined\else
+\catcode`\:=12
+\ifx\kanjiskip\undefined\else
+\kcatcode`!=18
+\fi
+\message{■csname}
+\protected\expandafter\def\csname :AB\endcsname{A}
+\protected\expandafter\def\csname !あい\endcsname{A}
+\edef\X{\csname :AB\endcsname}
+\edef\Y{\csname !あい\endcsname}
+\show\X\relax\message{(\meaning\X)}
+\show\Y\relax\message{(\meaning\Y)}
+
+\protected\expandafter\def\csname AB:\endcsname{A}
+\protected\expandafter\def\csname あい!\endcsname{A}
+\edef\X{\csname AB:\endcsname}
+\edef\Y{\csname あい!\endcsname}
+\show\X\relax\message{(\meaning\X)}
+\show\Y\relax\message{(\meaning\Y)}
+
+\ifx\enablecjktoken\undefined\else
+\message{■not_cjk}
+\kcatcode`ア=15
+\kcatcode`!=15
+\catcode"8A=12\relax
+\protected\expandafter\def\csname ア\endcsname{A}
+\protected\expandafter\def\csname !\endcsname{A}
+\edef\X{\csname ア\endcsname}
+\edef\Y{\csname !\endcsname}
+\show\X\relax\message{(\meaning\X)}
+\show\Y\relax\message{(\meaning\Y)}
+\fi
+\fi
+
+\message{■行末の和文コントロールシンボル}
+
+
+\kcatcode`】=18
+
+\def\】{◆}
+\def\@{◆}
+
+1. \】◇,\@◇
+
+2. \】%
+◇, \@%
+◇
+
+3. \】
+◇, \@
+◇
+
+4. \】 ◇, \@ ◇
+
+
+\end
+
diff --git a/Build/source/texk/web2c/euptexdir/tests/ctrlsym.tex b/Build/source/texk/web2c/euptexdir/tests/ctrlsym.tex
index f007708a2bc..d84a541707b 100644
--- a/Build/source/texk/web2c/euptexdir/tests/ctrlsym.tex
+++ b/Build/source/texk/web2c/euptexdir/tests/ctrlsym.tex
@@ -19,7 +19,8 @@
\count255=`\雪 \relax
\message{B: \the\count255. 雪\string\雪 \雪^^J}
\message{\expandafter\meaning\csname 雪\endcsname.^^J}
- \message{\expandafter\meaning\csname \Uchar"96EA\endcsname.^^J}%
+ \ifx\Uchar\undefined\else\message{\expandafter\meaning\csname \Uchar"96EA\endcsname.^^J}\fi%
+ \ifnum\jis"2121="3000
\count4095=0
\count4095=`🍌 \relax
\message{^^JC: \the\count4095^^J}
@@ -27,7 +28,8 @@
\count4095=`\🍌 \relax
\message{D: \the\count4095. 🍌\string\🍌 \🍌^^J}
\message{\expandafter\meaning\csname 🍌\endcsname.^^J}
- \message{\expandafter\meaning\csname \Uchar"1F34C\endcsname.^^J}%
+ \ifx\Uchar\undefined\else\message{\expandafter\meaning\csname \Uchar"1F34C\endcsname.^^J}\fi%
+ \fi
%--
\setbox0=\vbox{} % no dvi/xdv/pdf output
\endgroup
diff --git a/Build/source/texk/web2c/euptexdir/tests/endline.log b/Build/source/texk/web2c/euptexdir/tests/endline.log
new file mode 100644
index 00000000000..3c0524db173
--- /dev/null
+++ b/Build/source/texk/web2c/euptexdir/tests/endline.log
@@ -0,0 +1,43 @@
+entering extended mode
+ restricted \write18 enabled.
+ %&-line parsing enabled.
+**eup-endline.tex
+(./eup-endline.tex
+! Undefined control sequence.
+l.29 7a\^^e3
+ ^^81^^82b\^^e3^^81^^82
+The control sequence at the end of the top line
+of your error message was never \def'ed. If you have
+misspelled it (e.g., `\hobx'), type `I' and the correct
+spelling (e.g., `I\hbox'). Otherwise just continue,
+and I'll forget about whatever was undefined.
+
+! Undefined control sequence.
+l.29 7a\^^e3^^81^^82b\^^e3
+ ^^81^^82
+The control sequence at the end of the top line
+of your error message was never \def'ed. If you have
+misspelled it (e.g., `\hobx'), type `I' and the correct
+spelling (e.g., `I\hbox'). Otherwise just continue,
+and I'll forget about whatever was undefined.
+
+! Undefined control sequence.
+l.38 8a\^^e3^^81^^82
+
+The control sequence at the end of the top line
+of your error message was never \def'ed. If you have
+misspelled it (e.g., `\hobx'), type `I' and the correct
+spelling (e.g., `I\hbox'). Otherwise just continue,
+and I'll forget about whatever was undefined.
+
+! Undefined control sequence.
+l.42 9a\^^e3^^81^^82
+
+The control sequence at the end of the top line
+of your error message was never \def'ed. If you have
+misspelled it (e.g., `\hobx'), type `I' and the correct
+spelling (e.g., `I\hbox'). Otherwise just continue,
+and I'll forget about whatever was undefined.
+
+[1] )
+Output written on eup-endline.dvi (1 page, 256 bytes).
diff --git a/Build/source/texk/web2c/euptexdir/tests/endlinechar.tex b/Build/source/texk/web2c/euptexdir/tests/endlinechar.tex
new file mode 100644
index 00000000000..5dc8de812e7
--- /dev/null
+++ b/Build/source/texk/web2c/euptexdir/tests/endlinechar.tex
@@ -0,0 +1,57 @@
+\font\x=ec-lmr10 \x
+\parindent0pt\noautoxspacing
+\output{\shipout\box255}
+\def\あ{PPP}
+\def\,{QQQ}
+
+1a\,
+% --> 1aQQQ
+
+2a\あ
+% --> 2aPPP
+
+\endlinechar=-1\relax
+
+3a\,
+% --> 3aQQQ
+% previous pTeX, upTeX: Undefined cs.
+
+4a\あ
+% --> 4aPPP
+% prev. pTeX: Undefined cs.
+
+5a,
+% prev. upTeX: byte-seq.
+
+6aあ
+% prev. upTeX: byte-seq.
+
+7a\^^e3^^81^^82b\^^e3^^81^^82
+% 2回エラーが出るが,これは正しい.
+
+% upTeX only tests
+\ifx\enablecjktoken\undefined
+\else
+ \catcode"E3=11\relax
+ \catcode"81=11\relax
+ \catcode"82=11\relax
+ 8a\^^e3^^81^^82
+ % (p3.10.0-u1.27) --> 8aPPP
+ % (p4.0.0-u1.27) --> エラー 8a
+ \kcatcode`あ=15\relax
+ 9a\あ
+ % == 9a\^^e3^^81^^82.
+ % (p3.10.0-u1.27) --> 9aPPP
+ % (p4.0.0-u1.27) --> エラー 9a
+ \def\^^e3^^81^^82{RRR}
+ 10a\あ b\あ
+ % == 10a\^^e3^^81^^82 b\^^e3^^81^^82. --> 10aRRRbRRR
+ \kcatcode12354=16\relax
+ 11a\あ b\あ
+ % == 11a\^^e3^^81^^82 b\^^e3^^81^^82.
+ % (p3.10.0-u1.27) --> 11aRRRbRRR
+ % (p4.0.0-u1.27) --> 11aPPPbPPP
+\fi
+
+\bye
+
diff --git a/Build/source/texk/web2c/euptexdir/tests/eup-endlinea.typ b/Build/source/texk/web2c/euptexdir/tests/eup-endlinea.typ
new file mode 100644
index 00000000000..1cbfa6b7501
--- /dev/null
+++ b/Build/source/texk/web2c/euptexdir/tests/eup-endlinea.typ
@@ -0,0 +1,93 @@
+Options selected:
+ Starting page = *
+ Maximum number of pages = 1000000
+ Output level = 4 (the works)
+ Resolution = 300.00000000 pixels per inch
+numerator/denominator=25400000/473628672
+magnification=1000; 0.00006334 pixels per DVI unit
+' TeX output 20XX.XX.XX'
+Postamble starts at byte 191.
+maxv=42152922, maxh=30785863, maxstackdepth=1, totalpages=1
+Font 0: ec-lmr10---loaded at size 655360 DVI units
+
+42: beginning of page 1
+87: down3 655360 v:=0+655360=655360, vv:=42
+91: push
+level 0:(h=0,v=655360,w=0,x=0,y=0,z=0,hh=0,vv=42)
+92: fntdef1 0: ec-lmr10
+116: fntnum0 current font is ec-lmr10
+117: setchar49 ("31) h:=0+327680=327680, hh:=21
+118: setchar97 ("61) h:=327680+327680=655360, hh:=42
+119: setchar81 ("51) h:=655360+509738=1165098, hh:=74
+120: setchar81 ("51) h:=1165098+509738=1674836, hh:=106
+121: setchar81 ("51) h:=1674836+509738=2184574, hh:=138
+[1aQQQ]
+122: pop
+level 0:(h=0,v=655360,w=0,x=0,y=0,z=0,hh=0,vv=42)
+123: y3 786432 v:=655360+786432=1441792, vv:=91
+127: push
+level 0:(h=0,v=1441792,w=0,x=0,y=786432,z=0,hh=0,vv=91)
+128: setchar50 ("32) h:=0+327680=327680, hh:=21
+129: setchar97 ("61) h:=327680+327680=655360, hh:=42
+130: setchar80 ("50) h:=655360+446005=1101365, hh:=70
+131: setchar80 ("50) h:=1101365+446005=1547370, hh:=98
+132: setchar80 ("50) h:=1547370+446005=1993375, hh:=126
+[2aPPP]
+133: pop
+level 0:(h=0,v=1441792,w=0,x=0,y=786432,z=0,hh=0,vv=91)
+134: y0 786432 v:=1441792+786432=2228224, vv:=141
+135: push
+level 0:(h=0,v=2228224,w=0,x=0,y=786432,z=0,hh=0,vv=141)
+136: setchar51 ("33) h:=0+327680=327680, hh:=21
+137: setchar97 ("61) h:=327680+327680=655360, hh:=42
+138: setchar81 ("51) h:=655360+509738=1165098, hh:=74
+139: setchar81 ("51) h:=1165098+509738=1674836, hh:=106
+140: setchar81 ("51) h:=1674836+509738=2184574, hh:=138
+141: setchar52 ("34) h:=2184574+327680=2512254, hh:=159
+142: setchar97 ("61) h:=2512254+327680=2839934, hh:=180
+143: setchar80 ("50) h:=2839934+446005=3285939, hh:=208
+144: setchar80 ("50) h:=3285939+446005=3731944, hh:=236
+145: setchar80 ("50) h:=3731944+446005=4177949, hh:=264
+146: setchar53 ("35) h:=4177949+327680=4505629, hh:=285
+147: setchar97 ("61) h:=4505629+327680=4833309, hh:=306
+148: setchar54 ("36) h:=4833309+327680=5160989, hh:=327
+149: setchar97 ("61) h:=5160989+327680=5488669, hh:=348
+150: setchar55 ("37) h:=5488669+327680=5816349, hh:=369
+151: setchar97 ("61) h:=5816349+327680=6144029, hh:=390
+[3aQQQ4aPPP5a6a7a]
+152: set1 129("81) h:=6144029+491520=6635549, hh:=421
+154: w2 -18205 h:=6635549-18205=6617344, hh:=420
+157: set1 130("82) h:=6617344+473301=7090645, hh:=450
+159: setchar98 ("62) h:=7090645+364085=7454730, hh:=473
+[b]
+160: set1 129("81) h:=7454730+491520=7946250, hh:=504
+162: w0 -18205 h:=7946250-18205=7928045, hh:=503
+163: set1 130("82) h:=7928045+473301=8401346, hh:=533
+165: setchar56 ("38) h:=8401346+327680=8729026, hh:=554
+166: setchar97 ("61) h:=8729026+327680=9056706, hh:=575
+167: setchar57 ("39) h:=9056706+327680=9384386, hh:=596
+168: setchar97 ("61) h:=9384386+327680=9712066, hh:=617
+169: setchar49 ("31) h:=9712066+327680=10039746, hh:=638
+170: setchar48 ("30) h:=10039746+327680=10367426, hh:=659
+171: setchar97 ("61) h:=10367426+327680=10695106, hh:=679
+172: setchar82 ("52) h:=10695106+482410=11177516, hh:=710
+173: setchar82 ("52) h:=11177516+482410=11659926, hh:=741
+174: setchar82 ("52) h:=11659926+482410=12142336, hh:=771
+175: setchar98 ("62) h:=12142336+364085=12506421, hh:=794
+176: setchar82 ("52) h:=12506421+482410=12988831, hh:=825
+177: setchar82 ("52) h:=12988831+482410=13471241, hh:=855
+178: setchar82 ("52) h:=13471241+482410=13953651, hh:=886
+179: setchar49 ("31) h:=13953651+327680=14281331, hh:=907
+180: setchar49 ("31) h:=14281331+327680=14609011, hh:=927
+181: setchar97 ("61) h:=14609011+327680=14936691, hh:=948
+182: setchar80 ("50) h:=14936691+446005=15382696, hh:=976
+183: setchar80 ("50) h:=15382696+446005=15828701, hh:=1004
+184: setchar80 ("50) h:=15828701+446005=16274706, hh:=1032
+185: setchar98 ("62) h:=16274706+364085=16638791, hh:=1055
+186: setchar80 ("50) h:=16638791+446005=17084796, hh:=1083
+187: setchar80 ("50) h:=17084796+446005=17530801, hh:=1111
+188: setchar80 ("50) h:=17530801+446005=17976806, hh:=1139
+[8a9a10aRRRbRRR11aPPPbPPP]
+189: pop
+level 0:(h=0,v=2228224,w=0,x=0,y=786432,z=0,hh=0,vv=141)
+190: eop
diff --git a/Build/source/texk/web2c/euptexdir/tests/eup-inhibitgluea.typ b/Build/source/texk/web2c/euptexdir/tests/eup-inhibitgluea.typ
new file mode 100644
index 00000000000..85cd742743e
--- /dev/null
+++ b/Build/source/texk/web2c/euptexdir/tests/eup-inhibitgluea.typ
@@ -0,0 +1,1846 @@
+Options selected:
+ Starting page = *
+ Maximum number of pages = 1000000
+ Output level = 4 (the works)
+ Resolution = 300.00000000 pixels per inch
+numerator/denominator=25400000/473628672
+magnification=1000; 0.00006334 pixels per DVI unit
+' TeX output 20XX.XX.XX'
+Postamble starts at byte 3019.
+maxv=43725786, maxh=30785863, maxstackdepth=4, totalpages=3
+Font 2: umin10 (JFM)---loaded at size 655360 DVI units
+Font 1: cmtt10---loaded at size 655360 DVI units
+
+42: beginning of page 1
+87: push
+level 0:(h=0,v=0,w=0,x=0,y=0,z=0,hh=0,vv=0)
+88: down3 -917504 v:=0-917504=-917504, vv:=-58
+92: pop
+level 0:(h=0,v=0,w=0,x=0,y=0,z=0,hh=0,vv=0)
+93: down4 42152922 v:=0+42152922=42152922, vv:=2670
+98: push
+level 0:(h=0,v=42152922,w=0,x=0,y=0,z=0,hh=0,vv=2670)
+99: down4 -41497562 v:=42152922-41497562=655360, vv:=42
+104: push
+level 1:(h=0,v=655360,w=0,x=0,y=0,z=0,hh=0,vv=42)
+105: fntdef1 2: umin10
+127: fntnum2 current font is umin10
+128: set2 9632("25A0) type=0 h:=0+630598=630598, hh:=40
+[■]
+131: fntdef1 1: cmtt10
+153: fntnum1 current font is cmtt10
+154: setchar92 ("5C) h:=630598+344061=974659, hh:=62
+155: setchar110 ("6E) h:=974659+344061=1318720, hh:=84
+156: setchar117 ("75) h:=1318720+344061=1662781, hh:=106
+157: setchar108 ("6C) h:=1662781+344061=2006842, hh:=128
+158: setchar108 ("6C) h:=2006842+344061=2350903, hh:=150
+[\null]
+159: pop
+level 1:(h=0,v=655360,w=0,x=0,y=0,z=0,hh=0,vv=42)
+160: y3 798030 v:=655360+798030=1453390, vv:=92
+164: push
+level 1:(h=0,v=1453390,w=0,x=0,y=798030,z=0,hh=0,vv=92)
+165: fntnum2 current font is umin10
+166: set2 12354("3042) type=0 h:=0+630598=630598, hh:=40
+169: set2 65289("FF09) type=1 h:=630598+330310=960908, hh:=61
+172: set2 65288("FF08) type=6 h:=960908+330310=1291218, hh:=82
+175: set2 12354("3042) type=0 h:=1291218+630598=1921816, hh:=122
+[あ)(あ]
+178: pop
+level 1:(h=0,v=1453390,w=0,x=0,y=798030,z=0,hh=0,vv=92)
+179: y0 798030 v:=1453390+798030=2251420, vv:=143
+180: push
+level 1:(h=0,v=2251420,w=0,x=0,y=798030,z=0,hh=0,vv=143)
+181: set2 12354("3042) type=0 h:=0+630598=630598, hh:=40
+184: set2 65289("FF09) type=1 h:=630598+330310=960908, hh:=61
+187: right3 300288 h:=960908+300288=1261196, hh:=80
+191: set2 65288("FF08) type=6 h:=1261196+330310=1591506, hh:=101
+194: set2 12354("3042) type=0 h:=1591506+630598=2222104, hh:=141
+[あ) (あ]
+197: pop
+level 1:(h=0,v=2251420,w=0,x=0,y=798030,z=0,hh=0,vv=143)
+198: y0 798030 v:=2251420+798030=3049450, vv:=193
+199: push
+level 1:(h=0,v=3049450,w=0,x=0,y=798030,z=0,hh=0,vv=193)
+200: set2 12354("3042) type=0 h:=0+630598=630598, hh:=40
+203: set2 65289("FF09) type=1 h:=630598+330310=960908, hh:=61
+206: right3 300288 h:=960908+300288=1261196, hh:=80
+210: set2 65288("FF08) type=6 h:=1261196+330310=1591506, hh:=101
+213: set2 12354("3042) type=0 h:=1591506+630598=2222104, hh:=141
+[あ) (あ]
+216: pop
+level 1:(h=0,v=3049450,w=0,x=0,y=798030,z=0,hh=0,vv=193)
+217: y0 798030 v:=3049450+798030=3847480, vv:=244
+218: push
+level 1:(h=0,v=3847480,w=0,x=0,y=798030,z=0,hh=0,vv=244)
+219: set2 12354("3042) type=0 h:=0+630598=630598, hh:=40
+222: set2 65289("FF09) type=1 h:=630598+330310=960908, hh:=61
+225: right3 600576 h:=960908+600576=1561484, hh:=99
+229: set2 65288("FF08) type=6 h:=1561484+330310=1891794, hh:=120
+232: set2 12354("3042) type=0 h:=1891794+630598=2522392, hh:=160
+[あ) (あ]
+235: pop
+level 1:(h=0,v=3847480,w=0,x=0,y=798030,z=0,hh=0,vv=244)
+236: y0 798030 v:=3847480+798030=4645510, vv:=294
+237: push
+level 1:(h=0,v=4645510,w=0,x=0,y=798030,z=0,hh=0,vv=294)
+238: set2 12354("3042) type=0 h:=0+630598=630598, hh:=40
+241: set2 65289("FF09) type=1 h:=630598+330310=960908, hh:=61
+244: right3 600576 h:=960908+600576=1561484, hh:=99
+248: set2 65288("FF08) type=6 h:=1561484+330310=1891794, hh:=120
+251: set2 12354("3042) type=0 h:=1891794+630598=2522392, hh:=160
+[あ) (あ]
+254: pop
+level 1:(h=0,v=4645510,w=0,x=0,y=798030,z=0,hh=0,vv=294)
+255: y0 798030 v:=4645510+798030=5443540, vv:=345
+256: push
+level 1:(h=0,v=5443540,w=0,x=0,y=798030,z=0,hh=0,vv=345)
+257: set2 9632("25A0) type=0 h:=0+630598=630598, hh:=40
+[■]
+260: fntnum1 current font is cmtt10
+261: setchar92 ("5C) h:=630598+344061=974659, hh:=62
+262: setchar117 ("75) h:=974659+344061=1318720, hh:=84
+263: setchar110 ("6E) h:=1318720+344061=1662781, hh:=106
+264: setchar98 ("62) h:=1662781+344061=2006842, hh:=128
+265: setchar111 ("6F) h:=2006842+344061=2350903, hh:=150
+266: setchar120 ("78) h:=2350903+344061=2694964, hh:=172
+[\unbox]
+267: pop
+level 1:(h=0,v=5443540,w=0,x=0,y=798030,z=0,hh=0,vv=345)
+268: y0 798030 v:=5443540+798030=6241570, vv:=395
+269: push
+level 1:(h=0,v=6241570,w=0,x=0,y=798030,z=0,hh=0,vv=395)
+270: fntnum2 current font is umin10
+271: set2 12354("3042) type=0 h:=0+630598=630598, hh:=40
+274: set2 65289("FF09) type=1 h:=630598+330310=960908, hh:=61
+277: set2 65288("FF08) type=6 h:=960908+330310=1291218, hh:=82
+280: set2 12354("3042) type=0 h:=1291218+630598=1921816, hh:=122
+[あ)(あ]
+283: pop
+level 1:(h=0,v=6241570,w=0,x=0,y=798030,z=0,hh=0,vv=395)
+284: y0 798030 v:=6241570+798030=7039600, vv:=446
+285: push
+level 1:(h=0,v=7039600,w=0,x=0,y=798030,z=0,hh=0,vv=446)
+286: set2 12354("3042) type=0 h:=0+630598=630598, hh:=40
+289: set2 65289("FF09) type=1 h:=630598+330310=960908, hh:=61
+292: right3 300288 h:=960908+300288=1261196, hh:=80
+296: set2 65288("FF08) type=6 h:=1261196+330310=1591506, hh:=101
+299: set2 12354("3042) type=0 h:=1591506+630598=2222104, hh:=141
+[あ) (あ]
+302: pop
+level 1:(h=0,v=7039600,w=0,x=0,y=798030,z=0,hh=0,vv=446)
+303: y0 798030 v:=7039600+798030=7837630, vv:=496
+304: push
+level 1:(h=0,v=7837630,w=0,x=0,y=798030,z=0,hh=0,vv=496)
+305: set2 12354("3042) type=0 h:=0+630598=630598, hh:=40
+308: set2 65289("FF09) type=1 h:=630598+330310=960908, hh:=61
+311: set2 65288("FF08) type=6 h:=960908+330310=1291218, hh:=82
+314: set2 12354("3042) type=0 h:=1291218+630598=1921816, hh:=122
+[あ)(あ]
+317: pop
+level 1:(h=0,v=7837630,w=0,x=0,y=798030,z=0,hh=0,vv=496)
+318: y0 798030 v:=7837630+798030=8635660, vv:=547
+319: push
+level 1:(h=0,v=8635660,w=0,x=0,y=798030,z=0,hh=0,vv=547)
+320: set2 12354("3042) type=0 h:=0+630598=630598, hh:=40
+323: set2 65289("FF09) type=1 h:=630598+330310=960908, hh:=61
+326: right3 300288 h:=960908+300288=1261196, hh:=80
+330: set2 65288("FF08) type=6 h:=1261196+330310=1591506, hh:=101
+333: set2 12354("3042) type=0 h:=1591506+630598=2222104, hh:=141
+[あ) (あ]
+336: pop
+level 1:(h=0,v=8635660,w=0,x=0,y=798030,z=0,hh=0,vv=547)
+337: y0 798030 v:=8635660+798030=9433690, vv:=598
+338: push
+level 1:(h=0,v=9433690,w=0,x=0,y=798030,z=0,hh=0,vv=598)
+339: set2 12354("3042) type=0 h:=0+630598=630598, hh:=40
+342: set2 65289("FF09) type=1 h:=630598+330310=960908, hh:=61
+345: right3 600576 h:=960908+600576=1561484, hh:=99
+349: set2 65288("FF08) type=6 h:=1561484+330310=1891794, hh:=120
+352: set2 12354("3042) type=0 h:=1891794+630598=2522392, hh:=160
+[あ) (あ]
+355: pop
+level 1:(h=0,v=9433690,w=0,x=0,y=798030,z=0,hh=0,vv=598)
+356: y0 798030 v:=9433690+798030=10231720, vv:=648
+357: push
+level 1:(h=0,v=10231720,w=0,x=0,y=798030,z=0,hh=0,vv=648)
+358: set2 12354("3042) type=0 h:=0+630598=630598, hh:=40
+361: set2 65289("FF09) type=1 h:=630598+330310=960908, hh:=61
+364: set2 65288("FF08) type=6 h:=960908+330310=1291218, hh:=82
+367: set2 12354("3042) type=0 h:=1291218+630598=1921816, hh:=122
+[あ)(あ]
+370: pop
+level 1:(h=0,v=10231720,w=0,x=0,y=798030,z=0,hh=0,vv=648)
+371: z3 798031 v:=10231720+798031=11029751, vv:=699
+375: push
+level 1:(h=0,v=11029751,w=0,x=0,y=798030,z=798031,hh=0,vv=699)
+376: set2 12354("3042) type=0 h:=0+630598=630598, hh:=40
+379: set2 65289("FF09) type=1 h:=630598+330310=960908, hh:=61
+382: right3 300288 h:=960908+300288=1261196, hh:=80
+386: set2 28450("6F22) type=0 h:=1261196+630598=1891794, hh:=120
+389: set2 65288("FF08) type=6 h:=1891794+330310=2222104, hh:=141
+392: set2 12354("3042) type=0 h:=2222104+630598=2852702, hh:=181
+[あ) 漢(あ]
+395: pop
+level 1:(h=0,v=11029751,w=0,x=0,y=798030,z=798031,hh=0,vv=699)
+396: y0 798030 v:=11029751+798030=11827781, vv:=749
+397: push
+level 1:(h=0,v=11827781,w=0,x=0,y=798030,z=798031,hh=0,vv=749)
+398: set2 12354("3042) type=0 h:=0+630598=630598, hh:=40
+401: set2 65289("FF09) type=1 h:=630598+330310=960908, hh:=61
+404: set2 28450("6F22) type=0 h:=960908+630598=1591506, hh:=101
+407: right3 300288 h:=1591506+300288=1891794, hh:=120
+411: set2 65288("FF08) type=6 h:=1891794+330310=2222104, hh:=141
+414: set2 12354("3042) type=0 h:=2222104+630598=2852702, hh:=181
+[あ)漢 (あ]
+417: pop
+level 1:(h=0,v=11827781,w=0,x=0,y=798030,z=798031,hh=0,vv=749)
+418: y0 798030 v:=11827781+798030=12625811, vv:=800
+419: push
+level 1:(h=0,v=12625811,w=0,x=0,y=798030,z=798031,hh=0,vv=800)
+420: set2 12354("3042) type=0 h:=0+630598=630598, hh:=40
+423: set2 65289("FF09) type=1 h:=630598+330310=960908, hh:=61
+426: w3 300288 h:=960908+300288=1261196, hh:=80
+430: set2 28450("6F22) type=0 h:=1261196+630598=1891794, hh:=120
+433: set2 28450("6F22) type=0 h:=1891794+630598=2522392, hh:=160
+436: w0 300288 h:=2522392+300288=2822680, hh:=179
+437: set2 65288("FF08) type=6 h:=2822680+330310=3152990, hh:=200
+440: set2 12354("3042) type=0 h:=3152990+630598=3783588, hh:=240
+[あ) 漢漢 (あ]
+443: pop
+level 1:(h=0,v=12625811,w=0,x=0,y=798030,z=798031,hh=0,vv=800)
+444: y0 798030 v:=12625811+798030=13423841, vv:=850
+445: push
+level 1:(h=0,v=13423841,w=0,x=0,y=798030,z=798031,hh=0,vv=850)
+446: set2 12354("3042) type=0 h:=0+630598=630598, hh:=40
+449: set2 65289("FF09) type=1 h:=630598+330310=960908, hh:=61
+452: w3 300288 h:=960908+300288=1261196, hh:=80
+456: set2 28450("6F22) type=0 h:=1261196+630598=1891794, hh:=120
+459: w0 300288 h:=1891794+300288=2192082, hh:=139
+460: set2 65288("FF08) type=6 h:=2192082+330310=2522392, hh:=160
+463: set2 12354("3042) type=0 h:=2522392+630598=3152990, hh:=200
+[あ) 漢 (あ]
+466: pop
+level 1:(h=0,v=13423841,w=0,x=0,y=798030,z=798031,hh=0,vv=850)
+467: y0 798030 v:=13423841+798030=14221871, vv:=901
+468: push
+level 1:(h=0,v=14221871,w=0,x=0,y=798030,z=798031,hh=0,vv=901)
+469: set2 9632("25A0) type=0 h:=0+630598=630598, hh:=40
+[■]
+472: fntnum1 current font is cmtt10
+473: setchar92 ("5C) h:=630598+344061=974659, hh:=62
+474: setchar118 ("76) h:=974659+344061=1318720, hh:=84
+475: setchar114 ("72) h:=1318720+344061=1662781, hh:=106
+476: setchar117 ("75) h:=1662781+344061=2006842, hh:=128
+477: setchar108 ("6C) h:=2006842+344061=2350903, hh:=150
+478: setchar101 ("65) h:=2350903+344061=2694964, hh:=172
+[\vrule]
+479: pop
+level 1:(h=0,v=14221871,w=0,x=0,y=798030,z=798031,hh=0,vv=901)
+480: y0 798030 v:=14221871+798030=15019901, vv:=951
+481: push
+level 1:(h=0,v=15019901,w=0,x=0,y=798030,z=798031,hh=0,vv=951)
+482: fntnum2 current font is umin10
+483: set2 12354("3042) type=0 h:=0+630598=630598, hh:=40
+486: set2 65289("FF09) type=1 h:=630598+330310=960908, hh:=61
+489: set2 65288("FF08) type=6 h:=960908+330310=1291218, hh:=82
+492: set2 12354("3042) type=0 h:=1291218+630598=1921816, hh:=122
+[あ)(あ]
+495: pop
+level 1:(h=0,v=15019901,w=0,x=0,y=798030,z=798031,hh=0,vv=951)
+496: y0 798030 v:=15019901+798030=15817931, vv:=1002
+497: push
+level 1:(h=0,v=15817931,w=0,x=0,y=798030,z=798031,hh=0,vv=1002)
+498: set2 12354("3042) type=0 h:=0+630598=630598, hh:=40
+501: set2 65289("FF09) type=1 h:=630598+330310=960908, hh:=61
+504: right3 300288 h:=960908+300288=1261196, hh:=80
+[あ) ]
+508: down3 91000 v:=15817931+91000=15908931, vv:=1008
+512: setrule height 600600, width 26214 (39x2 pixels)
+ h:=1261196+26214=1287410, hh:=82
+521: down3 -91000 v:=15908931-91000=15817931, vv:=1002
+525: set2 65288("FF08) type=6 h:=1287410+330310=1617720, hh:=103
+528: set2 12354("3042) type=0 h:=1617720+630598=2248318, hh:=143
+[(あ]
+531: pop
+level 1:(h=0,v=15817931,w=0,x=0,y=798030,z=798031,hh=0,vv=1002)
+532: y0 798030 v:=15817931+798030=16615961, vv:=1052
+533: push
+level 1:(h=0,v=16615961,w=0,x=0,y=798030,z=798031,hh=0,vv=1052)
+534: set2 12354("3042) type=0 h:=0+630598=630598, hh:=40
+537: set2 65289("FF09) type=1 h:=630598+330310=960908, hh:=61
+[あ)]
+540: down3 91000 v:=16615961+91000=16706961, vv:=1058
+544: setrule height 600600, width 26214 (39x2 pixels)
+ h:=960908+26214=987122, hh:=63
+553: right3 300288 h:=987122+300288=1287410, hh:=82
+[ ]
+557: down3 -91000 v:=16706961-91000=16615961, vv:=1052
+561: set2 65288("FF08) type=6 h:=1287410+330310=1617720, hh:=103
+564: set2 12354("3042) type=0 h:=1617720+630598=2248318, hh:=143
+[(あ]
+567: pop
+level 1:(h=0,v=16615961,w=0,x=0,y=798030,z=798031,hh=0,vv=1052)
+568: y0 798030 v:=16615961+798030=17413991, vv:=1103
+569: push
+level 1:(h=0,v=17413991,w=0,x=0,y=798030,z=798031,hh=0,vv=1103)
+570: set2 12354("3042) type=0 h:=0+630598=630598, hh:=40
+573: set2 65289("FF09) type=1 h:=630598+330310=960908, hh:=61
+576: w3 300288 h:=960908+300288=1261196, hh:=80
+[あ) ]
+580: down3 91000 v:=17413991+91000=17504991, vv:=1109
+584: setrule height 600600, width 26214 (39x2 pixels)
+ h:=1261196+26214=1287410, hh:=82
+593: setrule height 600600, width 26214 (39x2 pixels)
+ h:=1287410+26214=1313624, hh:=84
+602: w0 300288 h:=1313624+300288=1613912, hh:=102
+[ ]
+603: down3 -91000 v:=17504991-91000=17413991, vv:=1103
+607: set2 65288("FF08) type=6 h:=1613912+330310=1944222, hh:=123
+610: set2 12354("3042) type=0 h:=1944222+630598=2574820, hh:=163
+[(あ]
+613: pop
+level 1:(h=0,v=17413991,w=0,x=0,y=798030,z=798031,hh=0,vv=1103)
+614: y0 798030 v:=17413991+798030=18212021, vv:=1154
+615: push
+level 1:(h=0,v=18212021,w=0,x=0,y=798030,z=798031,hh=0,vv=1154)
+616: set2 12354("3042) type=0 h:=0+630598=630598, hh:=40
+619: set2 65289("FF09) type=1 h:=630598+330310=960908, hh:=61
+622: w3 300288 h:=960908+300288=1261196, hh:=80
+[あ) ]
+626: down3 91000 v:=18212021+91000=18303021, vv:=1160
+630: setrule height 600600, width 26214 (39x2 pixels)
+ h:=1261196+26214=1287410, hh:=82
+639: w0 300288 h:=1287410+300288=1587698, hh:=101
+[ ]
+640: down3 -91000 v:=18303021-91000=18212021, vv:=1154
+644: set2 65288("FF08) type=6 h:=1587698+330310=1918008, hh:=122
+647: set2 12354("3042) type=0 h:=1918008+630598=2548606, hh:=162
+[(あ]
+650: pop
+level 1:(h=0,v=18212021,w=0,x=0,y=798030,z=798031,hh=0,vv=1154)
+651: y0 798030 v:=18212021+798030=19010051, vv:=1204
+652: push
+level 1:(h=0,v=19010051,w=0,x=0,y=798030,z=798031,hh=0,vv=1204)
+653: set2 9632("25A0) type=0 h:=0+630598=630598, hh:=40
+[■]
+656: fntnum1 current font is cmtt10
+657: setchar92 ("5C) h:=630598+344061=974659, hh:=62
+658: setchar100 ("64) h:=974659+344061=1318720, hh:=84
+659: setchar105 ("69) h:=1318720+344061=1662781, hh:=106
+660: setchar115 ("73) h:=1662781+344061=2006842, hh:=128
+661: setchar99 ("63) h:=2006842+344061=2350903, hh:=150
+662: setchar114 ("72) h:=2350903+344061=2694964, hh:=172
+663: setchar101 ("65) h:=2694964+344061=3039025, hh:=194
+664: setchar116 ("74) h:=3039025+344061=3383086, hh:=216
+665: setchar105 ("69) h:=3383086+344061=3727147, hh:=238
+666: setchar111 ("6F) h:=3727147+344061=4071208, hh:=260
+667: setchar110 ("6E) h:=4071208+344061=4415269, hh:=282
+668: setchar97 ("61) h:=4415269+344061=4759330, hh:=303
+669: setchar114 ("72) h:=4759330+344061=5103391, hh:=325
+670: setchar121 ("79) h:=5103391+344061=5447452, hh:=347
+[\discretionary]
+671: pop
+level 1:(h=0,v=19010051,w=0,x=0,y=798030,z=798031,hh=0,vv=1204)
+672: y0 798030 v:=19010051+798030=19808081, vv:=1255
+673: push
+level 1:(h=0,v=19808081,w=0,x=0,y=798030,z=798031,hh=0,vv=1255)
+674: fntnum2 current font is umin10
+675: set2 12354("3042) type=0 h:=0+630598=630598, hh:=40
+678: set2 65289("FF09) type=1 h:=630598+330310=960908, hh:=61
+681: set2 65288("FF08) type=6 h:=960908+330310=1291218, hh:=82
+684: set2 12354("3042) type=0 h:=1291218+630598=1921816, hh:=122
+[あ)(あ]
+687: pop
+level 1:(h=0,v=19808081,w=0,x=0,y=798030,z=798031,hh=0,vv=1255)
+688: y0 798030 v:=19808081+798030=20606111, vv:=1305
+689: push
+level 1:(h=0,v=20606111,w=0,x=0,y=798030,z=798031,hh=0,vv=1305)
+690: set2 12354("3042) type=0 h:=0+630598=630598, hh:=40
+693: set2 65289("FF09) type=1 h:=630598+330310=960908, hh:=61
+696: right3 300288 h:=960908+300288=1261196, hh:=80
+700: set2 65288("FF08) type=6 h:=1261196+330310=1591506, hh:=101
+703: set2 12354("3042) type=0 h:=1591506+630598=2222104, hh:=141
+[あ) (あ]
+706: pop
+level 1:(h=0,v=20606111,w=0,x=0,y=798030,z=798031,hh=0,vv=1305)
+707: y0 798030 v:=20606111+798030=21404141, vv:=1356
+708: push
+level 1:(h=0,v=21404141,w=0,x=0,y=798030,z=798031,hh=0,vv=1356)
+709: set2 12354("3042) type=0 h:=0+630598=630598, hh:=40
+712: set2 65289("FF09) type=1 h:=630598+330310=960908, hh:=61
+715: right3 300288 h:=960908+300288=1261196, hh:=80
+719: set2 65288("FF08) type=6 h:=1261196+330310=1591506, hh:=101
+722: set2 12354("3042) type=0 h:=1591506+630598=2222104, hh:=141
+[あ) (あ]
+725: pop
+level 1:(h=0,v=21404141,w=0,x=0,y=798030,z=798031,hh=0,vv=1356)
+726: y0 798030 v:=21404141+798030=22202171, vv:=1406
+727: push
+level 1:(h=0,v=22202171,w=0,x=0,y=798030,z=798031,hh=0,vv=1406)
+728: set2 12354("3042) type=0 h:=0+630598=630598, hh:=40
+731: set2 65289("FF09) type=1 h:=630598+330310=960908, hh:=61
+734: right3 600576 h:=960908+600576=1561484, hh:=99
+738: set2 65288("FF08) type=6 h:=1561484+330310=1891794, hh:=120
+741: set2 12354("3042) type=0 h:=1891794+630598=2522392, hh:=160
+[あ) (あ]
+744: pop
+level 1:(h=0,v=22202171,w=0,x=0,y=798030,z=798031,hh=0,vv=1406)
+745: y0 798030 v:=22202171+798030=23000201, vv:=1457
+746: push
+level 1:(h=0,v=23000201,w=0,x=0,y=798030,z=798031,hh=0,vv=1457)
+747: set2 12354("3042) type=0 h:=0+630598=630598, hh:=40
+750: set2 65289("FF09) type=1 h:=630598+330310=960908, hh:=61
+753: right3 600576 h:=960908+600576=1561484, hh:=99
+757: set2 65288("FF08) type=6 h:=1561484+330310=1891794, hh:=120
+760: set2 12354("3042) type=0 h:=1891794+630598=2522392, hh:=160
+[あ) (あ]
+763: pop
+level 1:(h=0,v=23000201,w=0,x=0,y=798030,z=798031,hh=0,vv=1457)
+764: y0 798030 v:=23000201+798030=23798231, vv:=1507
+765: push
+level 1:(h=0,v=23798231,w=0,x=0,y=798030,z=798031,hh=0,vv=1507)
+766: set2 9632("25A0) type=0 h:=0+630598=630598, hh:=40
+[■]
+769: fntnum1 current font is cmtt10
+770: setchar92 ("5C) h:=630598+344061=974659, hh:=62
+771: setchar115 ("73) h:=974659+344061=1318720, hh:=84
+772: setchar112 ("70) h:=1318720+344061=1662781, hh:=106
+773: setchar101 ("65) h:=1662781+344061=2006842, hh:=128
+774: setchar99 ("63) h:=2006842+344061=2350903, hh:=150
+775: setchar105 ("69) h:=2350903+344061=2694964, hh:=172
+776: setchar97 ("61) h:=2694964+344061=3039025, hh:=194
+777: setchar108 ("6C) h:=3039025+344061=3383086, hh:=216
+[\special]
+778: pop
+level 1:(h=0,v=23798231,w=0,x=0,y=798030,z=798031,hh=0,vv=1507)
+779: y0 798030 v:=23798231+798030=24596261, vv:=1558
+780: push
+level 1:(h=0,v=24596261,w=0,x=0,y=798030,z=798031,hh=0,vv=1558)
+781: fntnum2 current font is umin10
+782: set2 12354("3042) type=0 h:=0+630598=630598, hh:=40
+785: set2 65289("FF09) type=1 h:=630598+330310=960908, hh:=61
+788: set2 65288("FF08) type=6 h:=960908+330310=1291218, hh:=82
+791: set2 12354("3042) type=0 h:=1291218+630598=1921816, hh:=122
+[あ)(あ]
+794: pop
+level 1:(h=0,v=24596261,w=0,x=0,y=798030,z=798031,hh=0,vv=1558)
+795: y0 798030 v:=24596261+798030=25394291, vv:=1608
+796: push
+level 1:(h=0,v=25394291,w=0,x=0,y=798030,z=798031,hh=0,vv=1608)
+797: set2 12354("3042) type=0 h:=0+630598=630598, hh:=40
+800: set2 65289("FF09) type=1 h:=630598+330310=960908, hh:=61
+803: right3 300288 h:=960908+300288=1261196, hh:=80
+[あ) ]
+807: xxx ''
+809: set2 65288("FF08) type=6 h:=1261196+330310=1591506, hh:=101
+812: set2 12354("3042) type=0 h:=1591506+630598=2222104, hh:=141
+[(あ]
+815: pop
+level 1:(h=0,v=25394291,w=0,x=0,y=798030,z=798031,hh=0,vv=1608)
+816: y0 798030 v:=25394291+798030=26192321, vv:=1659
+817: push
+level 1:(h=0,v=26192321,w=0,x=0,y=798030,z=798031,hh=0,vv=1659)
+818: set2 12354("3042) type=0 h:=0+630598=630598, hh:=40
+821: set2 65289("FF09) type=1 h:=630598+330310=960908, hh:=61
+[あ)]
+824: xxx ''
+826: right3 300288 h:=960908+300288=1261196, hh:=80
+830: set2 65288("FF08) type=6 h:=1261196+330310=1591506, hh:=101
+833: set2 12354("3042) type=0 h:=1591506+630598=2222104, hh:=141
+[ (あ]
+836: pop
+level 1:(h=0,v=26192321,w=0,x=0,y=798030,z=798031,hh=0,vv=1659)
+837: y0 798030 v:=26192321+798030=26990351, vv:=1710
+838: push
+level 1:(h=0,v=26990351,w=0,x=0,y=798030,z=798031,hh=0,vv=1710)
+839: set2 12354("3042) type=0 h:=0+630598=630598, hh:=40
+842: set2 65289("FF09) type=1 h:=630598+330310=960908, hh:=61
+845: w3 300288 h:=960908+300288=1261196, hh:=80
+[あ) ]
+849: xxx ''
+851: xxx ''
+853: w0 300288 h:=1261196+300288=1561484, hh:=99
+854: set2 65288("FF08) type=6 h:=1561484+330310=1891794, hh:=120
+857: set2 12354("3042) type=0 h:=1891794+630598=2522392, hh:=160
+[ (あ]
+860: pop
+level 1:(h=0,v=26990351,w=0,x=0,y=798030,z=798031,hh=0,vv=1710)
+861: y0 798030 v:=26990351+798030=27788381, vv:=1760
+862: push
+level 1:(h=0,v=27788381,w=0,x=0,y=798030,z=798031,hh=0,vv=1760)
+863: set2 12354("3042) type=0 h:=0+630598=630598, hh:=40
+866: set2 65289("FF09) type=1 h:=630598+330310=960908, hh:=61
+869: w3 300288 h:=960908+300288=1261196, hh:=80
+[あ) ]
+873: xxx ''
+875: w0 300288 h:=1261196+300288=1561484, hh:=99
+876: set2 65288("FF08) type=6 h:=1561484+330310=1891794, hh:=120
+879: set2 12354("3042) type=0 h:=1891794+630598=2522392, hh:=160
+[ (あ]
+882: pop
+level 1:(h=0,v=27788381,w=0,x=0,y=798030,z=798031,hh=0,vv=1760)
+883: y0 798030 v:=27788381+798030=28586411, vv:=1811
+884: push
+level 1:(h=0,v=28586411,w=0,x=0,y=798030,z=798031,hh=0,vv=1811)
+885: set2 9632("25A0) type=0 h:=0+630598=630598, hh:=40
+[■]
+888: fntnum1 current font is cmtt10
+889: setchar92 ("5C) h:=630598+344061=974659, hh:=62
+890: setchar119 ("77) h:=974659+344061=1318720, hh:=84
+891: setchar114 ("72) h:=1318720+344061=1662781, hh:=106
+892: setchar105 ("69) h:=1662781+344061=2006842, hh:=128
+893: setchar116 ("74) h:=2006842+344061=2350903, hh:=150
+894: setchar101 ("65) h:=2350903+344061=2694964, hh:=172
+895: setchar45 ("2D) h:=2694964+344061=3039025, hh:=194
+896: setchar49 ("31) h:=3039025+344061=3383086, hh:=216
+[\write-1]
+897: pop
+level 1:(h=0,v=28586411,w=0,x=0,y=798030,z=798031,hh=0,vv=1811)
+898: y0 798030 v:=28586411+798030=29384441, vv:=1861
+899: push
+level 1:(h=0,v=29384441,w=0,x=0,y=798030,z=798031,hh=0,vv=1861)
+900: fntnum2 current font is umin10
+901: set2 12354("3042) type=0 h:=0+630598=630598, hh:=40
+904: set2 65289("FF09) type=1 h:=630598+330310=960908, hh:=61
+907: set2 65288("FF08) type=6 h:=960908+330310=1291218, hh:=82
+910: set2 12354("3042) type=0 h:=1291218+630598=1921816, hh:=122
+[あ)(あ]
+913: pop
+level 1:(h=0,v=29384441,w=0,x=0,y=798030,z=798031,hh=0,vv=1861)
+914: y0 798030 v:=29384441+798030=30182471, vv:=1912
+915: push
+level 1:(h=0,v=30182471,w=0,x=0,y=798030,z=798031,hh=0,vv=1912)
+916: set2 12354("3042) type=0 h:=0+630598=630598, hh:=40
+919: set2 65289("FF09) type=1 h:=630598+330310=960908, hh:=61
+922: right3 300288 h:=960908+300288=1261196, hh:=80
+926: set2 65288("FF08) type=6 h:=1261196+330310=1591506, hh:=101
+929: set2 12354("3042) type=0 h:=1591506+630598=2222104, hh:=141
+[あ) (あ]
+932: pop
+level 1:(h=0,v=30182471,w=0,x=0,y=798030,z=798031,hh=0,vv=1912)
+933: y0 798030 v:=30182471+798030=30980501, vv:=1962
+934: push
+level 1:(h=0,v=30980501,w=0,x=0,y=798030,z=798031,hh=0,vv=1962)
+935: set2 12354("3042) type=0 h:=0+630598=630598, hh:=40
+938: set2 65289("FF09) type=1 h:=630598+330310=960908, hh:=61
+941: right3 300288 h:=960908+300288=1261196, hh:=80
+945: set2 65288("FF08) type=6 h:=1261196+330310=1591506, hh:=101
+948: set2 12354("3042) type=0 h:=1591506+630598=2222104, hh:=141
+[あ) (あ]
+951: pop
+level 1:(h=0,v=30980501,w=0,x=0,y=798030,z=798031,hh=0,vv=1962)
+952: z0 798031 v:=30980501+798031=31778532, vv:=2013
+953: push
+level 1:(h=0,v=31778532,w=0,x=0,y=798030,z=798031,hh=0,vv=2013)
+954: set2 12354("3042) type=0 h:=0+630598=630598, hh:=40
+957: set2 65289("FF09) type=1 h:=630598+330310=960908, hh:=61
+960: right3 600576 h:=960908+600576=1561484, hh:=99
+964: set2 65288("FF08) type=6 h:=1561484+330310=1891794, hh:=120
+967: set2 12354("3042) type=0 h:=1891794+630598=2522392, hh:=160
+[あ) (あ]
+970: pop
+level 1:(h=0,v=31778532,w=0,x=0,y=798030,z=798031,hh=0,vv=2013)
+971: y0 798030 v:=31778532+798030=32576562, vv:=2063
+972: push
+level 1:(h=0,v=32576562,w=0,x=0,y=798030,z=798031,hh=0,vv=2063)
+973: set2 12354("3042) type=0 h:=0+630598=630598, hh:=40
+976: set2 65289("FF09) type=1 h:=630598+330310=960908, hh:=61
+979: right3 600576 h:=960908+600576=1561484, hh:=99
+983: set2 65288("FF08) type=6 h:=1561484+330310=1891794, hh:=120
+986: set2 12354("3042) type=0 h:=1891794+630598=2522392, hh:=160
+[あ) (あ]
+989: pop
+level 1:(h=0,v=32576562,w=0,x=0,y=798030,z=798031,hh=0,vv=2063)
+990: y0 798030 v:=32576562+798030=33374592, vv:=2114
+991: push
+level 1:(h=0,v=33374592,w=0,x=0,y=798030,z=798031,hh=0,vv=2114)
+992: set2 9632("25A0) type=0 h:=0+630598=630598, hh:=40
+[■]
+995: fntnum1 current font is cmtt10
+996: setchar92 ("5C) h:=630598+344061=974659, hh:=62
+997: setchar105 ("69) h:=974659+344061=1318720, hh:=84
+998: setchar110 ("6E) h:=1318720+344061=1662781, hh:=106
+999: setchar115 ("73) h:=1662781+344061=2006842, hh:=128
+1000: setchar101 ("65) h:=2006842+344061=2350903, hh:=150
+1001: setchar114 ("72) h:=2350903+344061=2694964, hh:=172
+1002: setchar116 ("74) h:=2694964+344061=3039025, hh:=194
+[\insert]
+1003: pop
+level 1:(h=0,v=33374592,w=0,x=0,y=798030,z=798031,hh=0,vv=2114)
+1004: y0 798030 v:=33374592+798030=34172622, vv:=2165
+1005: push
+level 1:(h=0,v=34172622,w=0,x=0,y=798030,z=798031,hh=0,vv=2165)
+1006: fntnum2 current font is umin10
+1007: set2 12354("3042) type=0 h:=0+630598=630598, hh:=40
+1010: set2 65289("FF09) type=1 h:=630598+330310=960908, hh:=61
+1013: set2 65288("FF08) type=6 h:=960908+330310=1291218, hh:=82
+1016: set2 12354("3042) type=0 h:=1291218+630598=1921816, hh:=122
+[あ)(あ]
+1019: pop
+level 1:(h=0,v=34172622,w=0,x=0,y=798030,z=798031,hh=0,vv=2165)
+1020: y0 798030 v:=34172622+798030=34970652, vv:=2215
+1021: push
+level 1:(h=0,v=34970652,w=0,x=0,y=798030,z=798031,hh=0,vv=2215)
+1022: set2 12354("3042) type=0 h:=0+630598=630598, hh:=40
+1025: set2 65289("FF09) type=1 h:=630598+330310=960908, hh:=61
+1028: right3 300288 h:=960908+300288=1261196, hh:=80
+1032: set2 65288("FF08) type=6 h:=1261196+330310=1591506, hh:=101
+1035: set2 12354("3042) type=0 h:=1591506+630598=2222104, hh:=141
+[あ) (あ]
+1038: pop
+level 1:(h=0,v=34970652,w=0,x=0,y=798030,z=798031,hh=0,vv=2215)
+1039: y0 798030 v:=34970652+798030=35768682, vv:=2266
+1040: push
+level 1:(h=0,v=35768682,w=0,x=0,y=798030,z=798031,hh=0,vv=2266)
+1041: set2 12354("3042) type=0 h:=0+630598=630598, hh:=40
+1044: set2 65289("FF09) type=1 h:=630598+330310=960908, hh:=61
+1047: right3 300288 h:=960908+300288=1261196, hh:=80
+1051: set2 65288("FF08) type=6 h:=1261196+330310=1591506, hh:=101
+1054: set2 12354("3042) type=0 h:=1591506+630598=2222104, hh:=141
+[あ) (あ]
+1057: pop
+level 1:(h=0,v=35768682,w=0,x=0,y=798030,z=798031,hh=0,vv=2266)
+1058: y0 798030 v:=35768682+798030=36566712, vv:=2316
+1059: push
+level 1:(h=0,v=36566712,w=0,x=0,y=798030,z=798031,hh=0,vv=2316)
+1060: set2 12354("3042) type=0 h:=0+630598=630598, hh:=40
+1063: set2 65289("FF09) type=1 h:=630598+330310=960908, hh:=61
+1066: right3 600576 h:=960908+600576=1561484, hh:=99
+1070: set2 65288("FF08) type=6 h:=1561484+330310=1891794, hh:=120
+1073: set2 12354("3042) type=0 h:=1891794+630598=2522392, hh:=160
+[あ) (あ]
+1076: pop
+level 1:(h=0,v=36566712,w=0,x=0,y=798030,z=798031,hh=0,vv=2316)
+1077: y0 798030 v:=36566712+798030=37364742, vv:=2367
+1078: push
+level 1:(h=0,v=37364742,w=0,x=0,y=798030,z=798031,hh=0,vv=2367)
+1079: set2 12354("3042) type=0 h:=0+630598=630598, hh:=40
+1082: set2 65289("FF09) type=1 h:=630598+330310=960908, hh:=61
+1085: right3 600576 h:=960908+600576=1561484, hh:=99
+1089: set2 65288("FF08) type=6 h:=1561484+330310=1891794, hh:=120
+1092: set2 12354("3042) type=0 h:=1891794+630598=2522392, hh:=160
+[あ) (あ]
+1095: pop
+level 1:(h=0,v=37364742,w=0,x=0,y=798030,z=798031,hh=0,vv=2367)
+1096: y0 798030 v:=37364742+798030=38162772, vv:=2417
+1097: push
+level 1:(h=0,v=38162772,w=0,x=0,y=798030,z=798031,hh=0,vv=2417)
+1098: set2 9632("25A0) type=0 h:=0+630598=630598, hh:=40
+[■]
+1101: fntnum1 current font is cmtt10
+1102: setchar92 ("5C) h:=630598+344061=974659, hh:=62
+1103: setchar109 ("6D) h:=974659+344061=1318720, hh:=84
+1104: setchar97 ("61) h:=1318720+344061=1662781, hh:=106
+1105: setchar114 ("72) h:=1662781+344061=2006842, hh:=128
+1106: setchar107 ("6B) h:=2006842+344061=2350903, hh:=150
+[\mark]
+1107: pop
+level 1:(h=0,v=38162772,w=0,x=0,y=798030,z=798031,hh=0,vv=2417)
+1108: y0 798030 v:=38162772+798030=38960802, vv:=2468
+1109: push
+level 1:(h=0,v=38960802,w=0,x=0,y=798030,z=798031,hh=0,vv=2468)
+1110: fntnum2 current font is umin10
+1111: set2 12354("3042) type=0 h:=0+630598=630598, hh:=40
+1114: set2 65289("FF09) type=1 h:=630598+330310=960908, hh:=61
+1117: set2 65288("FF08) type=6 h:=960908+330310=1291218, hh:=82
+1120: set2 12354("3042) type=0 h:=1291218+630598=1921816, hh:=122
+[あ)(あ]
+1123: pop
+level 1:(h=0,v=38960802,w=0,x=0,y=798030,z=798031,hh=0,vv=2468)
+1124: y0 798030 v:=38960802+798030=39758832, vv:=2518
+1125: push
+level 1:(h=0,v=39758832,w=0,x=0,y=798030,z=798031,hh=0,vv=2518)
+1126: set2 12354("3042) type=0 h:=0+630598=630598, hh:=40
+1129: set2 65289("FF09) type=1 h:=630598+330310=960908, hh:=61
+1132: right3 300288 h:=960908+300288=1261196, hh:=80
+1136: set2 65288("FF08) type=6 h:=1261196+330310=1591506, hh:=101
+1139: set2 12354("3042) type=0 h:=1591506+630598=2222104, hh:=141
+[あ) (あ]
+1142: pop
+level 1:(h=0,v=39758832,w=0,x=0,y=798030,z=798031,hh=0,vv=2518)
+1143: y0 798030 v:=39758832+798030=40556862, vv:=2569
+1144: push
+level 1:(h=0,v=40556862,w=0,x=0,y=798030,z=798031,hh=0,vv=2569)
+1145: set2 12354("3042) type=0 h:=0+630598=630598, hh:=40
+1148: set2 65289("FF09) type=1 h:=630598+330310=960908, hh:=61
+1151: right3 300288 h:=960908+300288=1261196, hh:=80
+1155: set2 65288("FF08) type=6 h:=1261196+330310=1591506, hh:=101
+1158: set2 12354("3042) type=0 h:=1591506+630598=2222104, hh:=141
+[あ) (あ]
+1161: pop
+level 1:(h=0,v=40556862,w=0,x=0,y=798030,z=798031,hh=0,vv=2569)
+1162: y0 798030 v:=40556862+798030=41354892, vv:=2619
+1163: push
+level 1:(h=0,v=41354892,w=0,x=0,y=798030,z=798031,hh=0,vv=2619)
+1164: set2 12354("3042) type=0 h:=0+630598=630598, hh:=40
+1167: set2 65289("FF09) type=1 h:=630598+330310=960908, hh:=61
+1170: right3 600576 h:=960908+600576=1561484, hh:=99
+1174: set2 65288("FF08) type=6 h:=1561484+330310=1891794, hh:=120
+1177: set2 12354("3042) type=0 h:=1891794+630598=2522392, hh:=160
+[あ) (あ]
+1180: pop
+level 1:(h=0,v=41354892,w=0,x=0,y=798030,z=798031,hh=0,vv=2619)
+1181: y0 798030 v:=41354892+798030=42152922, vv:=2670
+1182: push
+level 1:(h=0,v=42152922,w=0,x=0,y=798030,z=798031,hh=0,vv=2670)
+1183: set2 12354("3042) type=0 h:=0+630598=630598, hh:=40
+1186: set2 65289("FF09) type=1 h:=630598+330310=960908, hh:=61
+1189: right3 600576 h:=960908+600576=1561484, hh:=99
+1193: set2 65288("FF08) type=6 h:=1561484+330310=1891794, hh:=120
+1196: set2 12354("3042) type=0 h:=1891794+630598=2522392, hh:=160
+[あ) (あ]
+1199: pop
+level 1:(h=0,v=42152922,w=0,x=0,y=798030,z=798031,hh=0,vv=2670)
+1200: pop
+level 0:(h=0,v=42152922,w=0,x=0,y=0,z=0,hh=0,vv=2670)
+1201: down3 1572864 v:=42152922+1572864=43725786, vv:=2770
+1205: eop
+
+1206: beginning of page 2
+1251: push
+level 0:(h=0,v=0,w=0,x=0,y=0,z=0,hh=0,vv=0)
+1252: down3 -917504 v:=0-917504=-917504, vv:=-58
+1256: pop
+level 0:(h=0,v=0,w=0,x=0,y=0,z=0,hh=0,vv=0)
+1257: down4 42152922 v:=0+42152922=42152922, vv:=2670
+1262: push
+level 0:(h=0,v=42152922,w=0,x=0,y=0,z=0,hh=0,vv=2670)
+1263: down4 -41497562 v:=42152922-41497562=655360, vv:=42
+1268: push
+level 1:(h=0,v=655360,w=0,x=0,y=0,z=0,hh=0,vv=42)
+1269: fntnum2 current font is umin10
+1270: set2 9632("25A0) type=0 h:=0+630598=630598, hh:=40
+[■]
+1273: fntnum1 current font is cmtt10
+1274: setchar92 ("5C) h:=630598+344061=974659, hh:=62
+1275: setchar118 ("76) h:=974659+344061=1318720, hh:=84
+1276: setchar97 ("61) h:=1318720+344061=1662781, hh:=106
+1277: setchar100 ("64) h:=1662781+344061=2006842, hh:=128
+1278: setchar106 ("6A) h:=2006842+344061=2350903, hh:=150
+1279: setchar117 ("75) h:=2350903+344061=2694964, hh:=172
+1280: setchar115 ("73) h:=2694964+344061=3039025, hh:=194
+1281: setchar116 ("74) h:=3039025+344061=3383086, hh:=216
+[\vadjust]
+1282: pop
+level 1:(h=0,v=655360,w=0,x=0,y=0,z=0,hh=0,vv=42)
+1283: y3 798030 v:=655360+798030=1453390, vv:=92
+1287: push
+level 1:(h=0,v=1453390,w=0,x=0,y=798030,z=0,hh=0,vv=92)
+1288: fntnum2 current font is umin10
+1289: set2 12354("3042) type=0 h:=0+630598=630598, hh:=40
+1292: set2 65289("FF09) type=1 h:=630598+330310=960908, hh:=61
+1295: set2 65288("FF08) type=6 h:=960908+330310=1291218, hh:=82
+1298: set2 12354("3042) type=0 h:=1291218+630598=1921816, hh:=122
+[あ)(あ]
+1301: pop
+level 1:(h=0,v=1453390,w=0,x=0,y=798030,z=0,hh=0,vv=92)
+1302: y0 798030 v:=1453390+798030=2251420, vv:=143
+1303: push
+level 1:(h=0,v=2251420,w=0,x=0,y=798030,z=0,hh=0,vv=143)
+1304: set2 12354("3042) type=0 h:=0+630598=630598, hh:=40
+1307: set2 65289("FF09) type=1 h:=630598+330310=960908, hh:=61
+1310: right3 300288 h:=960908+300288=1261196, hh:=80
+1314: set2 65288("FF08) type=6 h:=1261196+330310=1591506, hh:=101
+1317: set2 12354("3042) type=0 h:=1591506+630598=2222104, hh:=141
+[あ) (あ]
+1320: pop
+level 1:(h=0,v=2251420,w=0,x=0,y=798030,z=0,hh=0,vv=143)
+1321: y0 798030 v:=2251420+798030=3049450, vv:=193
+1322: push
+level 1:(h=0,v=3049450,w=0,x=0,y=798030,z=0,hh=0,vv=193)
+1323: set2 12354("3042) type=0 h:=0+630598=630598, hh:=40
+1326: set2 65289("FF09) type=1 h:=630598+330310=960908, hh:=61
+1329: right3 300288 h:=960908+300288=1261196, hh:=80
+1333: set2 65288("FF08) type=6 h:=1261196+330310=1591506, hh:=101
+1336: set2 12354("3042) type=0 h:=1591506+630598=2222104, hh:=141
+[あ) (あ]
+1339: pop
+level 1:(h=0,v=3049450,w=0,x=0,y=798030,z=0,hh=0,vv=193)
+1340: y0 798030 v:=3049450+798030=3847480, vv:=244
+1341: push
+level 1:(h=0,v=3847480,w=0,x=0,y=798030,z=0,hh=0,vv=244)
+1342: set2 12354("3042) type=0 h:=0+630598=630598, hh:=40
+1345: set2 65289("FF09) type=1 h:=630598+330310=960908, hh:=61
+1348: right3 600576 h:=960908+600576=1561484, hh:=99
+1352: set2 65288("FF08) type=6 h:=1561484+330310=1891794, hh:=120
+1355: set2 12354("3042) type=0 h:=1891794+630598=2522392, hh:=160
+[あ) (あ]
+1358: pop
+level 1:(h=0,v=3847480,w=0,x=0,y=798030,z=0,hh=0,vv=244)
+1359: y0 798030 v:=3847480+798030=4645510, vv:=294
+1360: push
+level 1:(h=0,v=4645510,w=0,x=0,y=798030,z=0,hh=0,vv=294)
+1361: set2 12354("3042) type=0 h:=0+630598=630598, hh:=40
+1364: set2 65289("FF09) type=1 h:=630598+330310=960908, hh:=61
+1367: right3 600576 h:=960908+600576=1561484, hh:=99
+1371: set2 65288("FF08) type=6 h:=1561484+330310=1891794, hh:=120
+1374: set2 12354("3042) type=0 h:=1891794+630598=2522392, hh:=160
+[あ) (あ]
+1377: pop
+level 1:(h=0,v=4645510,w=0,x=0,y=798030,z=0,hh=0,vv=294)
+1378: y0 798030 v:=4645510+798030=5443540, vv:=345
+1379: push
+level 1:(h=0,v=5443540,w=0,x=0,y=798030,z=0,hh=0,vv=345)
+1380: set2 9632("25A0) type=0 h:=0+630598=630598, hh:=40
+[■]
+1383: fntnum1 current font is cmtt10
+1384: setchar92 ("5C) h:=630598+344061=974659, hh:=62
+1385: setchar104 ("68) h:=974659+344061=1318720, hh:=84
+1386: setchar115 ("73) h:=1318720+344061=1662781, hh:=106
+1387: setchar107 ("6B) h:=1662781+344061=2006842, hh:=128
+1388: setchar105 ("69) h:=2006842+344061=2350903, hh:=150
+1389: setchar112 ("70) h:=2350903+344061=2694964, hh:=172
+1390: setchar48 ("30) h:=2694964+344061=3039025, hh:=194
+1391: setchar112 ("70) h:=3039025+344061=3383086, hh:=216
+1392: setchar116 ("74) h:=3383086+344061=3727147, hh:=238
+[\hskip0pt]
+1393: pop
+level 1:(h=0,v=5443540,w=0,x=0,y=798030,z=0,hh=0,vv=345)
+1394: y0 798030 v:=5443540+798030=6241570, vv:=395
+1395: push
+level 1:(h=0,v=6241570,w=0,x=0,y=798030,z=0,hh=0,vv=395)
+1396: fntnum2 current font is umin10
+1397: set2 12354("3042) type=0 h:=0+630598=630598, hh:=40
+1400: set2 65289("FF09) type=1 h:=630598+330310=960908, hh:=61
+1403: set2 65288("FF08) type=6 h:=960908+330310=1291218, hh:=82
+1406: set2 12354("3042) type=0 h:=1291218+630598=1921816, hh:=122
+[あ)(あ]
+1409: pop
+level 1:(h=0,v=6241570,w=0,x=0,y=798030,z=0,hh=0,vv=395)
+1410: y0 798030 v:=6241570+798030=7039600, vv:=446
+1411: push
+level 1:(h=0,v=7039600,w=0,x=0,y=798030,z=0,hh=0,vv=446)
+1412: set2 12354("3042) type=0 h:=0+630598=630598, hh:=40
+1415: set2 65289("FF09) type=1 h:=630598+330310=960908, hh:=61
+1418: right3 300288 h:=960908+300288=1261196, hh:=80
+1422: set2 65288("FF08) type=6 h:=1261196+330310=1591506, hh:=101
+1425: set2 12354("3042) type=0 h:=1591506+630598=2222104, hh:=141
+[あ) (あ]
+1428: pop
+level 1:(h=0,v=7039600,w=0,x=0,y=798030,z=0,hh=0,vv=446)
+1429: y0 798030 v:=7039600+798030=7837630, vv:=496
+1430: push
+level 1:(h=0,v=7837630,w=0,x=0,y=798030,z=0,hh=0,vv=496)
+1431: set2 12354("3042) type=0 h:=0+630598=630598, hh:=40
+1434: set2 65289("FF09) type=1 h:=630598+330310=960908, hh:=61
+1437: right3 300288 h:=960908+300288=1261196, hh:=80
+1441: set2 65288("FF08) type=6 h:=1261196+330310=1591506, hh:=101
+1444: set2 12354("3042) type=0 h:=1591506+630598=2222104, hh:=141
+[あ) (あ]
+1447: pop
+level 1:(h=0,v=7837630,w=0,x=0,y=798030,z=0,hh=0,vv=496)
+1448: y0 798030 v:=7837630+798030=8635660, vv:=547
+1449: push
+level 1:(h=0,v=8635660,w=0,x=0,y=798030,z=0,hh=0,vv=547)
+1450: set2 12354("3042) type=0 h:=0+630598=630598, hh:=40
+1453: set2 65289("FF09) type=1 h:=630598+330310=960908, hh:=61
+1456: right3 600576 h:=960908+600576=1561484, hh:=99
+1460: set2 65288("FF08) type=6 h:=1561484+330310=1891794, hh:=120
+1463: set2 12354("3042) type=0 h:=1891794+630598=2522392, hh:=160
+[あ) (あ]
+1466: pop
+level 1:(h=0,v=8635660,w=0,x=0,y=798030,z=0,hh=0,vv=547)
+1467: y0 798030 v:=8635660+798030=9433690, vv:=598
+1468: push
+level 1:(h=0,v=9433690,w=0,x=0,y=798030,z=0,hh=0,vv=598)
+1469: set2 12354("3042) type=0 h:=0+630598=630598, hh:=40
+1472: set2 65289("FF09) type=1 h:=630598+330310=960908, hh:=61
+1475: right3 600576 h:=960908+600576=1561484, hh:=99
+1479: set2 65288("FF08) type=6 h:=1561484+330310=1891794, hh:=120
+1482: set2 12354("3042) type=0 h:=1891794+630598=2522392, hh:=160
+[あ) (あ]
+1485: pop
+level 1:(h=0,v=9433690,w=0,x=0,y=798030,z=0,hh=0,vv=598)
+1486: y0 798030 v:=9433690+798030=10231720, vv:=648
+1487: push
+level 1:(h=0,v=10231720,w=0,x=0,y=798030,z=0,hh=0,vv=648)
+1488: set2 9632("25A0) type=0 h:=0+630598=630598, hh:=40
+[■]
+1491: fntnum1 current font is cmtt10
+1492: setchar92 ("5C) h:=630598+344061=974659, hh:=62
+1493: setchar107 ("6B) h:=974659+344061=1318720, hh:=84
+1494: setchar101 ("65) h:=1318720+344061=1662781, hh:=106
+1495: setchar114 ("72) h:=1662781+344061=2006842, hh:=128
+1496: setchar110 ("6E) h:=2006842+344061=2350903, hh:=150
+1497: setchar48 ("30) h:=2350903+344061=2694964, hh:=172
+1498: setchar112 ("70) h:=2694964+344061=3039025, hh:=194
+1499: setchar116 ("74) h:=3039025+344061=3383086, hh:=216
+[\kern0pt]
+1500: pop
+level 1:(h=0,v=10231720,w=0,x=0,y=798030,z=0,hh=0,vv=648)
+1501: z3 798031 v:=10231720+798031=11029751, vv:=699
+1505: push
+level 1:(h=0,v=11029751,w=0,x=0,y=798030,z=798031,hh=0,vv=699)
+1506: fntnum2 current font is umin10
+1507: set2 12354("3042) type=0 h:=0+630598=630598, hh:=40
+1510: set2 65289("FF09) type=1 h:=630598+330310=960908, hh:=61
+1513: set2 65288("FF08) type=6 h:=960908+330310=1291218, hh:=82
+1516: set2 12354("3042) type=0 h:=1291218+630598=1921816, hh:=122
+[あ)(あ]
+1519: pop
+level 1:(h=0,v=11029751,w=0,x=0,y=798030,z=798031,hh=0,vv=699)
+1520: y0 798030 v:=11029751+798030=11827781, vv:=749
+1521: push
+level 1:(h=0,v=11827781,w=0,x=0,y=798030,z=798031,hh=0,vv=749)
+1522: set2 12354("3042) type=0 h:=0+630598=630598, hh:=40
+1525: set2 65289("FF09) type=1 h:=630598+330310=960908, hh:=61
+1528: right3 300288 h:=960908+300288=1261196, hh:=80
+1532: set2 65288("FF08) type=6 h:=1261196+330310=1591506, hh:=101
+1535: set2 12354("3042) type=0 h:=1591506+630598=2222104, hh:=141
+[あ) (あ]
+1538: pop
+level 1:(h=0,v=11827781,w=0,x=0,y=798030,z=798031,hh=0,vv=749)
+1539: y0 798030 v:=11827781+798030=12625811, vv:=800
+1540: push
+level 1:(h=0,v=12625811,w=0,x=0,y=798030,z=798031,hh=0,vv=800)
+1541: set2 12354("3042) type=0 h:=0+630598=630598, hh:=40
+1544: set2 65289("FF09) type=1 h:=630598+330310=960908, hh:=61
+1547: right3 300288 h:=960908+300288=1261196, hh:=80
+1551: set2 65288("FF08) type=6 h:=1261196+330310=1591506, hh:=101
+1554: set2 12354("3042) type=0 h:=1591506+630598=2222104, hh:=141
+[あ) (あ]
+1557: pop
+level 1:(h=0,v=12625811,w=0,x=0,y=798030,z=798031,hh=0,vv=800)
+1558: y0 798030 v:=12625811+798030=13423841, vv:=850
+1559: push
+level 1:(h=0,v=13423841,w=0,x=0,y=798030,z=798031,hh=0,vv=850)
+1560: set2 12354("3042) type=0 h:=0+630598=630598, hh:=40
+1563: set2 65289("FF09) type=1 h:=630598+330310=960908, hh:=61
+1566: right3 600576 h:=960908+600576=1561484, hh:=99
+1570: set2 65288("FF08) type=6 h:=1561484+330310=1891794, hh:=120
+1573: set2 12354("3042) type=0 h:=1891794+630598=2522392, hh:=160
+[あ) (あ]
+1576: pop
+level 1:(h=0,v=13423841,w=0,x=0,y=798030,z=798031,hh=0,vv=850)
+1577: y0 798030 v:=13423841+798030=14221871, vv:=901
+1578: push
+level 1:(h=0,v=14221871,w=0,x=0,y=798030,z=798031,hh=0,vv=901)
+1579: set2 12354("3042) type=0 h:=0+630598=630598, hh:=40
+1582: set2 65289("FF09) type=1 h:=630598+330310=960908, hh:=61
+1585: right3 600576 h:=960908+600576=1561484, hh:=99
+1589: set2 65288("FF08) type=6 h:=1561484+330310=1891794, hh:=120
+1592: set2 12354("3042) type=0 h:=1891794+630598=2522392, hh:=160
+[あ) (あ]
+1595: pop
+level 1:(h=0,v=14221871,w=0,x=0,y=798030,z=798031,hh=0,vv=901)
+1596: y0 798030 v:=14221871+798030=15019901, vv:=951
+1597: push
+level 1:(h=0,v=15019901,w=0,x=0,y=798030,z=798031,hh=0,vv=951)
+1598: set2 9632("25A0) type=0 h:=0+630598=630598, hh:=40
+[■]
+1601: fntnum1 current font is cmtt10
+1602: setchar92 ("5C) h:=630598+344061=974659, hh:=62
+1603: setchar112 ("70) h:=974659+344061=1318720, hh:=84
+1604: setchar101 ("65) h:=1318720+344061=1662781, hh:=106
+1605: setchar110 ("6E) h:=1662781+344061=2006842, hh:=128
+1606: setchar97 ("61) h:=2006842+344061=2350903, hh:=150
+1607: setchar108 ("6C) h:=2350903+344061=2694964, hh:=172
+1608: setchar116 ("74) h:=2694964+344061=3039025, hh:=194
+1609: setchar121 ("79) h:=3039025+344061=3383086, hh:=216
+1610: setchar49 ("31) h:=3383086+344061=3727147, hh:=238
+1611: setchar48 ("30) h:=3727147+344061=4071208, hh:=260
+1612: setchar48 ("30) h:=4071208+344061=4415269, hh:=282
+1613: setchar48 ("30) h:=4415269+344061=4759330, hh:=303
+1614: setchar48 ("30) h:=4759330+344061=5103391, hh:=325
+[\penalty10000]
+1615: pop
+level 1:(h=0,v=15019901,w=0,x=0,y=798030,z=798031,hh=0,vv=951)
+1616: y0 798030 v:=15019901+798030=15817931, vv:=1002
+1617: push
+level 1:(h=0,v=15817931,w=0,x=0,y=798030,z=798031,hh=0,vv=1002)
+1618: fntnum2 current font is umin10
+1619: set2 12354("3042) type=0 h:=0+630598=630598, hh:=40
+1622: set2 65289("FF09) type=1 h:=630598+330310=960908, hh:=61
+1625: set2 65288("FF08) type=6 h:=960908+330310=1291218, hh:=82
+1628: set2 12354("3042) type=0 h:=1291218+630598=1921816, hh:=122
+[あ)(あ]
+1631: pop
+level 1:(h=0,v=15817931,w=0,x=0,y=798030,z=798031,hh=0,vv=1002)
+1632: y0 798030 v:=15817931+798030=16615961, vv:=1052
+1633: push
+level 1:(h=0,v=16615961,w=0,x=0,y=798030,z=798031,hh=0,vv=1052)
+1634: set2 12354("3042) type=0 h:=0+630598=630598, hh:=40
+1637: set2 65289("FF09) type=1 h:=630598+330310=960908, hh:=61
+1640: right3 300288 h:=960908+300288=1261196, hh:=80
+1644: set2 65288("FF08) type=6 h:=1261196+330310=1591506, hh:=101
+1647: set2 12354("3042) type=0 h:=1591506+630598=2222104, hh:=141
+[あ) (あ]
+1650: pop
+level 1:(h=0,v=16615961,w=0,x=0,y=798030,z=798031,hh=0,vv=1052)
+1651: y0 798030 v:=16615961+798030=17413991, vv:=1103
+1652: push
+level 1:(h=0,v=17413991,w=0,x=0,y=798030,z=798031,hh=0,vv=1103)
+1653: set2 12354("3042) type=0 h:=0+630598=630598, hh:=40
+1656: set2 65289("FF09) type=1 h:=630598+330310=960908, hh:=61
+1659: right3 300288 h:=960908+300288=1261196, hh:=80
+1663: set2 65288("FF08) type=6 h:=1261196+330310=1591506, hh:=101
+1666: set2 12354("3042) type=0 h:=1591506+630598=2222104, hh:=141
+[あ) (あ]
+1669: pop
+level 1:(h=0,v=17413991,w=0,x=0,y=798030,z=798031,hh=0,vv=1103)
+1670: y0 798030 v:=17413991+798030=18212021, vv:=1154
+1671: push
+level 1:(h=0,v=18212021,w=0,x=0,y=798030,z=798031,hh=0,vv=1154)
+1672: set2 12354("3042) type=0 h:=0+630598=630598, hh:=40
+1675: set2 65289("FF09) type=1 h:=630598+330310=960908, hh:=61
+1678: right3 600576 h:=960908+600576=1561484, hh:=99
+1682: set2 65288("FF08) type=6 h:=1561484+330310=1891794, hh:=120
+1685: set2 12354("3042) type=0 h:=1891794+630598=2522392, hh:=160
+[あ) (あ]
+1688: pop
+level 1:(h=0,v=18212021,w=0,x=0,y=798030,z=798031,hh=0,vv=1154)
+1689: y0 798030 v:=18212021+798030=19010051, vv:=1204
+1690: push
+level 1:(h=0,v=19010051,w=0,x=0,y=798030,z=798031,hh=0,vv=1204)
+1691: set2 12354("3042) type=0 h:=0+630598=630598, hh:=40
+1694: set2 65289("FF09) type=1 h:=630598+330310=960908, hh:=61
+1697: right3 600576 h:=960908+600576=1561484, hh:=99
+1701: set2 65288("FF08) type=6 h:=1561484+330310=1891794, hh:=120
+1704: set2 12354("3042) type=0 h:=1891794+630598=2522392, hh:=160
+[あ) (あ]
+1707: pop
+level 1:(h=0,v=19010051,w=0,x=0,y=798030,z=798031,hh=0,vv=1204)
+1708: y0 798030 v:=19010051+798030=19808081, vv:=1255
+1709: push
+level 1:(h=0,v=19808081,w=0,x=0,y=798030,z=798031,hh=0,vv=1255)
+1710: set2 9632("25A0) type=0 h:=0+630598=630598, hh:=40
+[■]
+1713: fntnum1 current font is cmtt10
+1714: setchar92 ("5C) h:=630598+344061=974659, hh:=62
+1715: setchar114 ("72) h:=974659+344061=1318720, hh:=84
+1716: setchar101 ("65) h:=1318720+344061=1662781, hh:=106
+1717: setchar108 ("6C) h:=1662781+344061=2006842, hh:=128
+1718: setchar97 ("61) h:=2006842+344061=2350903, hh:=150
+1719: setchar120 ("78) h:=2350903+344061=2694964, hh:=172
+[\relax]
+1720: pop
+level 1:(h=0,v=19808081,w=0,x=0,y=798030,z=798031,hh=0,vv=1255)
+1721: y0 798030 v:=19808081+798030=20606111, vv:=1305
+1722: push
+level 1:(h=0,v=20606111,w=0,x=0,y=798030,z=798031,hh=0,vv=1305)
+1723: fntnum2 current font is umin10
+1724: set2 12354("3042) type=0 h:=0+630598=630598, hh:=40
+1727: set2 65289("FF09) type=1 h:=630598+330310=960908, hh:=61
+1730: set2 65288("FF08) type=6 h:=960908+330310=1291218, hh:=82
+1733: set2 12354("3042) type=0 h:=1291218+630598=1921816, hh:=122
+[あ)(あ]
+1736: pop
+level 1:(h=0,v=20606111,w=0,x=0,y=798030,z=798031,hh=0,vv=1305)
+1737: y0 798030 v:=20606111+798030=21404141, vv:=1356
+1738: push
+level 1:(h=0,v=21404141,w=0,x=0,y=798030,z=798031,hh=0,vv=1356)
+1739: set2 12354("3042) type=0 h:=0+630598=630598, hh:=40
+1742: set2 65289("FF09) type=1 h:=630598+330310=960908, hh:=61
+1745: right3 300288 h:=960908+300288=1261196, hh:=80
+1749: set2 65288("FF08) type=6 h:=1261196+330310=1591506, hh:=101
+1752: set2 12354("3042) type=0 h:=1591506+630598=2222104, hh:=141
+[あ) (あ]
+1755: pop
+level 1:(h=0,v=21404141,w=0,x=0,y=798030,z=798031,hh=0,vv=1356)
+1756: y0 798030 v:=21404141+798030=22202171, vv:=1406
+1757: push
+level 1:(h=0,v=22202171,w=0,x=0,y=798030,z=798031,hh=0,vv=1406)
+1758: set2 12354("3042) type=0 h:=0+630598=630598, hh:=40
+1761: set2 65289("FF09) type=1 h:=630598+330310=960908, hh:=61
+1764: set2 65288("FF08) type=6 h:=960908+330310=1291218, hh:=82
+1767: set2 12354("3042) type=0 h:=1291218+630598=1921816, hh:=122
+[あ)(あ]
+1770: pop
+level 1:(h=0,v=22202171,w=0,x=0,y=798030,z=798031,hh=0,vv=1406)
+1771: y0 798030 v:=22202171+798030=23000201, vv:=1457
+1772: push
+level 1:(h=0,v=23000201,w=0,x=0,y=798030,z=798031,hh=0,vv=1457)
+1773: set2 12354("3042) type=0 h:=0+630598=630598, hh:=40
+1776: set2 65289("FF09) type=1 h:=630598+330310=960908, hh:=61
+1779: right3 300288 h:=960908+300288=1261196, hh:=80
+1783: set2 65288("FF08) type=6 h:=1261196+330310=1591506, hh:=101
+1786: set2 12354("3042) type=0 h:=1591506+630598=2222104, hh:=141
+[あ) (あ]
+1789: pop
+level 1:(h=0,v=23000201,w=0,x=0,y=798030,z=798031,hh=0,vv=1457)
+1790: y0 798030 v:=23000201+798030=23798231, vv:=1507
+1791: push
+level 1:(h=0,v=23798231,w=0,x=0,y=798030,z=798031,hh=0,vv=1507)
+1792: set2 12354("3042) type=0 h:=0+630598=630598, hh:=40
+1795: set2 65289("FF09) type=1 h:=630598+330310=960908, hh:=61
+1798: right3 600576 h:=960908+600576=1561484, hh:=99
+1802: set2 65288("FF08) type=6 h:=1561484+330310=1891794, hh:=120
+1805: set2 12354("3042) type=0 h:=1891794+630598=2522392, hh:=160
+[あ) (あ]
+1808: pop
+level 1:(h=0,v=23798231,w=0,x=0,y=798030,z=798031,hh=0,vv=1507)
+1809: y0 798030 v:=23798231+798030=24596261, vv:=1558
+1810: push
+level 1:(h=0,v=24596261,w=0,x=0,y=798030,z=798031,hh=0,vv=1558)
+1811: set2 12354("3042) type=0 h:=0+630598=630598, hh:=40
+1814: set2 12354("3042) type=0 h:=630598+630598=1261196, hh:=80
+1817: set2 65288("FF08) type=6 h:=1261196+330310=1591506, hh:=101
+1820: set2 12354("3042) type=0 h:=1591506+630598=2222104, hh:=141
+[ああ(あ]
+1823: pop
+level 1:(h=0,v=24596261,w=0,x=0,y=798030,z=798031,hh=0,vv=1558)
+1824: y0 798030 v:=24596261+798030=25394291, vv:=1608
+1825: push
+level 1:(h=0,v=25394291,w=0,x=0,y=798030,z=798031,hh=0,vv=1608)
+1826: set2 12354("3042) type=0 h:=0+630598=630598, hh:=40
+1829: set2 12354("3042) type=0 h:=630598+630598=1261196, hh:=80
+1832: set2 65288("FF08) type=6 h:=1261196+330310=1591506, hh:=101
+1835: set2 12354("3042) type=0 h:=1591506+630598=2222104, hh:=141
+[ああ(あ]
+1838: pop
+level 1:(h=0,v=25394291,w=0,x=0,y=798030,z=798031,hh=0,vv=1608)
+1839: y0 798030 v:=25394291+798030=26192321, vv:=1659
+1840: push
+level 1:(h=0,v=26192321,w=0,x=0,y=798030,z=798031,hh=0,vv=1659)
+1841: set2 12354("3042) type=0 h:=0+630598=630598, hh:=40
+1844: set2 65289("FF09) type=1 h:=630598+330310=960908, hh:=61
+1847: right3 300288 h:=960908+300288=1261196, hh:=80
+1851: set2 12354("3042) type=0 h:=1261196+630598=1891794, hh:=120
+1854: set2 12354("3042) type=0 h:=1891794+630598=2522392, hh:=160
+[あ) ああ]
+1857: pop
+level 1:(h=0,v=26192321,w=0,x=0,y=798030,z=798031,hh=0,vv=1659)
+1858: y0 798030 v:=26192321+798030=26990351, vv:=1710
+1859: push
+level 1:(h=0,v=26990351,w=0,x=0,y=798030,z=798031,hh=0,vv=1710)
+1860: set2 12354("3042) type=0 h:=0+630598=630598, hh:=40
+1863: set2 65289("FF09) type=1 h:=630598+330310=960908, hh:=61
+1866: set2 12354("3042) type=0 h:=960908+630598=1591506, hh:=101
+1869: set2 12354("3042) type=0 h:=1591506+630598=2222104, hh:=141
+[あ)ああ]
+1872: pop
+level 1:(h=0,v=26990351,w=0,x=0,y=798030,z=798031,hh=0,vv=1710)
+1873: y0 798030 v:=26990351+798030=27788381, vv:=1760
+1874: push
+level 1:(h=0,v=27788381,w=0,x=0,y=798030,z=798031,hh=0,vv=1760)
+1875: set2 9632("25A0) type=0 h:=0+630598=630598, hh:=40
+[■]
+1878: fntnum1 current font is cmtt10
+1879: setchar92 ("5C) h:=630598+344061=974659, hh:=62
+1880: setchar105 ("69) h:=974659+344061=1318720, hh:=84
+1881: setchar103 ("67) h:=1318720+344061=1662781, hh:=106
+1882: setchar110 ("6E) h:=1662781+344061=2006842, hh:=128
+1883: setchar111 ("6F) h:=2006842+344061=2350903, hh:=150
+1884: setchar114 ("72) h:=2350903+344061=2694964, hh:=172
+1885: setchar101 ("65) h:=2694964+344061=3039025, hh:=194
+1886: setchar115 ("73) h:=3039025+344061=3383086, hh:=216
+1887: setchar112 ("70) h:=3383086+344061=3727147, hh:=238
+1888: setchar97 ("61) h:=3727147+344061=4071208, hh:=260
+1889: setchar99 ("63) h:=4071208+344061=4415269, hh:=282
+1890: setchar101 ("65) h:=4415269+344061=4759330, hh:=303
+1891: setchar115 ("73) h:=4759330+344061=5103391, hh:=325
+[\ignorespaces]
+1892: pop
+level 1:(h=0,v=27788381,w=0,x=0,y=798030,z=798031,hh=0,vv=1760)
+1893: y0 798030 v:=27788381+798030=28586411, vv:=1811
+1894: push
+level 1:(h=0,v=28586411,w=0,x=0,y=798030,z=798031,hh=0,vv=1811)
+1895: fntnum2 current font is umin10
+1896: set2 12354("3042) type=0 h:=0+630598=630598, hh:=40
+1899: set2 65289("FF09) type=1 h:=630598+330310=960908, hh:=61
+1902: set2 65288("FF08) type=6 h:=960908+330310=1291218, hh:=82
+1905: set2 12354("3042) type=0 h:=1291218+630598=1921816, hh:=122
+[あ)(あ]
+1908: pop
+level 1:(h=0,v=28586411,w=0,x=0,y=798030,z=798031,hh=0,vv=1811)
+1909: y0 798030 v:=28586411+798030=29384441, vv:=1861
+1910: push
+level 1:(h=0,v=29384441,w=0,x=0,y=798030,z=798031,hh=0,vv=1861)
+1911: set2 12354("3042) type=0 h:=0+630598=630598, hh:=40
+1914: set2 65289("FF09) type=1 h:=630598+330310=960908, hh:=61
+1917: right3 300288 h:=960908+300288=1261196, hh:=80
+1921: set2 65288("FF08) type=6 h:=1261196+330310=1591506, hh:=101
+1924: set2 12354("3042) type=0 h:=1591506+630598=2222104, hh:=141
+[あ) (あ]
+1927: pop
+level 1:(h=0,v=29384441,w=0,x=0,y=798030,z=798031,hh=0,vv=1861)
+1928: y0 798030 v:=29384441+798030=30182471, vv:=1912
+1929: push
+level 1:(h=0,v=30182471,w=0,x=0,y=798030,z=798031,hh=0,vv=1912)
+1930: set2 12354("3042) type=0 h:=0+630598=630598, hh:=40
+1933: set2 65289("FF09) type=1 h:=630598+330310=960908, hh:=61
+1936: set2 65288("FF08) type=6 h:=960908+330310=1291218, hh:=82
+1939: set2 12354("3042) type=0 h:=1291218+630598=1921816, hh:=122
+[あ)(あ]
+1942: pop
+level 1:(h=0,v=30182471,w=0,x=0,y=798030,z=798031,hh=0,vv=1912)
+1943: y0 798030 v:=30182471+798030=30980501, vv:=1962
+1944: push
+level 1:(h=0,v=30980501,w=0,x=0,y=798030,z=798031,hh=0,vv=1962)
+1945: set2 12354("3042) type=0 h:=0+630598=630598, hh:=40
+1948: set2 65289("FF09) type=1 h:=630598+330310=960908, hh:=61
+1951: right3 300288 h:=960908+300288=1261196, hh:=80
+1955: set2 65288("FF08) type=6 h:=1261196+330310=1591506, hh:=101
+1958: set2 12354("3042) type=0 h:=1591506+630598=2222104, hh:=141
+[あ) (あ]
+1961: pop
+level 1:(h=0,v=30980501,w=0,x=0,y=798030,z=798031,hh=0,vv=1962)
+1962: z0 798031 v:=30980501+798031=31778532, vv:=2013
+1963: push
+level 1:(h=0,v=31778532,w=0,x=0,y=798030,z=798031,hh=0,vv=2013)
+1964: set2 12354("3042) type=0 h:=0+630598=630598, hh:=40
+1967: set2 65289("FF09) type=1 h:=630598+330310=960908, hh:=61
+1970: right3 600576 h:=960908+600576=1561484, hh:=99
+1974: set2 65288("FF08) type=6 h:=1561484+330310=1891794, hh:=120
+1977: set2 12354("3042) type=0 h:=1891794+630598=2522392, hh:=160
+[あ) (あ]
+1980: pop
+level 1:(h=0,v=31778532,w=0,x=0,y=798030,z=798031,hh=0,vv=2013)
+1981: y0 798030 v:=31778532+798030=32576562, vv:=2063
+1982: push
+level 1:(h=0,v=32576562,w=0,x=0,y=798030,z=798031,hh=0,vv=2063)
+1983: set2 9632("25A0) type=0 h:=0+630598=630598, hh:=40
+[■]
+1986: fntnum1 current font is cmtt10
+1987: setchar92 ("5C) h:=630598+344061=974659, hh:=62
+1988: setchar115 ("73) h:=974659+344061=1318720, hh:=84
+1989: setchar112 ("70) h:=1318720+344061=1662781, hh:=106
+1990: setchar97 ("61) h:=1662781+344061=2006842, hh:=128
+1991: setchar99 ("63) h:=2006842+344061=2350903, hh:=150
+1992: setchar101 ("65) h:=2350903+344061=2694964, hh:=172
+1993: setchar102 ("66) h:=2694964+344061=3039025, hh:=194
+1994: setchar97 ("61) h:=3039025+344061=3383086, hh:=216
+1995: setchar99 ("63) h:=3383086+344061=3727147, hh:=238
+1996: setchar116 ("74) h:=3727147+344061=4071208, hh:=260
+1997: setchar111 ("6F) h:=4071208+344061=4415269, hh:=282
+1998: setchar114 ("72) h:=4415269+344061=4759330, hh:=303
+1999: setchar49 ("31) h:=4759330+344061=5103391, hh:=325
+2000: setchar48 ("30) h:=5103391+344061=5447452, hh:=347
+2001: setchar48 ("30) h:=5447452+344061=5791513, hh:=369
+2002: setchar48 ("30) h:=5791513+344061=6135574, hh:=391
+[\spacefactor1000]
+2003: pop
+level 1:(h=0,v=32576562,w=0,x=0,y=798030,z=798031,hh=0,vv=2063)
+2004: y0 798030 v:=32576562+798030=33374592, vv:=2114
+2005: push
+level 1:(h=0,v=33374592,w=0,x=0,y=798030,z=798031,hh=0,vv=2114)
+2006: fntnum2 current font is umin10
+2007: set2 12354("3042) type=0 h:=0+630598=630598, hh:=40
+2010: set2 65289("FF09) type=1 h:=630598+330310=960908, hh:=61
+2013: set2 65288("FF08) type=6 h:=960908+330310=1291218, hh:=82
+2016: set2 12354("3042) type=0 h:=1291218+630598=1921816, hh:=122
+[あ)(あ]
+2019: pop
+level 1:(h=0,v=33374592,w=0,x=0,y=798030,z=798031,hh=0,vv=2114)
+2020: y0 798030 v:=33374592+798030=34172622, vv:=2165
+2021: push
+level 1:(h=0,v=34172622,w=0,x=0,y=798030,z=798031,hh=0,vv=2165)
+2022: set2 12354("3042) type=0 h:=0+630598=630598, hh:=40
+2025: set2 65289("FF09) type=1 h:=630598+330310=960908, hh:=61
+2028: right3 300288 h:=960908+300288=1261196, hh:=80
+2032: set2 65288("FF08) type=6 h:=1261196+330310=1591506, hh:=101
+2035: set2 12354("3042) type=0 h:=1591506+630598=2222104, hh:=141
+[あ) (あ]
+2038: pop
+level 1:(h=0,v=34172622,w=0,x=0,y=798030,z=798031,hh=0,vv=2165)
+2039: y0 798030 v:=34172622+798030=34970652, vv:=2215
+2040: push
+level 1:(h=0,v=34970652,w=0,x=0,y=798030,z=798031,hh=0,vv=2215)
+2041: set2 12354("3042) type=0 h:=0+630598=630598, hh:=40
+2044: set2 65289("FF09) type=1 h:=630598+330310=960908, hh:=61
+2047: set2 65288("FF08) type=6 h:=960908+330310=1291218, hh:=82
+2050: set2 12354("3042) type=0 h:=1291218+630598=1921816, hh:=122
+[あ)(あ]
+2053: pop
+level 1:(h=0,v=34970652,w=0,x=0,y=798030,z=798031,hh=0,vv=2215)
+2054: y0 798030 v:=34970652+798030=35768682, vv:=2266
+2055: push
+level 1:(h=0,v=35768682,w=0,x=0,y=798030,z=798031,hh=0,vv=2266)
+2056: set2 12354("3042) type=0 h:=0+630598=630598, hh:=40
+2059: set2 65289("FF09) type=1 h:=630598+330310=960908, hh:=61
+2062: right3 300288 h:=960908+300288=1261196, hh:=80
+2066: set2 65288("FF08) type=6 h:=1261196+330310=1591506, hh:=101
+2069: set2 12354("3042) type=0 h:=1591506+630598=2222104, hh:=141
+[あ) (あ]
+2072: pop
+level 1:(h=0,v=35768682,w=0,x=0,y=798030,z=798031,hh=0,vv=2266)
+2073: y0 798030 v:=35768682+798030=36566712, vv:=2316
+2074: push
+level 1:(h=0,v=36566712,w=0,x=0,y=798030,z=798031,hh=0,vv=2316)
+2075: set2 12354("3042) type=0 h:=0+630598=630598, hh:=40
+2078: set2 65289("FF09) type=1 h:=630598+330310=960908, hh:=61
+2081: right3 600576 h:=960908+600576=1561484, hh:=99
+2085: set2 65288("FF08) type=6 h:=1561484+330310=1891794, hh:=120
+2088: set2 12354("3042) type=0 h:=1891794+630598=2522392, hh:=160
+[あ) (あ]
+2091: pop
+level 1:(h=0,v=36566712,w=0,x=0,y=798030,z=798031,hh=0,vv=2316)
+2092: y0 798030 v:=36566712+798030=37364742, vv:=2367
+2093: push
+level 1:(h=0,v=37364742,w=0,x=0,y=798030,z=798031,hh=0,vv=2367)
+2094: set2 9632("25A0) type=0 h:=0+630598=630598, hh:=40
+[■]
+2097: fntnum1 current font is cmtt10
+2098: setchar92 ("5C) h:=630598+344061=974659, hh:=62
+2099: setchar108 ("6C) h:=974659+344061=1318720, hh:=84
+2100: setchar101 ("65) h:=1318720+344061=1662781, hh:=106
+2101: setchar116 ("74) h:=1662781+344061=2006842, hh:=128
+[\let]
+2102: pop
+level 1:(h=0,v=37364742,w=0,x=0,y=798030,z=798031,hh=0,vv=2367)
+2103: y0 798030 v:=37364742+798030=38162772, vv:=2417
+2104: push
+level 1:(h=0,v=38162772,w=0,x=0,y=798030,z=798031,hh=0,vv=2417)
+2105: fntnum2 current font is umin10
+2106: set2 12354("3042) type=0 h:=0+630598=630598, hh:=40
+2109: set2 65289("FF09) type=1 h:=630598+330310=960908, hh:=61
+2112: set2 65288("FF08) type=6 h:=960908+330310=1291218, hh:=82
+2115: set2 12354("3042) type=0 h:=1291218+630598=1921816, hh:=122
+[あ)(あ]
+2118: pop
+level 1:(h=0,v=38162772,w=0,x=0,y=798030,z=798031,hh=0,vv=2417)
+2119: y0 798030 v:=38162772+798030=38960802, vv:=2468
+2120: push
+level 1:(h=0,v=38960802,w=0,x=0,y=798030,z=798031,hh=0,vv=2468)
+2121: set2 12354("3042) type=0 h:=0+630598=630598, hh:=40
+2124: set2 65289("FF09) type=1 h:=630598+330310=960908, hh:=61
+2127: right3 300288 h:=960908+300288=1261196, hh:=80
+2131: set2 65288("FF08) type=6 h:=1261196+330310=1591506, hh:=101
+2134: set2 12354("3042) type=0 h:=1591506+630598=2222104, hh:=141
+[あ) (あ]
+2137: pop
+level 1:(h=0,v=38960802,w=0,x=0,y=798030,z=798031,hh=0,vv=2468)
+2138: y0 798030 v:=38960802+798030=39758832, vv:=2518
+2139: push
+level 1:(h=0,v=39758832,w=0,x=0,y=798030,z=798031,hh=0,vv=2518)
+2140: set2 12354("3042) type=0 h:=0+630598=630598, hh:=40
+2143: set2 65289("FF09) type=1 h:=630598+330310=960908, hh:=61
+2146: set2 65288("FF08) type=6 h:=960908+330310=1291218, hh:=82
+2149: set2 12354("3042) type=0 h:=1291218+630598=1921816, hh:=122
+[あ)(あ]
+2152: pop
+level 1:(h=0,v=39758832,w=0,x=0,y=798030,z=798031,hh=0,vv=2518)
+2153: y0 798030 v:=39758832+798030=40556862, vv:=2569
+2154: push
+level 1:(h=0,v=40556862,w=0,x=0,y=798030,z=798031,hh=0,vv=2569)
+2155: set2 12354("3042) type=0 h:=0+630598=630598, hh:=40
+2158: set2 65289("FF09) type=1 h:=630598+330310=960908, hh:=61
+2161: right3 300288 h:=960908+300288=1261196, hh:=80
+2165: set2 65288("FF08) type=6 h:=1261196+330310=1591506, hh:=101
+2168: set2 12354("3042) type=0 h:=1591506+630598=2222104, hh:=141
+[あ) (あ]
+2171: pop
+level 1:(h=0,v=40556862,w=0,x=0,y=798030,z=798031,hh=0,vv=2569)
+2172: y0 798030 v:=40556862+798030=41354892, vv:=2619
+2173: push
+level 1:(h=0,v=41354892,w=0,x=0,y=798030,z=798031,hh=0,vv=2619)
+2174: set2 12354("3042) type=0 h:=0+630598=630598, hh:=40
+2177: set2 65289("FF09) type=1 h:=630598+330310=960908, hh:=61
+2180: right3 600576 h:=960908+600576=1561484, hh:=99
+2184: set2 65288("FF08) type=6 h:=1561484+330310=1891794, hh:=120
+2187: set2 12354("3042) type=0 h:=1891794+630598=2522392, hh:=160
+[あ) (あ]
+2190: pop
+level 1:(h=0,v=41354892,w=0,x=0,y=798030,z=798031,hh=0,vv=2619)
+2191: y0 798030 v:=41354892+798030=42152922, vv:=2670
+2192: push
+level 1:(h=0,v=42152922,w=0,x=0,y=798030,z=798031,hh=0,vv=2670)
+2193: set2 9632("25A0) type=0 h:=0+630598=630598, hh:=40
+[■]
+2196: fntnum1 current font is cmtt10
+2197: setchar92 ("5C) h:=630598+344061=974659, hh:=62
+2198: setchar103 ("67) h:=974659+344061=1318720, hh:=84
+2199: setchar114 ("72) h:=1318720+344061=1662781, hh:=106
+2200: setchar111 ("6F) h:=1662781+344061=2006842, hh:=128
+2201: setchar117 ("75) h:=2006842+344061=2350903, hh:=150
+2202: setchar112 ("70) h:=2350903+344061=2694964, hh:=172
+[\group]
+2203: pop
+level 1:(h=0,v=42152922,w=0,x=0,y=798030,z=798031,hh=0,vv=2670)
+2204: pop
+level 0:(h=0,v=42152922,w=0,x=0,y=0,z=0,hh=0,vv=2670)
+2205: down3 1572864 v:=42152922+1572864=43725786, vv:=2770
+2209: eop
+
+2210: beginning of page 3
+2255: push
+level 0:(h=0,v=0,w=0,x=0,y=0,z=0,hh=0,vv=0)
+2256: down3 -917504 v:=0-917504=-917504, vv:=-58
+2260: pop
+level 0:(h=0,v=0,w=0,x=0,y=0,z=0,hh=0,vv=0)
+2261: down4 42152922 v:=0+42152922=42152922, vv:=2670
+2266: push
+level 0:(h=0,v=42152922,w=0,x=0,y=0,z=0,hh=0,vv=2670)
+2267: down4 -41497562 v:=42152922-41497562=655360, vv:=42
+2272: push
+level 1:(h=0,v=655360,w=0,x=0,y=0,z=0,hh=0,vv=42)
+2273: fntnum2 current font is umin10
+2274: set2 12354("3042) type=0 h:=0+630598=630598, hh:=40
+2277: set2 65289("FF09) type=1 h:=630598+330310=960908, hh:=61
+2280: right3 300288 h:=960908+300288=1261196, hh:=80
+2284: set2 65288("FF08) type=6 h:=1261196+330310=1591506, hh:=101
+2287: set2 12354("3042) type=0 h:=1591506+630598=2222104, hh:=141
+[あ) (あ]
+2290: pop
+level 1:(h=0,v=655360,w=0,x=0,y=0,z=0,hh=0,vv=42)
+2291: y3 786432 v:=655360+786432=1441792, vv:=91
+2295: push
+level 1:(h=0,v=1441792,w=0,x=0,y=786432,z=0,hh=0,vv=91)
+2296: set2 12354("3042) type=0 h:=0+630598=630598, hh:=40
+2299: set2 65289("FF09) type=1 h:=630598+330310=960908, hh:=61
+2302: right3 600576 h:=960908+600576=1561484, hh:=99
+2306: set2 65288("FF08) type=6 h:=1561484+330310=1891794, hh:=120
+2309: set2 12354("3042) type=0 h:=1891794+630598=2522392, hh:=160
+[あ) (あ]
+2312: pop
+level 1:(h=0,v=1441792,w=0,x=0,y=786432,z=0,hh=0,vv=91)
+2313: y0 786432 v:=1441792+786432=2228224, vv:=141
+2314: push
+level 1:(h=0,v=2228224,w=0,x=0,y=786432,z=0,hh=0,vv=141)
+2315: set2 12354("3042) type=0 h:=0+630598=630598, hh:=40
+2318: set2 65289("FF09) type=1 h:=630598+330310=960908, hh:=61
+2321: right3 300288 h:=960908+300288=1261196, hh:=80
+2325: set2 65288("FF08) type=6 h:=1261196+330310=1591506, hh:=101
+2328: set2 12354("3042) type=0 h:=1591506+630598=2222104, hh:=141
+[あ) (あ]
+2331: pop
+level 1:(h=0,v=2228224,w=0,x=0,y=786432,z=0,hh=0,vv=141)
+2332: y0 786432 v:=2228224+786432=3014656, vv:=191
+2333: push
+level 1:(h=0,v=3014656,w=0,x=0,y=786432,z=0,hh=0,vv=191)
+2334: set2 12354("3042) type=0 h:=0+630598=630598, hh:=40
+2337: set2 65289("FF09) type=1 h:=630598+330310=960908, hh:=61
+2340: set2 65288("FF08) type=6 h:=960908+330310=1291218, hh:=82
+2343: set2 12354("3042) type=0 h:=1291218+630598=1921816, hh:=122
+[あ)(あ]
+2346: pop
+level 1:(h=0,v=3014656,w=0,x=0,y=786432,z=0,hh=0,vv=191)
+2347: y0 786432 v:=3014656+786432=3801088, vv:=241
+2348: push
+level 1:(h=0,v=3801088,w=0,x=0,y=786432,z=0,hh=0,vv=241)
+2349: set2 12354("3042) type=0 h:=0+630598=630598, hh:=40
+2352: set2 65289("FF09) type=1 h:=630598+330310=960908, hh:=61
+2355: right3 300288 h:=960908+300288=1261196, hh:=80
+2359: set2 65288("FF08) type=6 h:=1261196+330310=1591506, hh:=101
+2362: set2 12354("3042) type=0 h:=1591506+630598=2222104, hh:=141
+[あ) (あ]
+2365: pop
+level 1:(h=0,v=3801088,w=0,x=0,y=786432,z=0,hh=0,vv=241)
+2366: y0 786432 v:=3801088+786432=4587520, vv:=291
+2367: push
+level 1:(h=0,v=4587520,w=0,x=0,y=786432,z=0,hh=0,vv=291)
+2368: set2 12354("3042) type=0 h:=0+630598=630598, hh:=40
+2371: set2 65289("FF09) type=1 h:=630598+330310=960908, hh:=61
+2374: right3 300288 h:=960908+300288=1261196, hh:=80
+2378: set2 65288("FF08) type=6 h:=1261196+330310=1591506, hh:=101
+2381: set2 12354("3042) type=0 h:=1591506+630598=2222104, hh:=141
+[あ) (あ]
+2384: pop
+level 1:(h=0,v=4587520,w=0,x=0,y=786432,z=0,hh=0,vv=291)
+2385: y0 786432 v:=4587520+786432=5373952, vv:=340
+2386: push
+level 1:(h=0,v=5373952,w=0,x=0,y=786432,z=0,hh=0,vv=340)
+2387: set2 12354("3042) type=0 h:=0+630598=630598, hh:=40
+2390: set2 65289("FF09) type=1 h:=630598+330310=960908, hh:=61
+2393: set2 65288("FF08) type=6 h:=960908+330310=1291218, hh:=82
+2396: set2 12354("3042) type=0 h:=1291218+630598=1921816, hh:=122
+[あ)(あ]
+2399: pop
+level 1:(h=0,v=5373952,w=0,x=0,y=786432,z=0,hh=0,vv=340)
+2400: y0 786432 v:=5373952+786432=6160384, vv:=390
+2401: push
+level 1:(h=0,v=6160384,w=0,x=0,y=786432,z=0,hh=0,vv=390)
+2402: set2 12354("3042) type=0 h:=0+630598=630598, hh:=40
+2405: set2 65289("FF09) type=1 h:=630598+330310=960908, hh:=61
+2408: right3 150143 h:=960908+150143=1111051, hh:=70
+2412: set2 12539("30FB) type=4 h:=1111051+231778=1342829, hh:=85
+2415: right3 199410 h:=1342829+199410=1542239, hh:=98
+2419: set2 12354("3042) type=0 h:=1542239+630598=2172837, hh:=138
+[あ) ・ あ]
+2422: pop
+level 1:(h=0,v=6160384,w=0,x=0,y=786432,z=0,hh=0,vv=390)
+2423: y0 786432 v:=6160384+786432=6946816, vv:=440
+2424: push
+level 1:(h=0,v=6946816,w=0,x=0,y=786432,z=0,hh=0,vv=440)
+2425: set2 12354("3042) type=0 h:=0+630598=630598, hh:=40
+2428: set2 65289("FF09) type=1 h:=630598+330310=960908, hh:=61
+2431: right3 499698 h:=960908+499698=1460606, hh:=93
+2435: set2 12539("30FB) type=4 h:=1460606+231778=1692384, hh:=108
+2438: right3 199410 h:=1692384+199410=1891794, hh:=120
+2442: set2 12354("3042) type=0 h:=1891794+630598=2522392, hh:=160
+[あ) ・ あ]
+2445: pop
+level 1:(h=0,v=6946816,w=0,x=0,y=786432,z=0,hh=0,vv=440)
+2446: y0 786432 v:=6946816+786432=7733248, vv:=490
+2447: push
+level 1:(h=0,v=7733248,w=0,x=0,y=786432,z=0,hh=0,vv=490)
+2448: set2 12354("3042) type=0 h:=0+630598=630598, hh:=40
+2451: set2 65289("FF09) type=1 h:=630598+330310=960908, hh:=61
+2454: right3 300288 h:=960908+300288=1261196, hh:=80
+2458: set2 12539("30FB) type=4 h:=1261196+231778=1492974, hh:=95
+2461: right3 199410 h:=1492974+199410=1692384, hh:=107
+2465: set2 12354("3042) type=0 h:=1692384+630598=2322982, hh:=147
+[あ) ・ あ]
+2468: pop
+level 1:(h=0,v=7733248,w=0,x=0,y=786432,z=0,hh=0,vv=490)
+2469: y0 786432 v:=7733248+786432=8519680, vv:=540
+2470: push
+level 1:(h=0,v=8519680,w=0,x=0,y=786432,z=0,hh=0,vv=540)
+2471: set2 12354("3042) type=0 h:=0+630598=630598, hh:=40
+2474: set2 65289("FF09) type=1 h:=630598+330310=960908, hh:=61
+2477: set2 12539("30FB) type=4 h:=960908+231778=1192686, hh:=76
+2480: right3 199410 h:=1192686+199410=1392096, hh:=88
+2484: set2 12354("3042) type=0 h:=1392096+630598=2022694, hh:=128
+[あ)・ あ]
+2487: pop
+level 1:(h=0,v=8519680,w=0,x=0,y=786432,z=0,hh=0,vv=540)
+2488: y0 786432 v:=8519680+786432=9306112, vv:=589
+2489: push
+level 1:(h=0,v=9306112,w=0,x=0,y=786432,z=0,hh=0,vv=589)
+2490: set2 12354("3042) type=0 h:=0+630598=630598, hh:=40
+2493: set2 65289("FF09) type=1 h:=630598+330310=960908, hh:=61
+2496: right3 300288 h:=960908+300288=1261196, hh:=80
+2500: set2 12539("30FB) type=4 h:=1261196+231778=1492974, hh:=95
+2503: right3 199410 h:=1492974+199410=1692384, hh:=107
+2507: set2 12354("3042) type=0 h:=1692384+630598=2322982, hh:=147
+[あ) ・ あ]
+2510: pop
+level 1:(h=0,v=9306112,w=0,x=0,y=786432,z=0,hh=0,vv=589)
+2511: y0 786432 v:=9306112+786432=10092544, vv:=639
+2512: push
+level 1:(h=0,v=10092544,w=0,x=0,y=786432,z=0,hh=0,vv=639)
+2513: set2 12354("3042) type=0 h:=0+630598=630598, hh:=40
+2516: set2 65289("FF09) type=1 h:=630598+330310=960908, hh:=61
+2519: right3 300288 h:=960908+300288=1261196, hh:=80
+2523: set2 12539("30FB) type=4 h:=1261196+231778=1492974, hh:=95
+2526: right3 199410 h:=1492974+199410=1692384, hh:=107
+2530: set2 12354("3042) type=0 h:=1692384+630598=2322982, hh:=147
+[あ) ・ あ]
+2533: pop
+level 1:(h=0,v=10092544,w=0,x=0,y=786432,z=0,hh=0,vv=639)
+2534: y0 786432 v:=10092544+786432=10878976, vv:=689
+2535: push
+level 1:(h=0,v=10878976,w=0,x=0,y=786432,z=0,hh=0,vv=689)
+2536: set2 12354("3042) type=0 h:=0+630598=630598, hh:=40
+2539: set2 65289("FF09) type=1 h:=630598+330310=960908, hh:=61
+2542: set2 12539("30FB) type=4 h:=960908+231778=1192686, hh:=76
+2545: right3 199410 h:=1192686+199410=1392096, hh:=88
+2549: set2 12354("3042) type=0 h:=1392096+630598=2022694, hh:=128
+[あ)・ あ]
+2552: pop
+level 1:(h=0,v=10878976,w=0,x=0,y=786432,z=0,hh=0,vv=689)
+2553: y0 786432 v:=10878976+786432=11665408, vv:=739
+2554: push
+level 1:(h=0,v=11665408,w=0,x=0,y=786432,z=0,hh=0,vv=739)
+2555: set2 12354("3042) type=0 h:=0+630598=630598, hh:=40
+2558: right3 199410 h:=630598+199410=830008, hh:=53
+2562: set2 12539("30FB) type=4 h:=830008+231778=1061786, hh:=68
+2565: right3 150143 h:=1061786+150143=1211929, hh:=77
+2569: set2 65288("FF08) type=6 h:=1211929+330310=1542239, hh:=98
+2572: set2 12354("3042) type=0 h:=1542239+630598=2172837, hh:=138
+[あ ・ (あ]
+2575: pop
+level 1:(h=0,v=11665408,w=0,x=0,y=786432,z=0,hh=0,vv=739)
+2576: y0 786432 v:=11665408+786432=12451840, vv:=789
+2577: push
+level 1:(h=0,v=12451840,w=0,x=0,y=786432,z=0,hh=0,vv=789)
+2578: set2 12354("3042) type=0 h:=0+630598=630598, hh:=40
+2581: right3 199410 h:=630598+199410=830008, hh:=53
+2585: set2 12539("30FB) type=4 h:=830008+231778=1061786, hh:=68
+2588: right3 499698 h:=1061786+499698=1561484, hh:=99
+2592: set2 65288("FF08) type=6 h:=1561484+330310=1891794, hh:=120
+2595: set2 12354("3042) type=0 h:=1891794+630598=2522392, hh:=160
+[あ ・ (あ]
+2598: pop
+level 1:(h=0,v=12451840,w=0,x=0,y=786432,z=0,hh=0,vv=789)
+2599: y0 786432 v:=12451840+786432=13238272, vv:=839
+2600: push
+level 1:(h=0,v=13238272,w=0,x=0,y=786432,z=0,hh=0,vv=839)
+2601: set2 12354("3042) type=0 h:=0+630598=630598, hh:=40
+2604: w3 199410 h:=630598+199410=830008, hh:=53
+2608: set2 12539("30FB) type=4 h:=830008+231778=1061786, hh:=68
+2611: w0 199410 h:=1061786+199410=1261196, hh:=80
+2612: set2 65288("FF08) type=6 h:=1261196+330310=1591506, hh:=101
+2615: set2 12354("3042) type=0 h:=1591506+630598=2222104, hh:=141
+[あ ・ (あ]
+2618: pop
+level 1:(h=0,v=13238272,w=0,x=0,y=786432,z=0,hh=0,vv=839)
+2619: y0 786432 v:=13238272+786432=14024704, vv:=888
+2620: push
+level 1:(h=0,v=14024704,w=0,x=0,y=786432,z=0,hh=0,vv=888)
+2621: set2 12354("3042) type=0 h:=0+630598=630598, hh:=40
+2624: right3 199410 h:=630598+199410=830008, hh:=53
+2628: set2 12539("30FB) type=4 h:=830008+231778=1061786, hh:=68
+2631: set2 65288("FF08) type=6 h:=1061786+330310=1392096, hh:=89
+2634: set2 12354("3042) type=0 h:=1392096+630598=2022694, hh:=129
+[あ ・(あ]
+2637: pop
+level 1:(h=0,v=14024704,w=0,x=0,y=786432,z=0,hh=0,vv=888)
+2638: y0 786432 v:=14024704+786432=14811136, vv:=938
+2639: push
+level 1:(h=0,v=14811136,w=0,x=0,y=786432,z=0,hh=0,vv=938)
+2640: set2 12354("3042) type=0 h:=0+630598=630598, hh:=40
+2643: w3 199410 h:=630598+199410=830008, hh:=53
+2647: set2 12539("30FB) type=4 h:=830008+231778=1061786, hh:=68
+2650: w0 199410 h:=1061786+199410=1261196, hh:=80
+2651: set2 65288("FF08) type=6 h:=1261196+330310=1591506, hh:=101
+2654: set2 12354("3042) type=0 h:=1591506+630598=2222104, hh:=141
+[あ ・ (あ]
+2657: pop
+level 1:(h=0,v=14811136,w=0,x=0,y=786432,z=0,hh=0,vv=938)
+2658: y0 786432 v:=14811136+786432=15597568, vv:=988
+2659: push
+level 1:(h=0,v=15597568,w=0,x=0,y=786432,z=0,hh=0,vv=988)
+2660: set2 12354("3042) type=0 h:=0+630598=630598, hh:=40
+2663: w3 199410 h:=630598+199410=830008, hh:=53
+2667: set2 12539("30FB) type=4 h:=830008+231778=1061786, hh:=68
+2670: w0 199410 h:=1061786+199410=1261196, hh:=80
+2671: set2 65288("FF08) type=6 h:=1261196+330310=1591506, hh:=101
+2674: set2 12354("3042) type=0 h:=1591506+630598=2222104, hh:=141
+[あ ・ (あ]
+2677: pop
+level 1:(h=0,v=15597568,w=0,x=0,y=786432,z=0,hh=0,vv=988)
+2678: y0 786432 v:=15597568+786432=16384000, vv:=1038
+2679: push
+level 1:(h=0,v=16384000,w=0,x=0,y=786432,z=0,hh=0,vv=1038)
+2680: set2 12354("3042) type=0 h:=0+630598=630598, hh:=40
+2683: right3 199410 h:=630598+199410=830008, hh:=53
+2687: set2 12539("30FB) type=4 h:=830008+231778=1061786, hh:=68
+2690: set2 65288("FF08) type=6 h:=1061786+330310=1392096, hh:=89
+2693: set2 12354("3042) type=0 h:=1392096+630598=2022694, hh:=129
+[あ ・(あ]
+2696: pop
+level 1:(h=0,v=16384000,w=0,x=0,y=786432,z=0,hh=0,vv=1038)
+2697: y0 786432 v:=16384000+786432=17170432, vv:=1088
+2698: push
+level 1:(h=0,v=17170432,w=0,x=0,y=786432,z=0,hh=0,vv=1088)
+2699: set2 9632("25A0) type=0 h:=0+630598=630598, hh:=40
+[■]
+2702: fntnum1 current font is cmtt10
+2703: setchar92 ("5C) h:=630598+344061=974659, hh:=62
+2704: setchar105 ("69) h:=974659+344061=1318720, hh:=84
+2705: setchar110 ("6E) h:=1318720+344061=1662781, hh:=106
+2706: setchar104 ("68) h:=1662781+344061=2006842, hh:=128
+2707: setchar105 ("69) h:=2006842+344061=2350903, hh:=150
+2708: setchar98 ("62) h:=2350903+344061=2694964, hh:=172
+2709: setchar105 ("69) h:=2694964+344061=3039025, hh:=194
+2710: setchar116 ("74) h:=3039025+344061=3383086, hh:=216
+2711: setchar103 ("67) h:=3383086+344061=3727147, hh:=238
+2712: setchar108 ("6C) h:=3727147+344061=4071208, hh:=260
+2713: setchar117 ("75) h:=4071208+344061=4415269, hh:=282
+2714: setchar101 ("65) h:=4415269+344061=4759330, hh:=303
+2715: w3 344061 h:=4759330+344061=5103391, hh:=323
+2719: setchar97 ("61) h:=5103391+344061=5447452, hh:=345
+2720: setchar116 ("74) h:=5447452+344061=5791513, hh:=367
+2721: w0 344061 h:=5791513+344061=6135574, hh:=389
+[\inhibitglue at ]
+2722: fntnum2 current font is umin10
+2723: set2 20869("5185) type=0 h:=6135574+630598=6766172, hh:=429
+2726: set2 20596("5074) type=0 h:=6766172+630598=7396770, hh:=469
+2729: set2 12522("30EA) type=10 h:=7396770+630598=8027368, hh:=509
+2732: set2 12473("30B9) type=0 h:=8027368+630598=8657966, hh:=549
+2735: set2 12488("30C8) type=10 h:=8657966+630598=9288564, hh:=589
+[内側リスト]
+2738: pop
+level 1:(h=0,v=17170432,w=0,x=0,y=786432,z=0,hh=0,vv=1088)
+2739: y0 786432 v:=17170432+786432=17956864, vv:=1137
+2740: push
+level 1:(h=0,v=17956864,w=0,x=0,y=786432,z=0,hh=0,vv=1137)
+2741: set2 12354("3042) type=0 h:=0+630598=630598, hh:=40
+2744: set2 65288("FF08) type=6 h:=630598+330310=960908, hh:=61
+[あ(]
+2747: pop
+level 1:(h=0,v=17956864,w=0,x=0,y=786432,z=0,hh=0,vv=1137)
+2748: y0 786432 v:=17956864+786432=18743296, vv:=1187
+2749: push
+level 1:(h=0,v=18743296,w=0,x=0,y=786432,z=0,hh=0,vv=1187)
+2750: set2 12354("3042) type=0 h:=0+630598=630598, hh:=40
+2753: right3 300288 h:=630598+300288=930886, hh:=59
+2757: set2 65288("FF08) type=6 h:=930886+330310=1261196, hh:=80
+[あ (]
+2760: pop
+level 1:(h=0,v=18743296,w=0,x=0,y=786432,z=0,hh=0,vv=1187)
+2761: y0 786432 v:=18743296+786432=19529728, vv:=1237
+2762: push
+level 1:(h=0,v=19529728,w=0,x=0,y=786432,z=0,hh=0,vv=1237)
+2763: set2 65289("FF09) type=1 h:=0+330310=330310, hh:=21
+2766: right3 600576 h:=330310+600576=930886, hh:=59
+2770: set2 65288("FF08) type=6 h:=930886+330310=1261196, hh:=80
+[) (]
+2773: pop
+level 1:(h=0,v=19529728,w=0,x=0,y=786432,z=0,hh=0,vv=1237)
+2774: y0 786432 v:=19529728+786432=20316160, vv:=1287
+2775: push
+level 1:(h=0,v=20316160,w=0,x=0,y=786432,z=0,hh=0,vv=1287)
+2776: set2 12354("3042) type=0 h:=0+630598=630598, hh:=40
+2779: w3 300288 h:=630598+300288=930886, hh:=59
+2783: set2 65288("FF08) type=6 h:=930886+330310=1261196, hh:=80
+2786: set2 12356("3044) type=0 h:=1261196+630598=1891794, hh:=120
+[あ (い]
+2789: push
+level 2:(h=1891794,v=20316160,w=300288,x=0,y=786432,z=0,hh=120,vv=1287)
+2790: push
+level 3:(h=1891794,v=20316160,w=300288,x=0,y=786432,z=0,hh=120,vv=1287)
+2791: w0 300288 h:=1891794+300288=2192082, hh:=139
+2792: set2 12300("300C) type=6 h:=2192082+330310=2522392, hh:=160
+[ 「]
+2795: pop
+level 3:(h=1891794,v=20316160,w=300288,x=0,y=786432,z=0,hh=120,vv=1287)
+2796: pop
+level 2:(h=1891794,v=20316160,w=300288,x=0,y=786432,z=0,hh=120,vv=1287)
+2797: pop
+level 1:(h=0,v=20316160,w=0,x=0,y=786432,z=0,hh=0,vv=1287)
+2798: y0 786432 v:=20316160+786432=21102592, vv:=1337
+2799: push
+level 1:(h=0,v=21102592,w=0,x=0,y=786432,z=0,hh=0,vv=1337)
+2800: set2 12354("3042) type=0 h:=0+630598=630598, hh:=40
+2803: w3 300288 h:=630598+300288=930886, hh:=59
+2807: set2 65288("FF08) type=6 h:=930886+330310=1261196, hh:=80
+2810: set2 12356("3044) type=0 h:=1261196+630598=1891794, hh:=120
+[あ (い]
+2813: push
+level 2:(h=1891794,v=21102592,w=300288,x=0,y=786432,z=0,hh=120,vv=1337)
+2814: push
+level 3:(h=1891794,v=21102592,w=300288,x=0,y=786432,z=0,hh=120,vv=1337)
+2815: w0 300288 h:=1891794+300288=2192082, hh:=139
+2816: set2 12300("300C) type=6 h:=2192082+330310=2522392, hh:=160
+[ 「]
+2819: pop
+level 3:(h=1891794,v=21102592,w=300288,x=0,y=786432,z=0,hh=120,vv=1337)
+2820: pop
+level 2:(h=1891794,v=21102592,w=300288,x=0,y=786432,z=0,hh=120,vv=1337)
+2821: pop
+level 1:(h=0,v=21102592,w=0,x=0,y=786432,z=0,hh=0,vv=1337)
+2822: y0 786432 v:=21102592+786432=21889024, vv:=1386
+2823: push
+level 1:(h=0,v=21889024,w=0,x=0,y=786432,z=0,hh=0,vv=1386)
+2824: set2 12354("3042) type=0 h:=0+630598=630598, hh:=40
+2827: set2 65288("FF08) type=6 h:=630598+330310=960908, hh:=61
+2830: set2 12356("3044) type=0 h:=960908+630598=1591506, hh:=101
+[あ(い]
+2833: push
+level 2:(h=1591506,v=21889024,w=0,x=0,y=786432,z=0,hh=101,vv=1386)
+2834: push
+level 3:(h=1591506,v=21889024,w=0,x=0,y=786432,z=0,hh=101,vv=1386)
+2835: right3 300288 h:=1591506+300288=1891794, hh:=120
+2839: set2 12300("300C) type=6 h:=1891794+330310=2222104, hh:=141
+[ 「]
+2842: pop
+level 3:(h=1591506,v=21889024,w=0,x=0,y=786432,z=0,hh=101,vv=1386)
+2843: pop
+level 2:(h=1591506,v=21889024,w=0,x=0,y=786432,z=0,hh=101,vv=1386)
+2844: pop
+level 1:(h=0,v=21889024,w=0,x=0,y=786432,z=0,hh=0,vv=1386)
+2845: y0 786432 v:=21889024+786432=22675456, vv:=1436
+2846: push
+level 1:(h=0,v=22675456,w=0,x=0,y=786432,z=0,hh=0,vv=1436)
+2847: set2 12354("3042) type=0 h:=0+630598=630598, hh:=40
+2850: right3 300288 h:=630598+300288=930886, hh:=59
+2854: set2 65288("FF08) type=6 h:=930886+330310=1261196, hh:=80
+2857: set2 12356("3044) type=0 h:=1261196+630598=1891794, hh:=120
+[あ (い]
+2860: push
+level 2:(h=1891794,v=22675456,w=0,x=0,y=786432,z=0,hh=120,vv=1436)
+2861: push
+level 3:(h=1891794,v=22675456,w=0,x=0,y=786432,z=0,hh=120,vv=1436)
+2862: set2 12300("300C) type=6 h:=1891794+330310=2222104, hh:=141
+[「]
+2865: pop
+level 3:(h=1891794,v=22675456,w=0,x=0,y=786432,z=0,hh=120,vv=1436)
+2866: pop
+level 2:(h=1891794,v=22675456,w=0,x=0,y=786432,z=0,hh=120,vv=1436)
+2867: pop
+level 1:(h=0,v=22675456,w=0,x=0,y=786432,z=0,hh=0,vv=1436)
+2868: y0 786432 v:=22675456+786432=23461888, vv:=1486
+2869: push
+level 1:(h=0,v=23461888,w=0,x=0,y=786432,z=0,hh=0,vv=1486)
+2870: set2 9632("25A0) type=0 h:=0+630598=630598, hh:=40
+[■]
+2873: fntnum1 current font is cmtt10
+2874: setchar92 ("5C) h:=630598+344061=974659, hh:=62
+2875: setchar105 ("69) h:=974659+344061=1318720, hh:=84
+2876: setchar110 ("6E) h:=1318720+344061=1662781, hh:=106
+2877: setchar104 ("68) h:=1662781+344061=2006842, hh:=128
+2878: setchar105 ("69) h:=2006842+344061=2350903, hh:=150
+2879: setchar98 ("62) h:=2350903+344061=2694964, hh:=172
+2880: setchar105 ("69) h:=2694964+344061=3039025, hh:=194
+2881: setchar116 ("74) h:=3039025+344061=3383086, hh:=216
+2882: setchar103 ("67) h:=3383086+344061=3727147, hh:=238
+2883: setchar108 ("6C) h:=3727147+344061=4071208, hh:=260
+2884: setchar117 ("75) h:=4071208+344061=4415269, hh:=282
+2885: setchar101 ("65) h:=4415269+344061=4759330, hh:=303
+2886: right3 344061 h:=4759330+344061=5103391, hh:=323
+2890: setchar116 ("74) h:=5103391+344061=5447452, hh:=345
+2891: setchar101 ("65) h:=5447452+344061=5791513, hh:=367
+2892: setchar115 ("73) h:=5791513+344061=6135574, hh:=389
+2893: setchar116 ("74) h:=6135574+344061=6479635, hh:=411
+[\inhibitglue test]
+2894: pop
+level 1:(h=0,v=23461888,w=0,x=0,y=786432,z=0,hh=0,vv=1486)
+2895: y0 786432 v:=23461888+786432=24248320, vv:=1536
+2896: push
+level 1:(h=0,v=24248320,w=0,x=0,y=786432,z=0,hh=0,vv=1536)
+2897: fntnum2 current font is umin10
+2898: set2 12354("3042) type=0 h:=0+630598=630598, hh:=40
+2901: set2 65289("FF09) type=1 h:=630598+330310=960908, hh:=61
+2904: set2 12354("3042) type=0 h:=960908+630598=1591506, hh:=101
+[あ)あ]
+2907: pop
+level 1:(h=0,v=24248320,w=0,x=0,y=786432,z=0,hh=0,vv=1536)
+2908: y0 786432 v:=24248320+786432=25034752, vv:=1586
+2909: push
+level 1:(h=0,v=25034752,w=0,x=0,y=786432,z=0,hh=0,vv=1586)
+2910: set2 12354("3042) type=0 h:=0+630598=630598, hh:=40
+2913: set2 65289("FF09) type=1 h:=630598+330310=960908, hh:=61
+2916: right3 300288 h:=960908+300288=1261196, hh:=80
+2920: set2 12354("3042) type=0 h:=1261196+630598=1891794, hh:=120
+[あ) あ]
+2923: pop
+level 1:(h=0,v=25034752,w=0,x=0,y=786432,z=0,hh=0,vv=1586)
+2924: y0 786432 v:=25034752+786432=25821184, vv:=1636
+2925: push
+level 1:(h=0,v=25821184,w=0,x=0,y=786432,z=0,hh=0,vv=1636)
+2926: set2 12354("3042) type=0 h:=0+630598=630598, hh:=40
+2929: set2 65289("FF09) type=1 h:=630598+330310=960908, hh:=61
+2932: set2 12354("3042) type=0 h:=960908+630598=1591506, hh:=101
+[あ)あ]
+2935: pop
+level 1:(h=0,v=25821184,w=0,x=0,y=786432,z=0,hh=0,vv=1636)
+2936: y0 786432 v:=25821184+786432=26607616, vv:=1685
+2937: push
+level 1:(h=0,v=26607616,w=0,x=0,y=786432,z=0,hh=0,vv=1685)
+2938: set2 12354("3042) type=0 h:=0+630598=630598, hh:=40
+2941: set2 65289("FF09) type=1 h:=630598+330310=960908, hh:=61
+2944: set2 12354("3042) type=0 h:=960908+630598=1591506, hh:=101
+[あ)あ]
+2947: pop
+level 1:(h=0,v=26607616,w=0,x=0,y=786432,z=0,hh=0,vv=1685)
+2948: y0 786432 v:=26607616+786432=27394048, vv:=1735
+2949: push
+level 1:(h=0,v=27394048,w=0,x=0,y=786432,z=0,hh=0,vv=1735)
+2950: set2 12354("3042) type=0 h:=0+630598=630598, hh:=40
+2953: set2 65289("FF09) type=1 h:=630598+330310=960908, hh:=61
+2956: right3 300288 h:=960908+300288=1261196, hh:=80
+2960: set2 12354("3042) type=0 h:=1261196+630598=1891794, hh:=120
+[あ) あ]
+2963: pop
+level 1:(h=0,v=27394048,w=0,x=0,y=786432,z=0,hh=0,vv=1735)
+2964: y0 786432 v:=27394048+786432=28180480, vv:=1785
+2965: push
+level 1:(h=0,v=28180480,w=0,x=0,y=786432,z=0,hh=0,vv=1785)
+2966: set2 12354("3042) type=0 h:=0+630598=630598, hh:=40
+2969: set2 65289("FF09) type=1 h:=630598+330310=960908, hh:=61
+2972: set2 12354("3042) type=0 h:=960908+630598=1591506, hh:=101
+[あ)あ]
+2975: pop
+level 1:(h=0,v=28180480,w=0,x=0,y=786432,z=0,hh=0,vv=1785)
+2976: y0 786432 v:=28180480+786432=28966912, vv:=1835
+2977: push
+level 1:(h=0,v=28966912,w=0,x=0,y=786432,z=0,hh=0,vv=1835)
+2978: down3 91000 v:=28966912+91000=29057912, vv:=1841
+2982: setrule height 600600, width 26214 (39x2 pixels)
+ h:=0+26214=26214, hh:=2
+2991: down3 -91000 v:=29057912-91000=28966912, vv:=1835
+2995: set2 65307("FF1B) type=4 h:=26214+231778=257992, hh:=17
+2998: right3 199410 h:=257992+199410=457402, hh:=29
+3002: set2 12354("3042) type=0 h:=457402+630598=1088000, hh:=69
+3005: right3 300288 h:=1088000+300288=1388288, hh:=88
+3009: set2 65288("FF08) type=6 h:=1388288+330310=1718598, hh:=109
+[; あ (]
+3012: pop
+level 1:(h=0,v=28966912,w=0,x=0,y=786432,z=0,hh=0,vv=1835)
+3013: pop
+level 0:(h=0,v=42152922,w=0,x=0,y=0,z=0,hh=0,vv=2670)
+3014: down3 1572864 v:=42152922+1572864=43725786, vv:=2770
+3018: eop
diff --git a/Build/source/texk/web2c/euptexdir/tests/eup-jctrlsyma.typ b/Build/source/texk/web2c/euptexdir/tests/eup-jctrlsyma.typ
new file mode 100644
index 00000000000..27132f1cea0
--- /dev/null
+++ b/Build/source/texk/web2c/euptexdir/tests/eup-jctrlsyma.typ
@@ -0,0 +1,121 @@
+Options selected:
+ Starting page = *
+ Maximum number of pages = 1000000
+ Output level = 4 (the works)
+ Resolution = 300.00000000 pixels per inch
+numerator/denominator=25400000/473628672
+magnification=1000; 0.00006334 pixels per DVI unit
+' TeX output 20XX.XX.XX'
+Postamble starts at byte 287.
+maxv=43725786, maxh=30785863, maxstackdepth=2, totalpages=1
+Font 1: umin10 (JFM)---loaded at size 655360 DVI units
+Font 0: cmr10---loaded at size 655360 DVI units
+
+42: beginning of page 1
+87: push
+level 0:(h=0,v=0,w=0,x=0,y=0,z=0,hh=0,vv=0)
+88: down3 -917504 v:=0-917504=-917504, vv:=-58
+92: pop
+level 0:(h=0,v=0,w=0,x=0,y=0,z=0,hh=0,vv=0)
+93: down4 42152922 v:=0+42152922=42152922, vv:=2670
+98: push
+level 0:(h=0,v=42152922,w=0,x=0,y=0,z=0,hh=0,vv=2670)
+99: down4 -41497562 v:=42152922-41497562=655360, vv:=42
+104: push
+level 1:(h=0,v=655360,w=0,x=0,y=0,z=0,hh=0,vv=42)
+105: right3 1310720 h:=0+1310720=1310720, hh:=83
+[ ]
+109: fntdef1 0: cmr10
+130: fntnum0 current font is cmr10
+131: setchar49 ("31) h:=1310720+327681=1638401, hh:=104
+132: setchar46 ("2E) h:=1638401+182045=1820446, hh:=116
+133: right3 291271 h:=1820446+291271=2111717, hh:=134
+[1. ]
+137: fntdef1 1: umin10
+159: fntnum1 current font is umin10
+160: set2 9670("25C6) type=0 h:=2111717+630598=2742315, hh:=174
+163: set2 9671("25C7) type=0 h:=2742315+630598=3372913, hh:=214
+166: set2 9670("25C6) type=0 h:=3372913+630598=4003511, hh:=254
+169: set2 9671("25C7) type=0 h:=4003511+630598=4634109, hh:=294
+[◆◇◆◇]
+172: pop
+level 1:(h=0,v=655360,w=0,x=0,y=0,z=0,hh=0,vv=42)
+173: y3 786432 v:=655360+786432=1441792, vv:=91
+177: push
+level 1:(h=0,v=1441792,w=0,x=0,y=786432,z=0,hh=0,vv=91)
+178: right3 1310720 h:=0+1310720=1310720, hh:=83
+[ ]
+182: fntnum0 current font is cmr10
+183: setchar50 ("32) h:=1310720+327681=1638401, hh:=104
+184: setchar46 ("2E) h:=1638401+182045=1820446, hh:=116
+185: right3 291271 h:=1820446+291271=2111717, hh:=134
+[2. ]
+189: fntnum1 current font is umin10
+190: set2 9670("25C6) type=0 h:=2111717+630598=2742315, hh:=174
+193: set2 9671("25C7) type=0 h:=2742315+630598=3372913, hh:=214
+[◆◇]
+196: fntnum0 current font is cmr10
+197: setchar44 ("2C) h:=3372913+182045=3554958, hh:=226
+198: right3 218453 h:=3554958+218453=3773411, hh:=239
+[, ]
+202: fntnum1 current font is umin10
+203: set2 9670("25C6) type=0 h:=3773411+630598=4404009, hh:=279
+206: set2 9671("25C7) type=0 h:=4404009+630598=5034607, hh:=319
+[◆◇]
+209: pop
+level 1:(h=0,v=1441792,w=0,x=0,y=786432,z=0,hh=0,vv=91)
+210: y0 786432 v:=1441792+786432=2228224, vv:=141
+211: push
+level 1:(h=0,v=2228224,w=0,x=0,y=786432,z=0,hh=0,vv=141)
+212: right3 1310720 h:=0+1310720=1310720, hh:=83
+[ ]
+216: fntnum0 current font is cmr10
+217: setchar51 ("33) h:=1310720+327681=1638401, hh:=104
+218: setchar46 ("2E) h:=1638401+182045=1820446, hh:=116
+219: right3 291271 h:=1820446+291271=2111717, hh:=134
+[3. ]
+223: fntnum1 current font is umin10
+224: set2 9670("25C6) type=0 h:=2111717+630598=2742315, hh:=174
+227: set2 9671("25C7) type=0 h:=2742315+630598=3372913, hh:=214
+[◆◇]
+230: fntnum0 current font is cmr10
+231: setchar44 ("2C) h:=3372913+182045=3554958, hh:=226
+232: w3 218453 h:=3554958+218453=3773411, hh:=239
+[, ]
+236: fntnum1 current font is umin10
+237: set2 9670("25C6) type=0 h:=3773411+630598=4404009, hh:=279
+240: w0 218453 h:=4404009+218453=4622462, hh:=293
+241: set2 9671("25C7) type=0 h:=4622462+630598=5253060, hh:=333
+[◆ ◇]
+244: pop
+level 1:(h=0,v=2228224,w=0,x=0,y=786432,z=0,hh=0,vv=141)
+245: y0 786432 v:=2228224+786432=3014656, vv:=191
+246: push
+level 1:(h=0,v=3014656,w=0,x=0,y=786432,z=0,hh=0,vv=191)
+247: right3 1310720 h:=0+1310720=1310720, hh:=83
+[ ]
+251: fntnum0 current font is cmr10
+252: setchar52 ("34) h:=1310720+327681=1638401, hh:=104
+253: setchar46 ("2E) h:=1638401+182045=1820446, hh:=116
+254: right3 291271 h:=1820446+291271=2111717, hh:=134
+[4. ]
+258: fntnum1 current font is umin10
+259: set2 9670("25C6) type=0 h:=2111717+630598=2742315, hh:=174
+262: w3 218453 h:=2742315+218453=2960768, hh:=188
+266: set2 9671("25C7) type=0 h:=2960768+630598=3591366, hh:=228
+[◆ ◇]
+269: fntnum0 current font is cmr10
+270: setchar44 ("2C) h:=3591366+182045=3773411, hh:=240
+271: w0 218453 h:=3773411+218453=3991864, hh:=253
+[, ]
+272: fntnum1 current font is umin10
+273: set2 9670("25C6) type=0 h:=3991864+630598=4622462, hh:=293
+276: w0 218453 h:=4622462+218453=4840915, hh:=307
+277: set2 9671("25C7) type=0 h:=4840915+630598=5471513, hh:=347
+[◆ ◇]
+280: pop
+level 1:(h=0,v=3014656,w=0,x=0,y=786432,z=0,hh=0,vv=191)
+281: pop
+level 0:(h=0,v=42152922,w=0,x=0,y=0,z=0,hh=0,vv=2670)
+282: down3 1572864 v:=42152922+1572864=43725786, vv:=2770
+286: eop
diff --git a/Build/source/texk/web2c/euptexdir/tests/eup-pknj-ea.typ b/Build/source/texk/web2c/euptexdir/tests/eup-pknj-ea.typ
new file mode 100644
index 00000000000..cf8d08f7715
--- /dev/null
+++ b/Build/source/texk/web2c/euptexdir/tests/eup-pknj-ea.typ
@@ -0,0 +1,825 @@
+Options selected:
+ Starting page = *
+ Maximum number of pages = 1000000
+ Output level = 4 (the works)
+ Resolution = 300.00000000 pixels per inch
+numerator/denominator=25400000/473628672
+magnification=1000; 0.00006334 pixels per DVI unit
+' TeX output 20XX.XX.XX'
+Postamble starts at byte 1055.
+maxv=43725786, maxh=30785863, maxstackdepth=3, totalpages=1
+Font 1: min10 (JFM)---loaded at size 655360 DVI units
+Font 0: ec-lmr10---loaded at size 655360 DVI units
+
+42: beginning of page 50623
+87: push
+level 0:(h=0,v=0,w=0,x=0,y=0,z=0,hh=0,vv=0)
+88: down3 -917504 v:=0-917504=-917504, vv:=-58
+92: pop
+level 0:(h=0,v=0,w=0,x=0,y=0,z=0,hh=0,vv=0)
+93: down4 42152922 v:=0+42152922=42152922, vv:=2670
+98: push
+level 0:(h=0,v=42152922,w=0,x=0,y=0,z=0,hh=0,vv=2670)
+99: down4 -41497562 v:=42152922-41497562=655360, vv:=42
+104: push
+level 1:(h=0,v=655360,w=0,x=0,y=0,z=0,hh=0,vv=42)
+105: right3 1310720 h:=0+1310720=1310720, hh:=83
+[ ]
+109: fntdef1 0: ec-lmr10
+133: fntnum0 current font is ec-lmr10
+134: setchar65 ("41) h:=1310720+491520=1802240, hh:=114
+135: setchar58 ("3A) h:=1802240+182043=1984283, hh:=126
+136: w3 218453 h:=1984283+218453=2202736, hh:=140
+[A: ]
+140: set1 197("C5) h:=2202736+491520=2694256, hh:=171
+142: set1 191("BF) h:=2694256+491520=3185776, hh:=202
+144: w0 218453 h:=3185776+218453=3404229, hh:=216
+[ ]
+145: set1 197("C5) h:=3404229+491520=3895749, hh:=247
+147: set1 191("BF) h:=3895749+491520=4387269, hh:=278
+149: w0 218453 h:=4387269+218453=4605722, hh:=292
+[ ]
+150: fntdef1 1: min10
+171: fntnum1 current font is min10
+172: set2 17727("453F) type=0 h:=4605722+630598=5236320, hh:=332
+[䔿]
+175: pop
+level 1:(h=0,v=655360,w=0,x=0,y=0,z=0,hh=0,vv=42)
+176: y3 786432 v:=655360+786432=1441792, vv:=91
+180: push
+level 1:(h=0,v=1441792,w=0,x=0,y=786432,z=0,hh=0,vv=91)
+181: push
+level 2:(h=0,v=1441792,w=0,x=0,y=786432,z=0,hh=0,vv=91)
+182: set2 8739("2223) type=0 h:=0+630598=630598, hh:=40
+[∣]
+185: pop
+level 2:(h=0,v=1441792,w=0,x=0,y=786432,z=0,hh=0,vv=91)
+186: right3 630598 h:=0+630598=630598, hh:=40
+[ ]
+190: fntnum0 current font is ec-lmr10
+191: setchar92 ("5C) h:=630598+327680=958278, hh:=61
+192: setchar109 ("6D) h:=958278+546111=1504389, hh:=96
+193: setchar101 ("65) h:=1504389+291275=1795664, hh:=114
+194: setchar97 ("61) h:=1795664+327680=2123344, hh:=135
+195: setchar110 ("6E) h:=2123344+364085=2487429, hh:=158
+196: setchar105 ("69) h:=2487429+182043=2669472, hh:=170
+197: setchar110 ("6E) h:=2669472+364085=3033557, hh:=193
+198: setchar103 ("67) h:=3033557+327680=3361237, hh:=214
+199: setchar46 ("2E) h:=3361237+182043=3543280, hh:=226
+[\meaning.]
+200: pop
+level 1:(h=0,v=1441792,w=0,x=0,y=786432,z=0,hh=0,vv=91)
+201: y0 786432 v:=1441792+786432=2228224, vv:=141
+202: push
+level 1:(h=0,v=2228224,w=0,x=0,y=786432,z=0,hh=0,vv=141)
+203: right3 1310720 h:=0+1310720=1310720, hh:=83
+207: setchar109 ("6D) h:=1310720+546111=1856831, hh:=118
+208: setchar97 ("61) h:=1856831+327680=2184511, hh:=139
+209: setchar99 ("63) h:=2184511+291275=2475786, hh:=157
+210: setchar114 ("72) h:=2475786+256683=2732469, hh:=173
+211: setchar111 ("6F) h:=2732469+327680=3060149, hh:=194
+212: setchar58 ("3A) h:=3060149+182043=3242192, hh:=206
+213: setchar45 ("2D) h:=3242192+218453=3460645, hh:=220
+214: setchar62 ("3E) h:=3460645+509738=3970383, hh:=252
+215: setchar109 ("6D) h:=3970383+546111=4516494, hh:=287
+216: setchar97 ("61) h:=4516494+327680=4844174, hh:=308
+217: setchar99 ("63) h:=4844174+291275=5135449, hh:=326
+218: setchar114 ("72) h:=5135449+256683=5392132, hh:=342
+219: setchar111 ("6F) h:=5392132+327680=5719812, hh:=363
+220: setchar58 ("3A) h:=5719812+182043=5901855, hh:=375
+221: setchar45 ("2D) h:=5901855+218453=6120308, hh:=389
+222: setchar62 ("3E) h:=6120308+509738=6630046, hh:=421
+[ macro:->macro:->]
+223: set1 197("C5) h:=6630046+491520=7121566, hh:=452
+225: set1 191("BF) h:=7121566+491520=7613086, hh:=483
+227: w3 218453 h:=7613086+218453=7831539, hh:=496
+[ ]
+231: set1 197("C5) h:=7831539+491520=8323059, hh:=527
+233: set1 191("BF) h:=8323059+491520=8814579, hh:=558
+235: w0 218453 h:=8814579+218453=9033032, hh:=572
+[ ]
+236: fntnum1 current font is min10
+237: set2 17727("453F) type=0 h:=9033032+630598=9663630, hh:=612
+[䔿]
+240: pop
+level 1:(h=0,v=2228224,w=0,x=0,y=786432,z=0,hh=0,vv=141)
+241: y0 786432 v:=2228224+786432=3014656, vv:=191
+242: push
+level 1:(h=0,v=3014656,w=0,x=0,y=786432,z=0,hh=0,vv=191)
+243: right3 1310720 h:=0+1310720=1310720, hh:=83
+[ ]
+247: fntnum0 current font is ec-lmr10
+248: setchar109 ("6D) h:=1310720+546111=1856831, hh:=118
+249: setchar97 ("61) h:=1856831+327680=2184511, hh:=139
+250: setchar99 ("63) h:=2184511+291275=2475786, hh:=157
+251: setchar114 ("72) h:=2475786+256683=2732469, hh:=173
+252: setchar111 ("6F) h:=2732469+327680=3060149, hh:=194
+253: setchar58 ("3A) h:=3060149+182043=3242192, hh:=206
+254: setchar45 ("2D) h:=3242192+218453=3460645, hh:=220
+255: setchar62 ("3E) h:=3460645+509738=3970383, hh:=252
+[macro:->]
+256: set1 197("C5) h:=3970383+491520=4461903, hh:=283
+258: set1 191("BF) h:=4461903+491520=4953423, hh:=314
+260: w3 218453 h:=4953423+218453=5171876, hh:=328
+[ ]
+264: set1 197("C5) h:=5171876+491520=5663396, hh:=359
+266: set1 191("BF) h:=5663396+491520=6154916, hh:=390
+268: w0 218453 h:=6154916+218453=6373369, hh:=404
+[ ]
+269: fntnum1 current font is min10
+270: set2 17727("453F) type=0 h:=6373369+630598=7003967, hh:=444
+[䔿]
+273: pop
+level 1:(h=0,v=3014656,w=0,x=0,y=786432,z=0,hh=0,vv=191)
+274: y0 786432 v:=3014656+786432=3801088, vv:=241
+275: push
+level 1:(h=0,v=3801088,w=0,x=0,y=786432,z=0,hh=0,vv=241)
+276: push
+level 2:(h=0,v=3801088,w=0,x=0,y=786432,z=0,hh=0,vv=241)
+277: set2 8739("2223) type=0 h:=0+630598=630598, hh:=40
+[∣]
+280: pop
+level 2:(h=0,v=3801088,w=0,x=0,y=786432,z=0,hh=0,vv=241)
+281: right3 630598 h:=0+630598=630598, hh:=40
+[ ]
+285: fntnum0 current font is ec-lmr10
+286: setchar92 ("5C) h:=630598+327680=958278, hh:=61
+287: setchar106 ("6A) h:=958278+200245=1158523, hh:=74
+288: setchar111 ("6F) h:=1158523+327680=1486203, hh:=95
+289: setchar98 ("62) h:=1486203+364085=1850288, hh:=118
+290: setchar110 ("6E) h:=1850288+364085=2214373, hh:=141
+291: setchar97 ("61) h:=2214373+327680=2542053, hh:=162
+292: setchar109 ("6D) h:=2542053+546111=3088164, hh:=197
+293: setchar101 ("65) h:=3088164+291275=3379439, hh:=215
+294: setchar46 ("2E) h:=3379439+182043=3561482, hh:=227
+[\jobname.]
+295: pop
+level 1:(h=0,v=3801088,w=0,x=0,y=786432,z=0,hh=0,vv=241)
+296: y0 786432 v:=3801088+786432=4587520, vv:=291
+297: push
+level 1:(h=0,v=4587520,w=0,x=0,y=786432,z=0,hh=0,vv=291)
+298: push
+level 2:(h=0,v=4587520,w=0,x=0,y=786432,z=0,hh=0,vv=291)
+299: fntnum1 current font is min10
+300: set2 8739("2223) type=0 h:=0+630598=630598, hh:=40
+[∣]
+303: pop
+level 2:(h=0,v=4587520,w=0,x=0,y=786432,z=0,hh=0,vv=291)
+304: right3 630598 h:=0+630598=630598, hh:=40
+[ ]
+308: fntnum0 current font is ec-lmr10
+309: setchar111 ("6F) h:=630598+327680=958278, hh:=61
+310: setchar110 ("6E) h:=958278+364085=1322363, hh:=84
+311: setchar101 ("65) h:=1322363+291275=1613638, hh:=102
+312: setchar108 ("6C) h:=1613638+182043=1795681, hh:=114
+313: setchar101 ("65) h:=1795681+291275=2086956, hh:=132
+314: setchar116 ("74) h:=2086956+254870=2341826, hh:=148
+315: setchar116 ("74) h:=2341826+254870=2596696, hh:=164
+316: setchar101 ("65) h:=2596696+291275=2887971, hh:=182
+317: setchar114 ("72) h:=2887971+256683=3144654, hh:=198
+318: setchar46 ("2E) h:=3144654+182043=3326697, hh:=210
+[oneletter.]
+319: pop
+level 1:(h=0,v=4587520,w=0,x=0,y=786432,z=0,hh=0,vv=291)
+320: y0 786432 v:=4587520+786432=5373952, vv:=340
+321: push
+level 1:(h=0,v=5373952,w=0,x=0,y=786432,z=0,hh=0,vv=340)
+322: right3 1310720 h:=0+1310720=1310720, hh:=83
+326: setchar92 ("5C) h:=1310720+327680=1638400, hh:=104
+[ \]
+327: set1 196("C4) h:=1638400+491520=2129920, hh:=135
+329: setchar46 ("2E) h:=2129920+182043=2311963, hh:=147
+330: setchar92 ("5C) h:=2311963+327680=2639643, hh:=168
+[.\]
+331: set1 197("C5) h:=2639643+491520=3131163, hh:=199
+333: setchar46 ("2E) h:=3131163+182043=3313206, hh:=211
+334: setchar92 ("5C) h:=3313206+327680=3640886, hh:=232
+[.\]
+335: set1 255("FF) h:=3640886+327680=3968566, hh:=253
+337: setchar46 ("2E) h:=3968566+182043=4150609, hh:=265
+338: right3 291271 h:=4150609+291271=4441880, hh:=281
+[. ]
+342: set1 191("BF) h:=4441880+491520=4933400, hh:=312
+344: pop
+level 1:(h=0,v=5373952,w=0,x=0,y=786432,z=0,hh=0,vv=340)
+345: y0 786432 v:=5373952+786432=6160384, vv:=390
+346: push
+level 1:(h=0,v=6160384,w=0,x=0,y=786432,z=0,hh=0,vv=390)
+347: push
+level 2:(h=0,v=6160384,w=0,x=0,y=786432,z=0,hh=0,vv=390)
+348: fntnum1 current font is min10
+349: set2 8739("2223) type=0 h:=0+630598=630598, hh:=40
+[∣]
+352: pop
+level 2:(h=0,v=6160384,w=0,x=0,y=786432,z=0,hh=0,vv=390)
+353: right3 630598 h:=0+630598=630598, hh:=40
+[ ]
+357: fntnum0 current font is ec-lmr10
+358: setchar99 ("63) h:=630598+291275=921873, hh:=58
+359: setchar115 ("73) h:=921873+258506=1180379, hh:=74
+360: setchar110 ("6E) h:=1180379+364085=1544464, hh:=97
+361: setchar97 ("61) h:=1544464+327680=1872144, hh:=118
+362: setchar109 ("6D) h:=1872144+546111=2418255, hh:=153
+363: setchar101 ("65) h:=2418255+291275=2709530, hh:=171
+364: setchar49 ("31) h:=2709530+327680=3037210, hh:=192
+365: setchar46 ("2E) h:=3037210+182043=3219253, hh:=204
+[csname1.]
+366: pop
+level 1:(h=0,v=6160384,w=0,x=0,y=786432,z=0,hh=0,vv=390)
+367: y0 786432 v:=6160384+786432=6946816, vv:=440
+368: push
+level 1:(h=0,v=6946816,w=0,x=0,y=786432,z=0,hh=0,vv=440)
+369: right3 1310720 h:=0+1310720=1310720, hh:=83
+373: setchar92 ("5C) h:=1310720+327680=1638400, hh:=104
+[ \]
+374: set1 197("C5) h:=1638400+491520=2129920, hh:=135
+376: set1 191("BF) h:=2129920+491520=2621440, hh:=166
+378: setchar92 ("5C) h:=2621440+327680=2949120, hh:=187
+[\]
+379: set1 197("C5) h:=2949120+491520=3440640, hh:=218
+381: set1 191("BF) h:=3440640+491520=3932160, hh:=249
+383: setchar44 ("2C) h:=3932160+182043=4114203, hh:=261
+384: w3 218453 h:=4114203+218453=4332656, hh:=274
+388: setchar92 ("5C) h:=4332656+327680=4660336, hh:=295
+[, \]
+389: set1 197("C5) h:=4660336+491520=5151856, hh:=326
+391: set1 191("BF) h:=5151856+491520=5643376, hh:=357
+393: setchar44 ("2C) h:=5643376+182043=5825419, hh:=369
+394: w0 218453 h:=5825419+218453=6043872, hh:=383
+395: setchar92 ("5C) h:=6043872+327680=6371552, hh:=404
+[, \]
+396: fntnum1 current font is min10
+397: set2 17727("453F) type=0 h:=6371552+630598=7002150, hh:=444
+[䔿]
+400: pop
+level 1:(h=0,v=6946816,w=0,x=0,y=786432,z=0,hh=0,vv=440)
+401: y0 786432 v:=6946816+786432=7733248, vv:=490
+402: push
+level 1:(h=0,v=7733248,w=0,x=0,y=786432,z=0,hh=0,vv=490)
+403: right3 1310720 h:=0+1310720=1310720, hh:=83
+[ ]
+407: fntnum0 current font is ec-lmr10
+408: setchar92 ("5C) h:=1310720+327680=1638400, hh:=104
+[\]
+409: fntnum1 current font is min10
+410: set2 9250("2422) type=0 h:=1638400+630598=2268998, hh:=144
+[␢]
+413: fntnum0 current font is ec-lmr10
+414: setchar61 ("3D) h:=2268998+509738=2778736, hh:=176
+415: setchar62 ("3E) h:=2778736+509738=3288474, hh:=208
+416: right3 218453 h:=3288474+218453=3506927, hh:=222
+420: setchar104 ("68) h:=3506927+364085=3871012, hh:=245
+421: setchar111 ("6F) h:=3871012+327680=4198692, hh:=266
+422: setchar103 ("67) h:=4198692+327680=4526372, hh:=287
+423: setchar101 ("65) h:=4526372+291275=4817647, hh:=305
+424: setchar44 ("2C) h:=4817647+182043=4999690, hh:=317
+[=> hoge,]
+425: pop
+level 1:(h=0,v=7733248,w=0,x=0,y=786432,z=0,hh=0,vv=490)
+426: y0 786432 v:=7733248+786432=8519680, vv:=540
+427: push
+level 1:(h=0,v=8519680,w=0,x=0,y=786432,z=0,hh=0,vv=540)
+428: right3 1310720 h:=0+1310720=1310720, hh:=83
+432: setchar92 ("5C) h:=1310720+327680=1638400, hh:=104
+[ \]
+433: set1 164("A4) h:=1638400+364085=2002485, hh:=127
+435: set1 162("A2) h:=2002485+291275=2293760, hh:=145
+437: setchar61 ("3D) h:=2293760+509738=2803498, hh:=177
+438: setchar62 ("3E) h:=2803498+509738=3313236, hh:=209
+439: right3 218453 h:=3313236+218453=3531689, hh:=224
+443: setchar112 ("70) h:=3531689+364085=3895774, hh:=247
+444: setchar105 ("69) h:=3895774+182043=4077817, hh:=259
+445: setchar121 ("79) h:=4077817+345898=4423715, hh:=281
+446: right2 -18205 h:=4423715-18205=4405510, hh:=280
+449: setchar111 ("6F) h:=4405510+327680=4733190, hh:=301
+450: setchar44 ("2C) h:=4733190+182043=4915233, hh:=313
+[=> piyo,]
+451: pop
+level 1:(h=0,v=8519680,w=0,x=0,y=786432,z=0,hh=0,vv=540)
+452: y0 786432 v:=8519680+786432=9306112, vv:=589
+453: push
+level 1:(h=0,v=9306112,w=0,x=0,y=786432,z=0,hh=0,vv=589)
+454: right3 1310720 h:=0+1310720=1310720, hh:=83
+458: setchar92 ("5C) h:=1310720+327680=1638400, hh:=104
+[ \]
+459: set1 164("A4) h:=1638400+364085=2002485, hh:=127
+461: set1 162("A2) h:=2002485+291275=2293760, hh:=145
+463: setchar61 ("3D) h:=2293760+509738=2803498, hh:=177
+464: setchar62 ("3E) h:=2803498+509738=3313236, hh:=209
+465: right3 218453 h:=3313236+218453=3531689, hh:=224
+469: setchar112 ("70) h:=3531689+364085=3895774, hh:=247
+470: setchar105 ("69) h:=3895774+182043=4077817, hh:=259
+471: setchar121 ("79) h:=4077817+345898=4423715, hh:=281
+472: right2 -18205 h:=4423715-18205=4405510, hh:=280
+475: setchar111 ("6F) h:=4405510+327680=4733190, hh:=301
+476: setchar46 ("2E) h:=4733190+182043=4915233, hh:=313
+[=> piyo.]
+477: pop
+level 1:(h=0,v=9306112,w=0,x=0,y=786432,z=0,hh=0,vv=589)
+478: y0 786432 v:=9306112+786432=10092544, vv:=639
+479: push
+level 1:(h=0,v=10092544,w=0,x=0,y=786432,z=0,hh=0,vv=639)
+480: push
+level 2:(h=0,v=10092544,w=0,x=0,y=786432,z=0,hh=0,vv=639)
+481: fntnum1 current font is min10
+482: set2 8739("2223) type=0 h:=0+630598=630598, hh:=40
+[∣]
+485: pop
+level 2:(h=0,v=10092544,w=0,x=0,y=786432,z=0,hh=0,vv=639)
+486: right3 630598 h:=0+630598=630598, hh:=40
+[ ]
+490: fntnum0 current font is ec-lmr10
+491: setchar48 ("30) h:=630598+327680=958278, hh:=61
+492: setchar120 ("78) h:=958278+345898=1304176, hh:=83
+493: setchar70 ("46) h:=1304176+427818=1731994, hh:=110
+494: setchar70 ("46) h:=1731994+427818=2159812, hh:=137
+495: setchar46 ("2E) h:=2159812+182043=2341855, hh:=149
+[0xFF.]
+496: pop
+level 1:(h=0,v=10092544,w=0,x=0,y=786432,z=0,hh=0,vv=639)
+497: y0 786432 v:=10092544+786432=10878976, vv:=689
+498: push
+level 1:(h=0,v=10878976,w=0,x=0,y=786432,z=0,hh=0,vv=689)
+499: right3 1310720 h:=0+1310720=1310720, hh:=83
+[ ]
+503: fntnum1 current font is min10
+504: set2 9250("2422) type=0 h:=1310720+630598=1941318, hh:=123
+[␢]
+507: fntnum0 current font is ec-lmr10
+508: setchar44 ("2C) h:=1941318+182043=2123361, hh:=135
+509: w3 218453 h:=2123361+218453=2341814, hh:=148
+513: setchar40 ("28) h:=2341814+254870=2596684, hh:=164
+514: setchar92 ("5C) h:=2596684+327680=2924364, hh:=185
+[, (\]
+515: set1 255("FF) h:=2924364+327680=3252044, hh:=206
+517: set1 197("C5) h:=3252044+491520=3743564, hh:=237
+519: set1 255("FF) h:=3743564+327680=4071244, hh:=258
+521: set1 191("BF) h:=4071244+491520=4562764, hh:=289
+523: setchar58 ("3A) h:=4562764+182043=4744807, hh:=301
+524: setchar109 ("6D) h:=4744807+546111=5290918, hh:=336
+525: setchar97 ("61) h:=5290918+327680=5618598, hh:=357
+526: setchar99 ("63) h:=5618598+291275=5909873, hh:=375
+527: setchar114 ("72) h:=5909873+256683=6166556, hh:=391
+528: setchar111 ("6F) h:=6166556+327680=6494236, hh:=412
+529: setchar58 ("3A) h:=6494236+182043=6676279, hh:=424
+530: setchar45 ("2D) h:=6676279+218453=6894732, hh:=438
+531: setchar62 ("3E) h:=6894732+509738=7404470, hh:=470
+[:macro:->]
+532: fntnum1 current font is min10
+533: set2 9250("2422) type=0 h:=7404470+630598=8035068, hh:=510
+[␢]
+536: fntnum0 current font is ec-lmr10
+537: setchar41 ("29) h:=8035068+254870=8289938, hh:=526
+538: setchar44 ("2C) h:=8289938+182043=8471981, hh:=538
+539: w0 218453 h:=8471981+218453=8690434, hh:=550
+540: setchar92 ("5C) h:=8690434+327680=9018114, hh:=571
+[), \]
+541: set1 255("FF) h:=9018114+327680=9345794, hh:=592
+543: set1 197("C5) h:=9345794+491520=9837314, hh:=623
+545: set1 255("FF) h:=9837314+327680=10164994, hh:=644
+547: set1 191("BF) h:=10164994+491520=10656514, hh:=675
+549: setchar44 ("2C) h:=10656514+182043=10838557, hh:=687
+550: w0 218453 h:=10838557+218453=11057010, hh:=700
+551: setchar109 ("6D) h:=11057010+546111=11603121, hh:=735
+552: setchar97 ("61) h:=11603121+327680=11930801, hh:=756
+553: setchar99 ("63) h:=11930801+291275=12222076, hh:=774
+554: setchar114 ("72) h:=12222076+256683=12478759, hh:=790
+555: setchar111 ("6F) h:=12478759+327680=12806439, hh:=811
+556: setchar58 ("3A) h:=12806439+182043=12988482, hh:=823
+557: setchar45 ("2D) h:=12988482+218453=13206935, hh:=837
+558: setchar62 ("3E) h:=13206935+509738=13716673, hh:=869
+559: setchar40 ("28) h:=13716673+254870=13971543, hh:=885
+560: setchar92 ("5C) h:=13971543+327680=14299223, hh:=906
+[, macro:->(\]
+561: set1 255("FF) h:=14299223+327680=14626903, hh:=927
+563: set1 197("C5) h:=14626903+491520=15118423, hh:=958
+565: set1 255("FF) h:=15118423+327680=15446103, hh:=979
+567: set1 191("BF) h:=15446103+491520=15937623, hh:=1010
+569: setchar58 ("3A) h:=15937623+182043=16119666, hh:=1022
+570: setchar109 ("6D) h:=16119666+546111=16665777, hh:=1057
+571: setchar97 ("61) h:=16665777+327680=16993457, hh:=1078
+572: setchar99 ("63) h:=16993457+291275=17284732, hh:=1096
+573: setchar114 ("72) h:=17284732+256683=17541415, hh:=1112
+574: setchar111 ("6F) h:=17541415+327680=17869095, hh:=1133
+575: setchar58 ("3A) h:=17869095+182043=18051138, hh:=1145
+576: setchar45 ("2D) h:=18051138+218453=18269591, hh:=1159
+577: setchar62 ("3E) h:=18269591+509738=18779329, hh:=1191
+[:macro:->]
+578: fntnum1 current font is min10
+579: set2 9250("2422) type=0 h:=18779329+630598=19409927, hh:=1231
+[␢]
+582: fntnum0 current font is ec-lmr10
+583: setchar41 ("29) h:=19409927+254870=19664797, hh:=1247
+584: setchar46 ("2E) h:=19664797+182043=19846840, hh:=1259
+[).]
+585: pop
+level 1:(h=0,v=10878976,w=0,x=0,y=786432,z=0,hh=0,vv=689)
+586: y0 786432 v:=10878976+786432=11665408, vv:=739
+587: push
+level 1:(h=0,v=11665408,w=0,x=0,y=786432,z=0,hh=0,vv=739)
+588: right3 1310720 h:=0+1310720=1310720, hh:=83
+[ ]
+592: fntnum1 current font is min10
+593: set2 9252("2424) type=0 h:=1310720+630598=1941318, hh:=123
+[␤]
+596: fntnum0 current font is ec-lmr10
+597: setchar44 ("2C) h:=1941318+182043=2123361, hh:=135
+598: w3 218453 h:=2123361+218453=2341814, hh:=148
+602: setchar40 ("28) h:=2341814+254870=2596684, hh:=164
+603: setchar92 ("5C) h:=2596684+327680=2924364, hh:=185
+[, (\]
+604: set1 255("FF) h:=2924364+327680=3252044, hh:=206
+606: setchar58 ("3A) h:=3252044+182043=3434087, hh:=218
+607: setchar109 ("6D) h:=3434087+546111=3980198, hh:=253
+608: setchar97 ("61) h:=3980198+327680=4307878, hh:=274
+609: setchar99 ("63) h:=4307878+291275=4599153, hh:=292
+610: setchar114 ("72) h:=4599153+256683=4855836, hh:=308
+611: setchar111 ("6F) h:=4855836+327680=5183516, hh:=329
+612: setchar58 ("3A) h:=5183516+182043=5365559, hh:=341
+613: setchar45 ("2D) h:=5365559+218453=5584012, hh:=355
+614: setchar62 ("3E) h:=5584012+509738=6093750, hh:=387
+[:macro:->]
+615: fntnum1 current font is min10
+616: set2 9252("2424) type=0 h:=6093750+630598=6724348, hh:=427
+[␤]
+619: fntnum0 current font is ec-lmr10
+620: setchar41 ("29) h:=6724348+254870=6979218, hh:=443
+621: setchar44 ("2C) h:=6979218+182043=7161261, hh:=455
+622: w0 218453 h:=7161261+218453=7379714, hh:=467
+623: setchar92 ("5C) h:=7379714+327680=7707394, hh:=488
+[), \]
+624: set1 255("FF) h:=7707394+327680=8035074, hh:=509
+626: setchar44 ("2C) h:=8035074+182043=8217117, hh:=521
+627: w0 218453 h:=8217117+218453=8435570, hh:=534
+628: setchar109 ("6D) h:=8435570+546111=8981681, hh:=569
+629: setchar97 ("61) h:=8981681+327680=9309361, hh:=590
+630: setchar99 ("63) h:=9309361+291275=9600636, hh:=608
+631: setchar114 ("72) h:=9600636+256683=9857319, hh:=624
+632: setchar111 ("6F) h:=9857319+327680=10184999, hh:=645
+633: setchar58 ("3A) h:=10184999+182043=10367042, hh:=657
+634: setchar45 ("2D) h:=10367042+218453=10585495, hh:=671
+635: setchar62 ("3E) h:=10585495+509738=11095233, hh:=703
+636: setchar40 ("28) h:=11095233+254870=11350103, hh:=719
+637: setchar92 ("5C) h:=11350103+327680=11677783, hh:=740
+[, macro:->(\]
+638: set1 255("FF) h:=11677783+327680=12005463, hh:=761
+640: setchar58 ("3A) h:=12005463+182043=12187506, hh:=773
+641: setchar109 ("6D) h:=12187506+546111=12733617, hh:=808
+642: setchar97 ("61) h:=12733617+327680=13061297, hh:=829
+643: setchar99 ("63) h:=13061297+291275=13352572, hh:=847
+644: setchar114 ("72) h:=13352572+256683=13609255, hh:=863
+645: setchar111 ("6F) h:=13609255+327680=13936935, hh:=884
+646: setchar58 ("3A) h:=13936935+182043=14118978, hh:=896
+647: setchar45 ("2D) h:=14118978+218453=14337431, hh:=910
+648: setchar62 ("3E) h:=14337431+509738=14847169, hh:=942
+[:macro:->]
+649: fntnum1 current font is min10
+650: set2 9252("2424) type=0 h:=14847169+630598=15477767, hh:=982
+[␤]
+653: fntnum0 current font is ec-lmr10
+654: setchar41 ("29) h:=15477767+254870=15732637, hh:=998
+655: setchar46 ("2E) h:=15732637+182043=15914680, hh:=1010
+[).]
+656: pop
+level 1:(h=0,v=11665408,w=0,x=0,y=786432,z=0,hh=0,vv=739)
+657: y0 786432 v:=11665408+786432=12451840, vv:=789
+658: push
+level 1:(h=0,v=12451840,w=0,x=0,y=786432,z=0,hh=0,vv=789)
+659: push
+level 2:(h=0,v=12451840,w=0,x=0,y=786432,z=0,hh=0,vv=789)
+660: fntnum1 current font is min10
+661: set2 8739("2223) type=0 h:=0+630598=630598, hh:=40
+[∣]
+664: pop
+level 2:(h=0,v=12451840,w=0,x=0,y=786432,z=0,hh=0,vv=789)
+665: right3 630598 h:=0+630598=630598, hh:=40
+[ ]
+669: fntnum0 current font is ec-lmr10
+670: setchar99 ("63) h:=630598+291275=921873, hh:=58
+671: setchar111 ("6F) h:=921873+327680=1249553, hh:=79
+672: setchar110 ("6E) h:=1249553+364085=1613638, hh:=102
+673: right2 -18205 h:=1613638-18205=1595433, hh:=101
+676: setchar116 ("74) h:=1595433+254870=1850303, hh:=117
+677: setchar101 ("65) h:=1850303+291275=2141578, hh:=135
+678: setchar120 ("78) h:=2141578+345898=2487476, hh:=157
+679: setchar116 ("74) h:=2487476+254870=2742346, hh:=173
+680: setchar115 ("73) h:=2742346+258506=3000852, hh:=189
+681: setchar46 ("2E) h:=3000852+182043=3182895, hh:=201
+[contexts.]
+682: pop
+level 1:(h=0,v=12451840,w=0,x=0,y=786432,z=0,hh=0,vv=789)
+683: y0 786432 v:=12451840+786432=13238272, vv:=839
+684: push
+level 1:(h=0,v=13238272,w=0,x=0,y=786432,z=0,hh=0,vv=839)
+685: right3 1310720 h:=0+1310720=1310720, hh:=83
+[ ]
+689: fntnum1 current font is min10
+690: set2 9250("2422) type=0 h:=1310720+630598=1941318, hh:=123
+[␢]
+693: fntnum0 current font is ec-lmr10
+694: set1 197("C5) h:=1941318+491520=2432838, hh:=154
+696: set1 191("BF) h:=2432838+491520=2924358, hh:=185
+698: w3 218453 h:=2924358+218453=3142811, hh:=199
+702: setchar97 ("61) h:=3142811+327680=3470491, hh:=220
+[ a]
+703: set1 197("C5) h:=3470491+491520=3962011, hh:=251
+705: set1 191("BF) h:=3962011+491520=4453531, hh:=282
+707: w0 218453 h:=4453531+218453=4671984, hh:=296
+[ ]
+708: set1 197("C5) h:=4671984+491520=5163504, hh:=327
+710: set1 191("BF) h:=5163504+491520=5655024, hh:=358
+712: w0 218453 h:=5655024+218453=5873477, hh:=372
+[ ]
+713: fntnum1 current font is min10
+714: set2 17727("453F) type=0 h:=5873477+630598=6504075, hh:=412
+717: w0 218453 h:=6504075+218453=6722528, hh:=426
+[䔿 ]
+718: fntnum0 current font is ec-lmr10
+719: set1 197("C5) h:=6722528+491520=7214048, hh:=457
+721: set1 191("BF) h:=7214048+491520=7705568, hh:=488
+723: w0 218453 h:=7705568+218453=7924021, hh:=502
+[ ]
+724: set1 197("C5) h:=7924021+491520=8415541, hh:=533
+726: set1 191("BF) h:=8415541+491520=8907061, hh:=564
+728: w0 218453 h:=8907061+218453=9125514, hh:=578
+[ ]
+729: fntnum1 current font is min10
+730: set2 17727("453F) type=0 h:=9125514+630598=9756112, hh:=618
+733: x3 436906 h:=9756112+436906=10193018, hh:=646
+[䔿 ]
+737: fntnum0 current font is ec-lmr10
+738: set1 197("C5) h:=10193018+491520=10684538, hh:=677
+740: set1 191("BF) h:=10684538+491520=11176058, hh:=708
+742: w0 218453 h:=11176058+218453=11394511, hh:=722
+[ ]
+743: fntnum1 current font is min10
+744: set2 17727("453F) type=0 h:=11394511+630598=12025109, hh:=762
+747: x0 436906 h:=12025109+436906=12462015, hh:=789
+748: set2 9252("2424) type=0 h:=12462015+630598=13092613, hh:=829
+[䔿 ␤]
+751: fntnum0 current font is ec-lmr10
+752: setchar109 ("6D) h:=13092613+546111=13638724, hh:=864
+753: setchar97 ("61) h:=13638724+327680=13966404, hh:=885
+754: setchar99 ("63) h:=13966404+291275=14257679, hh:=903
+755: setchar114 ("72) h:=14257679+256683=14514362, hh:=919
+756: setchar111 ("6F) h:=14514362+327680=14842042, hh:=940
+757: setchar58 ("3A) h:=14842042+182043=15024085, hh:=952
+758: setchar45 ("2D) h:=15024085+218453=15242538, hh:=966
+759: setchar62 ("3E) h:=15242538+509738=15752276, hh:=998
+[macro:->]
+760: set1 197("C5) h:=15752276+491520=16243796, hh:=1029
+762: set1 191("BF) h:=16243796+491520=16735316, hh:=1060
+764: w0 218453 h:=16735316+218453=16953769, hh:=1074
+[ ]
+765: set1 197("C5) h:=16953769+491520=17445289, hh:=1105
+767: set1 191("BF) h:=17445289+491520=17936809, hh:=1136
+769: w0 218453 h:=17936809+218453=18155262, hh:=1150
+[ ]
+770: fntnum1 current font is min10
+771: set2 17727("453F) type=0 h:=18155262+630598=18785860, hh:=1190
+[䔿]
+774: pop
+level 1:(h=0,v=13238272,w=0,x=0,y=786432,z=0,hh=0,vv=839)
+775: y0 786432 v:=13238272+786432=14024704, vv:=888
+776: push
+level 1:(h=0,v=14024704,w=0,x=0,y=786432,z=0,hh=0,vv=888)
+777: right3 1310720 h:=0+1310720=1310720, hh:=83
+781: set2 9250("2422) type=0 h:=1310720+630598=1941318, hh:=123
+[ ␢]
+784: fntnum0 current font is ec-lmr10
+785: set1 197("C5) h:=1941318+491520=2432838, hh:=154
+787: set1 191("BF) h:=2432838+491520=2924358, hh:=185
+789: w3 218453 h:=2924358+218453=3142811, hh:=199
+[ ]
+793: set1 197("C5) h:=3142811+491520=3634331, hh:=230
+795: set1 191("BF) h:=3634331+491520=4125851, hh:=261
+797: fntnum1 current font is min10
+798: set2 17727("453F) type=0 h:=4125851+630598=4756449, hh:=301
+[䔿]
+801: fntnum0 current font is ec-lmr10
+802: set1 197("C5) h:=4756449+491520=5247969, hh:=332
+804: set1 191("BF) h:=5247969+491520=5739489, hh:=363
+806: w0 218453 h:=5739489+218453=5957942, hh:=377
+807: setchar97 ("61) h:=5957942+327680=6285622, hh:=398
+[ a]
+808: set1 197("C5) h:=6285622+491520=6777142, hh:=429
+810: set1 191("BF) h:=6777142+491520=7268662, hh:=460
+812: w0 218453 h:=7268662+218453=7487115, hh:=474
+[ ]
+813: set1 197("C5) h:=7487115+491520=7978635, hh:=505
+815: set1 191("BF) h:=7978635+491520=8470155, hh:=536
+817: w0 218453 h:=8470155+218453=8688608, hh:=550
+[ ]
+818: fntnum1 current font is min10
+819: set2 17727("453F) type=0 h:=8688608+630598=9319206, hh:=590
+822: w0 218453 h:=9319206+218453=9537659, hh:=604
+[䔿 ]
+823: fntnum0 current font is ec-lmr10
+824: set1 197("C5) h:=9537659+491520=10029179, hh:=635
+826: set1 191("BF) h:=10029179+491520=10520699, hh:=666
+828: w0 218453 h:=10520699+218453=10739152, hh:=680
+[ ]
+829: set1 197("C5) h:=10739152+491520=11230672, hh:=711
+831: set1 191("BF) h:=11230672+491520=11722192, hh:=742
+833: w0 218453 h:=11722192+218453=11940645, hh:=756
+[ ]
+834: fntnum1 current font is min10
+835: set2 17727("453F) type=0 h:=11940645+630598=12571243, hh:=796
+838: right3 436906 h:=12571243+436906=13008149, hh:=824
+[䔿 ]
+842: fntnum0 current font is ec-lmr10
+843: set1 197("C5) h:=13008149+491520=13499669, hh:=855
+845: set1 191("BF) h:=13499669+491520=13991189, hh:=886
+847: w0 218453 h:=13991189+218453=14209642, hh:=900
+[ ]
+848: fntnum1 current font is min10
+849: set2 17727("453F) type=0 h:=14209642+630598=14840240, hh:=940
+852: w0 218453 h:=14840240+218453=15058693, hh:=954
+853: set2 9252("2424) type=0 h:=15058693+630598=15689291, hh:=994
+[䔿 ␤]
+856: fntnum0 current font is ec-lmr10
+857: set1 255("FF) h:=15689291+327680=16016971, hh:=1015
+859: pop
+level 1:(h=0,v=14024704,w=0,x=0,y=786432,z=0,hh=0,vv=888)
+860: y0 786432 v:=14024704+786432=14811136, vv:=938
+861: push
+level 1:(h=0,v=14811136,w=0,x=0,y=786432,z=0,hh=0,vv=938)
+862: right3 1310720 h:=0+1310720=1310720, hh:=83
+[ ]
+866: fntnum1 current font is min10
+867: set2 9252("2424) type=0 h:=1310720+630598=1941318, hh:=123
+[␤]
+870: pop
+level 1:(h=0,v=14811136,w=0,x=0,y=786432,z=0,hh=0,vv=938)
+871: y0 786432 v:=14811136+786432=15597568, vv:=988
+872: push
+level 1:(h=0,v=15597568,w=0,x=0,y=786432,z=0,hh=0,vv=988)
+873: right3 1310720 h:=0+1310720=1310720, hh:=83
+[ ]
+877: fntnum0 current font is ec-lmr10
+878: setchar46 ("2E) h:=1310720+182043=1492763, hh:=95
+879: setchar97 ("61) h:=1492763+327680=1820443, hh:=116
+880: setchar46 ("2E) h:=1820443+182043=2002486, hh:=128
+881: setchar46 ("2E) h:=2002486+182043=2184529, hh:=140
+882: setchar46 ("2E) h:=2184529+182043=2366572, hh:=152
+883: setchar99 ("63) h:=2366572+291275=2657847, hh:=170
+884: setchar46 ("2E) h:=2657847+182043=2839890, hh:=182
+885: setchar98 ("62) h:=2839890+364085=3203975, hh:=205
+886: setchar46 ("2E) h:=3203975+182043=3386018, hh:=216
+887: setchar46 ("2E) h:=3386018+182043=3568061, hh:=228
+[.a...c.b..]
+888: pop
+level 1:(h=0,v=15597568,w=0,x=0,y=786432,z=0,hh=0,vv=988)
+889: y0 786432 v:=15597568+786432=16384000, vv:=1038
+890: push
+level 1:(h=0,v=16384000,w=0,x=0,y=786432,z=0,hh=0,vv=1038)
+891: right3 1310720 h:=0+1310720=1310720, hh:=83
+895: setchar46 ("2E) h:=1310720+182043=1492763, hh:=95
+896: setchar97 ("61) h:=1492763+327680=1820443, hh:=116
+897: setchar46 ("2E) h:=1820443+182043=2002486, hh:=128
+898: setchar46 ("2E) h:=2002486+182043=2184529, hh:=140
+899: setchar46 ("2E) h:=2184529+182043=2366572, hh:=152
+900: setchar99 ("63) h:=2366572+291275=2657847, hh:=170
+901: setchar46 ("2E) h:=2657847+182043=2839890, hh:=182
+902: setchar98 ("62) h:=2839890+364085=3203975, hh:=205
+903: setchar46 ("2E) h:=3203975+182043=3386018, hh:=216
+904: setchar46 ("2E) h:=3386018+182043=3568061, hh:=228
+[ .a...c.b..]
+905: pop
+level 1:(h=0,v=16384000,w=0,x=0,y=786432,z=0,hh=0,vv=1038)
+906: y0 786432 v:=16384000+786432=17170432, vv:=1088
+907: push
+level 1:(h=0,v=17170432,w=0,x=0,y=786432,z=0,hh=0,vv=1088)
+908: push
+level 2:(h=0,v=17170432,w=0,x=0,y=786432,z=0,hh=0,vv=1088)
+909: fntnum1 current font is min10
+910: set2 8739("2223) type=0 h:=0+630598=630598, hh:=40
+[∣]
+913: pop
+level 2:(h=0,v=17170432,w=0,x=0,y=786432,z=0,hh=0,vv=1088)
+914: right3 630598 h:=0+630598=630598, hh:=40
+[ ]
+918: fntnum0 current font is ec-lmr10
+919: setchar99 ("63) h:=630598+291275=921873, hh:=58
+920: setchar115 ("73) h:=921873+258506=1180379, hh:=74
+921: setchar110 ("6E) h:=1180379+364085=1544464, hh:=97
+922: setchar97 ("61) h:=1544464+327680=1872144, hh:=118
+923: setchar109 ("6D) h:=1872144+546111=2418255, hh:=153
+924: setchar101 ("65) h:=2418255+291275=2709530, hh:=171
+925: setchar50 ("32) h:=2709530+327680=3037210, hh:=192
+926: setchar46 ("2E) h:=3037210+182043=3219253, hh:=204
+[csname2.]
+927: pop
+level 1:(h=0,v=17170432,w=0,x=0,y=786432,z=0,hh=0,vv=1088)
+928: y0 786432 v:=17170432+786432=17956864, vv:=1137
+929: push
+level 1:(h=0,v=17956864,w=0,x=0,y=786432,z=0,hh=0,vv=1137)
+930: right3 1310720 h:=0+1310720=1310720, hh:=83
+934: setchar83 ("53) h:=1310720+364085=1674805, hh:=106
+935: setchar72 ("48) h:=1674805+491520=2166325, hh:=137
+936: setchar79 ("4F) h:=2166325+509738=2676063, hh:=169
+937: right2 -18205 h:=2676063-18205=2657858, hh:=168
+940: setchar87 ("57) h:=2657858+673578=3331436, hh:=211
+941: right3 218453 h:=3331436+218453=3549889, hh:=225
+945: setchar46 ("2E) h:=3549889+182043=3731932, hh:=237
+946: setchar46 ("2E) h:=3731932+182043=3913975, hh:=249
+947: setchar46 ("2E) h:=3913975+182043=4096018, hh:=261
+[ SHOW ...]
+948: pop
+level 1:(h=0,v=17956864,w=0,x=0,y=786432,z=0,hh=0,vv=1137)
+949: y0 786432 v:=17956864+786432=18743296, vv:=1187
+950: push
+level 1:(h=0,v=18743296,w=0,x=0,y=786432,z=0,hh=0,vv=1187)
+951: right3 1310720 h:=0+1310720=1310720, hh:=83
+955: setchar83 ("53) h:=1310720+364085=1674805, hh:=106
+956: setchar84 ("54) h:=1674805+473301=2148106, hh:=136
+957: setchar82 ("52) h:=2148106+482410=2630516, hh:=167
+958: setchar73 ("49) h:=2630516+236650=2867166, hh:=182
+959: setchar78 ("4E) h:=2867166+491520=3358686, hh:=213
+960: setchar71 ("47) h:=3358686+514261=3872947, hh:=246
+961: right3 218453 h:=3872947+218453=4091400, hh:=259
+965: setchar92 ("5C) h:=4091400+327680=4419080, hh:=280
+[ STRING \]
+966: set1 197("C5) h:=4419080+491520=4910600, hh:=311
+968: set1 191("BF) h:=4910600+491520=5402120, hh:=342
+970: setchar46 ("2E) h:=5402120+182043=5584163, hh:=354
+971: setchar92 ("5C) h:=5584163+327680=5911843, hh:=375
+[.\]
+972: fntnum1 current font is min10
+973: set2 17727("453F) type=0 h:=5911843+630598=6542441, hh:=415
+[䔿]
+976: fntnum0 current font is ec-lmr10
+977: setchar46 ("2E) h:=6542441+182043=6724484, hh:=427
+978: setchar92 ("5C) h:=6724484+327680=7052164, hh:=448
+[.\]
+979: set1 197("C5) h:=7052164+491520=7543684, hh:=479
+981: set1 191("BF) h:=7543684+491520=8035204, hh:=510
+983: setchar46 ("2E) h:=8035204+182043=8217247, hh:=522
+984: setchar92 ("5C) h:=8217247+327680=8544927, hh:=543
+[.\]
+985: fntnum1 current font is min10
+986: set2 17727("453F) type=0 h:=8544927+630598=9175525, hh:=583
+[䔿]
+989: pop
+level 1:(h=0,v=18743296,w=0,x=0,y=786432,z=0,hh=0,vv=1187)
+990: y0 786432 v:=18743296+786432=19529728, vv:=1237
+991: push
+level 1:(h=0,v=19529728,w=0,x=0,y=786432,z=0,hh=0,vv=1237)
+992: right3 1310720 h:=0+1310720=1310720, hh:=83
+[ ]
+996: fntnum0 current font is ec-lmr10
+997: setchar77 ("4D) h:=1310720+600768=1911488, hh:=121
+998: setchar69 ("45) h:=1911488+446005=2357493, hh:=149
+999: setchar65 ("41) h:=2357493+491520=2849013, hh:=180
+1000: setchar78 ("4E) h:=2849013+491520=3340533, hh:=211
+1001: setchar73 ("49) h:=3340533+236650=3577183, hh:=226
+1002: setchar78 ("4E) h:=3577183+491520=4068703, hh:=257
+1003: setchar71 ("47) h:=4068703+514261=4582964, hh:=290
+1004: right3 218453 h:=4582964+218453=4801417, hh:=304
+1008: setchar109 ("6D) h:=4801417+546111=5347528, hh:=339
+1009: setchar97 ("61) h:=5347528+327680=5675208, hh:=360
+1010: setchar99 ("63) h:=5675208+291275=5966483, hh:=378
+1011: setchar114 ("72) h:=5966483+256683=6223166, hh:=394
+1012: setchar111 ("6F) h:=6223166+327680=6550846, hh:=415
+1013: setchar58 ("3A) h:=6550846+182043=6732889, hh:=427
+1014: setchar45 ("2D) h:=6732889+218453=6951342, hh:=441
+1015: setchar62 ("3E) h:=6951342+509738=7461080, hh:=473
+1016: setchar97 ("61) h:=7461080+327680=7788760, hh:=494
+1017: setchar46 ("2E) h:=7788760+182043=7970803, hh:=506
+1018: setchar109 ("6D) h:=7970803+546111=8516914, hh:=541
+1019: setchar97 ("61) h:=8516914+327680=8844594, hh:=562
+1020: setchar99 ("63) h:=8844594+291275=9135869, hh:=580
+1021: setchar114 ("72) h:=9135869+256683=9392552, hh:=596
+1022: setchar111 ("6F) h:=9392552+327680=9720232, hh:=617
+1023: setchar58 ("3A) h:=9720232+182043=9902275, hh:=629
+1024: setchar45 ("2D) h:=9902275+218453=10120728, hh:=643
+1025: setchar62 ("3E) h:=10120728+509738=10630466, hh:=675
+1026: setchar98 ("62) h:=10630466+364085=10994551, hh:=698
+1027: setchar46 ("2E) h:=10994551+182043=11176594, hh:=710
+1028: setchar109 ("6D) h:=11176594+546111=11722705, hh:=745
+1029: setchar97 ("61) h:=11722705+327680=12050385, hh:=765
+1030: setchar99 ("63) h:=12050385+291275=12341660, hh:=783
+1031: setchar114 ("72) h:=12341660+256683=12598343, hh:=799
+1032: setchar111 ("6F) h:=12598343+327680=12926023, hh:=820
+1033: setchar58 ("3A) h:=12926023+182043=13108066, hh:=832
+1034: setchar45 ("2D) h:=13108066+218453=13326519, hh:=846
+1035: setchar62 ("3E) h:=13326519+509738=13836257, hh:=878
+1036: setchar97 ("61) h:=13836257+327680=14163937, hh:=899
+1037: setchar46 ("2E) h:=14163937+182043=14345980, hh:=911
+1038: setchar109 ("6D) h:=14345980+546111=14892091, hh:=945
+1039: setchar97 ("61) h:=14892091+327680=15219771, hh:=966
+1040: setchar99 ("63) h:=15219771+291275=15511046, hh:=984
+1041: setchar114 ("72) h:=15511046+256683=15767729, hh:=1000
+1042: setchar111 ("6F) h:=15767729+327680=16095409, hh:=1021
+1043: setchar58 ("3A) h:=16095409+182043=16277452, hh:=1033
+1044: setchar45 ("2D) h:=16277452+218453=16495905, hh:=1047
+1045: setchar62 ("3E) h:=16495905+509738=17005643, hh:=1079
+1046: setchar98 ("62) h:=17005643+364085=17369728, hh:=1102
+1047: setchar46 ("2E) h:=17369728+182043=17551771, hh:=1114
+[MEANING macro:->a.macro:->b.macro:->a.macro:->b.]
+1048: pop
+level 1:(h=0,v=19529728,w=0,x=0,y=786432,z=0,hh=0,vv=1237)
+1049: pop
+level 0:(h=0,v=42152922,w=0,x=0,y=0,z=0,hh=0,vv=2670)
+1050: down3 1572864 v:=42152922+1572864=43725786, vv:=2770
+1054: eop
diff --git a/Build/source/texk/web2c/euptexdir/tests/eup-pknj-sa.typ b/Build/source/texk/web2c/euptexdir/tests/eup-pknj-sa.typ
new file mode 100644
index 00000000000..9373f0bac64
--- /dev/null
+++ b/Build/source/texk/web2c/euptexdir/tests/eup-pknj-sa.typ
@@ -0,0 +1,829 @@
+Options selected:
+ Starting page = *
+ Maximum number of pages = 1000000
+ Output level = 4 (the works)
+ Resolution = 300.00000000 pixels per inch
+numerator/denominator=25400000/473628672
+magnification=1000; 0.00006334 pixels per DVI unit
+' TeX output 20XX.XX.XX'
+Postamble starts at byte 1061.
+maxv=43725786, maxh=30785863, maxstackdepth=3, totalpages=1
+Font 1: min10 (JFM)---loaded at size 655360 DVI units
+Font 0: ec-lmr10---loaded at size 655360 DVI units
+
+42: beginning of page 37726
+87: push
+level 0:(h=0,v=0,w=0,x=0,y=0,z=0,hh=0,vv=0)
+88: down3 -917504 v:=0-917504=-917504, vv:=-58
+92: pop
+level 0:(h=0,v=0,w=0,x=0,y=0,z=0,hh=0,vv=0)
+93: down4 42152922 v:=0+42152922=42152922, vv:=2670
+98: push
+level 0:(h=0,v=42152922,w=0,x=0,y=0,z=0,hh=0,vv=2670)
+99: down4 -41497562 v:=42152922-41497562=655360, vv:=42
+104: push
+level 1:(h=0,v=655360,w=0,x=0,y=0,z=0,hh=0,vv=42)
+105: right3 1310720 h:=0+1310720=1310720, hh:=83
+[ ]
+109: fntdef1 0: ec-lmr10
+133: fntnum0 current font is ec-lmr10
+134: setchar65 ("41) h:=1310720+491520=1802240, hh:=114
+135: setchar58 ("3A) h:=1802240+182043=1984283, hh:=126
+136: w3 218453 h:=1984283+218453=2202736, hh:=140
+[A: ]
+140: set1 197("C5) h:=2202736+491520=2694256, hh:=171
+142: set1 191("BF) h:=2694256+491520=3185776, hh:=202
+144: w0 218453 h:=3185776+218453=3404229, hh:=216
+[ ]
+145: set1 197("C5) h:=3404229+491520=3895749, hh:=247
+147: set1 191("BF) h:=3895749+491520=4387269, hh:=278
+149: w0 218453 h:=4387269+218453=4605722, hh:=292
+[ ]
+150: fntdef1 1: min10
+171: fntnum1 current font is min10
+172: set2 17727("453F) type=0 h:=4605722+630598=5236320, hh:=332
+[䔿]
+175: pop
+level 1:(h=0,v=655360,w=0,x=0,y=0,z=0,hh=0,vv=42)
+176: y3 786432 v:=655360+786432=1441792, vv:=91
+180: push
+level 1:(h=0,v=1441792,w=0,x=0,y=786432,z=0,hh=0,vv=91)
+181: push
+level 2:(h=0,v=1441792,w=0,x=0,y=786432,z=0,hh=0,vv=91)
+182: set2 8739("2223) type=0 h:=0+630598=630598, hh:=40
+[∣]
+185: pop
+level 2:(h=0,v=1441792,w=0,x=0,y=786432,z=0,hh=0,vv=91)
+186: right3 630598 h:=0+630598=630598, hh:=40
+[ ]
+190: fntnum0 current font is ec-lmr10
+191: setchar92 ("5C) h:=630598+327680=958278, hh:=61
+192: setchar109 ("6D) h:=958278+546111=1504389, hh:=96
+193: setchar101 ("65) h:=1504389+291275=1795664, hh:=114
+194: setchar97 ("61) h:=1795664+327680=2123344, hh:=135
+195: setchar110 ("6E) h:=2123344+364085=2487429, hh:=158
+196: setchar105 ("69) h:=2487429+182043=2669472, hh:=170
+197: setchar110 ("6E) h:=2669472+364085=3033557, hh:=193
+198: setchar103 ("67) h:=3033557+327680=3361237, hh:=214
+199: setchar46 ("2E) h:=3361237+182043=3543280, hh:=226
+[\meaning.]
+200: pop
+level 1:(h=0,v=1441792,w=0,x=0,y=786432,z=0,hh=0,vv=91)
+201: y0 786432 v:=1441792+786432=2228224, vv:=141
+202: push
+level 1:(h=0,v=2228224,w=0,x=0,y=786432,z=0,hh=0,vv=141)
+203: right3 1310720 h:=0+1310720=1310720, hh:=83
+207: setchar109 ("6D) h:=1310720+546111=1856831, hh:=118
+208: setchar97 ("61) h:=1856831+327680=2184511, hh:=139
+209: setchar99 ("63) h:=2184511+291275=2475786, hh:=157
+210: setchar114 ("72) h:=2475786+256683=2732469, hh:=173
+211: setchar111 ("6F) h:=2732469+327680=3060149, hh:=194
+212: setchar58 ("3A) h:=3060149+182043=3242192, hh:=206
+213: setchar45 ("2D) h:=3242192+218453=3460645, hh:=220
+214: setchar62 ("3E) h:=3460645+509738=3970383, hh:=252
+215: setchar109 ("6D) h:=3970383+546111=4516494, hh:=287
+216: setchar97 ("61) h:=4516494+327680=4844174, hh:=308
+217: setchar99 ("63) h:=4844174+291275=5135449, hh:=326
+218: setchar114 ("72) h:=5135449+256683=5392132, hh:=342
+219: setchar111 ("6F) h:=5392132+327680=5719812, hh:=363
+220: setchar58 ("3A) h:=5719812+182043=5901855, hh:=375
+221: setchar45 ("2D) h:=5901855+218453=6120308, hh:=389
+222: setchar62 ("3E) h:=6120308+509738=6630046, hh:=421
+[ macro:->macro:->]
+223: set1 197("C5) h:=6630046+491520=7121566, hh:=452
+225: set1 191("BF) h:=7121566+491520=7613086, hh:=483
+227: w3 218453 h:=7613086+218453=7831539, hh:=496
+[ ]
+231: set1 197("C5) h:=7831539+491520=8323059, hh:=527
+233: set1 191("BF) h:=8323059+491520=8814579, hh:=558
+235: w0 218453 h:=8814579+218453=9033032, hh:=572
+[ ]
+236: fntnum1 current font is min10
+237: set2 17727("453F) type=0 h:=9033032+630598=9663630, hh:=612
+[䔿]
+240: pop
+level 1:(h=0,v=2228224,w=0,x=0,y=786432,z=0,hh=0,vv=141)
+241: y0 786432 v:=2228224+786432=3014656, vv:=191
+242: push
+level 1:(h=0,v=3014656,w=0,x=0,y=786432,z=0,hh=0,vv=191)
+243: right3 1310720 h:=0+1310720=1310720, hh:=83
+[ ]
+247: fntnum0 current font is ec-lmr10
+248: setchar109 ("6D) h:=1310720+546111=1856831, hh:=118
+249: setchar97 ("61) h:=1856831+327680=2184511, hh:=139
+250: setchar99 ("63) h:=2184511+291275=2475786, hh:=157
+251: setchar114 ("72) h:=2475786+256683=2732469, hh:=173
+252: setchar111 ("6F) h:=2732469+327680=3060149, hh:=194
+253: setchar58 ("3A) h:=3060149+182043=3242192, hh:=206
+254: setchar45 ("2D) h:=3242192+218453=3460645, hh:=220
+255: setchar62 ("3E) h:=3460645+509738=3970383, hh:=252
+[macro:->]
+256: set1 197("C5) h:=3970383+491520=4461903, hh:=283
+258: set1 191("BF) h:=4461903+491520=4953423, hh:=314
+260: w3 218453 h:=4953423+218453=5171876, hh:=328
+[ ]
+264: set1 197("C5) h:=5171876+491520=5663396, hh:=359
+266: set1 191("BF) h:=5663396+491520=6154916, hh:=390
+268: w0 218453 h:=6154916+218453=6373369, hh:=404
+[ ]
+269: fntnum1 current font is min10
+270: set2 17727("453F) type=0 h:=6373369+630598=7003967, hh:=444
+[䔿]
+273: pop
+level 1:(h=0,v=3014656,w=0,x=0,y=786432,z=0,hh=0,vv=191)
+274: y0 786432 v:=3014656+786432=3801088, vv:=241
+275: push
+level 1:(h=0,v=3801088,w=0,x=0,y=786432,z=0,hh=0,vv=241)
+276: push
+level 2:(h=0,v=3801088,w=0,x=0,y=786432,z=0,hh=0,vv=241)
+277: set2 8739("2223) type=0 h:=0+630598=630598, hh:=40
+[∣]
+280: pop
+level 2:(h=0,v=3801088,w=0,x=0,y=786432,z=0,hh=0,vv=241)
+281: right3 630598 h:=0+630598=630598, hh:=40
+[ ]
+285: fntnum0 current font is ec-lmr10
+286: setchar92 ("5C) h:=630598+327680=958278, hh:=61
+287: setchar106 ("6A) h:=958278+200245=1158523, hh:=74
+288: setchar111 ("6F) h:=1158523+327680=1486203, hh:=95
+289: setchar98 ("62) h:=1486203+364085=1850288, hh:=118
+290: setchar110 ("6E) h:=1850288+364085=2214373, hh:=141
+291: setchar97 ("61) h:=2214373+327680=2542053, hh:=162
+292: setchar109 ("6D) h:=2542053+546111=3088164, hh:=197
+293: setchar101 ("65) h:=3088164+291275=3379439, hh:=215
+294: setchar46 ("2E) h:=3379439+182043=3561482, hh:=227
+[\jobname.]
+295: pop
+level 1:(h=0,v=3801088,w=0,x=0,y=786432,z=0,hh=0,vv=241)
+296: y0 786432 v:=3801088+786432=4587520, vv:=291
+297: push
+level 1:(h=0,v=4587520,w=0,x=0,y=786432,z=0,hh=0,vv=291)
+298: push
+level 2:(h=0,v=4587520,w=0,x=0,y=786432,z=0,hh=0,vv=291)
+299: fntnum1 current font is min10
+300: set2 8739("2223) type=0 h:=0+630598=630598, hh:=40
+[∣]
+303: pop
+level 2:(h=0,v=4587520,w=0,x=0,y=786432,z=0,hh=0,vv=291)
+304: right3 630598 h:=0+630598=630598, hh:=40
+[ ]
+308: fntnum0 current font is ec-lmr10
+309: setchar111 ("6F) h:=630598+327680=958278, hh:=61
+310: setchar110 ("6E) h:=958278+364085=1322363, hh:=84
+311: setchar101 ("65) h:=1322363+291275=1613638, hh:=102
+312: setchar108 ("6C) h:=1613638+182043=1795681, hh:=114
+313: setchar101 ("65) h:=1795681+291275=2086956, hh:=132
+314: setchar116 ("74) h:=2086956+254870=2341826, hh:=148
+315: setchar116 ("74) h:=2341826+254870=2596696, hh:=164
+316: setchar101 ("65) h:=2596696+291275=2887971, hh:=182
+317: setchar114 ("72) h:=2887971+256683=3144654, hh:=198
+318: setchar46 ("2E) h:=3144654+182043=3326697, hh:=210
+[oneletter.]
+319: pop
+level 1:(h=0,v=4587520,w=0,x=0,y=786432,z=0,hh=0,vv=291)
+320: y0 786432 v:=4587520+786432=5373952, vv:=340
+321: push
+level 1:(h=0,v=5373952,w=0,x=0,y=786432,z=0,hh=0,vv=340)
+322: right3 1310720 h:=0+1310720=1310720, hh:=83
+326: setchar92 ("5C) h:=1310720+327680=1638400, hh:=104
+[ \]
+327: set1 196("C4) h:=1638400+491520=2129920, hh:=135
+329: setchar46 ("2E) h:=2129920+182043=2311963, hh:=147
+330: setchar92 ("5C) h:=2311963+327680=2639643, hh:=168
+[.\]
+331: set1 197("C5) h:=2639643+491520=3131163, hh:=199
+333: setchar46 ("2E) h:=3131163+182043=3313206, hh:=211
+334: setchar92 ("5C) h:=3313206+327680=3640886, hh:=232
+[.\]
+335: set1 255("FF) h:=3640886+327680=3968566, hh:=253
+337: setchar46 ("2E) h:=3968566+182043=4150609, hh:=265
+338: right3 291271 h:=4150609+291271=4441880, hh:=281
+[. ]
+342: set1 191("BF) h:=4441880+491520=4933400, hh:=312
+344: pop
+level 1:(h=0,v=5373952,w=0,x=0,y=786432,z=0,hh=0,vv=340)
+345: y0 786432 v:=5373952+786432=6160384, vv:=390
+346: push
+level 1:(h=0,v=6160384,w=0,x=0,y=786432,z=0,hh=0,vv=390)
+347: push
+level 2:(h=0,v=6160384,w=0,x=0,y=786432,z=0,hh=0,vv=390)
+348: fntnum1 current font is min10
+349: set2 8739("2223) type=0 h:=0+630598=630598, hh:=40
+[∣]
+352: pop
+level 2:(h=0,v=6160384,w=0,x=0,y=786432,z=0,hh=0,vv=390)
+353: right3 630598 h:=0+630598=630598, hh:=40
+[ ]
+357: fntnum0 current font is ec-lmr10
+358: setchar99 ("63) h:=630598+291275=921873, hh:=58
+359: setchar115 ("73) h:=921873+258506=1180379, hh:=74
+360: setchar110 ("6E) h:=1180379+364085=1544464, hh:=97
+361: setchar97 ("61) h:=1544464+327680=1872144, hh:=118
+362: setchar109 ("6D) h:=1872144+546111=2418255, hh:=153
+363: setchar101 ("65) h:=2418255+291275=2709530, hh:=171
+364: setchar49 ("31) h:=2709530+327680=3037210, hh:=192
+365: setchar46 ("2E) h:=3037210+182043=3219253, hh:=204
+[csname1.]
+366: pop
+level 1:(h=0,v=6160384,w=0,x=0,y=786432,z=0,hh=0,vv=390)
+367: y0 786432 v:=6160384+786432=6946816, vv:=440
+368: push
+level 1:(h=0,v=6946816,w=0,x=0,y=786432,z=0,hh=0,vv=440)
+369: right3 1310720 h:=0+1310720=1310720, hh:=83
+373: setchar92 ("5C) h:=1310720+327680=1638400, hh:=104
+[ \]
+374: set1 197("C5) h:=1638400+491520=2129920, hh:=135
+376: set1 191("BF) h:=2129920+491520=2621440, hh:=166
+378: setchar92 ("5C) h:=2621440+327680=2949120, hh:=187
+[\]
+379: set1 197("C5) h:=2949120+491520=3440640, hh:=218
+381: set1 191("BF) h:=3440640+491520=3932160, hh:=249
+383: setchar44 ("2C) h:=3932160+182043=4114203, hh:=261
+384: w3 218453 h:=4114203+218453=4332656, hh:=274
+388: setchar92 ("5C) h:=4332656+327680=4660336, hh:=295
+[, \]
+389: set1 197("C5) h:=4660336+491520=5151856, hh:=326
+391: set1 191("BF) h:=5151856+491520=5643376, hh:=357
+393: setchar44 ("2C) h:=5643376+182043=5825419, hh:=369
+394: w0 218453 h:=5825419+218453=6043872, hh:=383
+395: setchar92 ("5C) h:=6043872+327680=6371552, hh:=404
+[, \]
+396: fntnum1 current font is min10
+397: set2 17727("453F) type=0 h:=6371552+630598=7002150, hh:=444
+[䔿]
+400: pop
+level 1:(h=0,v=6946816,w=0,x=0,y=786432,z=0,hh=0,vv=440)
+401: y0 786432 v:=6946816+786432=7733248, vv:=490
+402: push
+level 1:(h=0,v=7733248,w=0,x=0,y=786432,z=0,hh=0,vv=490)
+403: right3 1310720 h:=0+1310720=1310720, hh:=83
+[ ]
+407: fntnum0 current font is ec-lmr10
+408: setchar92 ("5C) h:=1310720+327680=1638400, hh:=104
+[\]
+409: fntnum1 current font is min10
+410: set2 9250("2422) type=0 h:=1638400+630598=2268998, hh:=144
+[␢]
+413: fntnum0 current font is ec-lmr10
+414: setchar61 ("3D) h:=2268998+509738=2778736, hh:=176
+415: setchar62 ("3E) h:=2778736+509738=3288474, hh:=208
+416: right3 218453 h:=3288474+218453=3506927, hh:=222
+420: setchar104 ("68) h:=3506927+364085=3871012, hh:=245
+421: setchar111 ("6F) h:=3871012+327680=4198692, hh:=266
+422: setchar103 ("67) h:=4198692+327680=4526372, hh:=287
+423: setchar101 ("65) h:=4526372+291275=4817647, hh:=305
+424: setchar44 ("2C) h:=4817647+182043=4999690, hh:=317
+[=> hoge,]
+425: pop
+level 1:(h=0,v=7733248,w=0,x=0,y=786432,z=0,hh=0,vv=490)
+426: y0 786432 v:=7733248+786432=8519680, vv:=540
+427: push
+level 1:(h=0,v=8519680,w=0,x=0,y=786432,z=0,hh=0,vv=540)
+428: right3 1310720 h:=0+1310720=1310720, hh:=83
+432: setchar92 ("5C) h:=1310720+327680=1638400, hh:=104
+[ \]
+433: set1 227("E3) h:=1638400+327680=1966080, hh:=125
+435: set1 129("81) h:=1966080+491520=2457600, hh:=156
+437: w2 -18205 h:=2457600-18205=2439395, hh:=155
+440: set1 130("82) h:=2439395+473301=2912696, hh:=185
+442: setchar61 ("3D) h:=2912696+509738=3422434, hh:=217
+443: setchar62 ("3E) h:=3422434+509738=3932172, hh:=249
+444: right3 218453 h:=3932172+218453=4150625, hh:=263
+448: setchar112 ("70) h:=4150625+364085=4514710, hh:=286
+449: setchar105 ("69) h:=4514710+182043=4696753, hh:=298
+450: setchar121 ("79) h:=4696753+345898=5042651, hh:=320
+451: w0 -18205 h:=5042651-18205=5024446, hh:=319
+452: setchar111 ("6F) h:=5024446+327680=5352126, hh:=340
+453: setchar44 ("2C) h:=5352126+182043=5534169, hh:=352
+[=> piyo,]
+454: pop
+level 1:(h=0,v=8519680,w=0,x=0,y=786432,z=0,hh=0,vv=540)
+455: y0 786432 v:=8519680+786432=9306112, vv:=589
+456: push
+level 1:(h=0,v=9306112,w=0,x=0,y=786432,z=0,hh=0,vv=589)
+457: right3 1310720 h:=0+1310720=1310720, hh:=83
+461: setchar92 ("5C) h:=1310720+327680=1638400, hh:=104
+[ \]
+462: set1 227("E3) h:=1638400+327680=1966080, hh:=125
+464: set1 129("81) h:=1966080+491520=2457600, hh:=156
+466: w2 -18205 h:=2457600-18205=2439395, hh:=155
+469: set1 130("82) h:=2439395+473301=2912696, hh:=185
+471: setchar61 ("3D) h:=2912696+509738=3422434, hh:=217
+472: setchar62 ("3E) h:=3422434+509738=3932172, hh:=249
+473: right3 218453 h:=3932172+218453=4150625, hh:=263
+477: setchar112 ("70) h:=4150625+364085=4514710, hh:=286
+478: setchar105 ("69) h:=4514710+182043=4696753, hh:=298
+479: setchar121 ("79) h:=4696753+345898=5042651, hh:=320
+480: w0 -18205 h:=5042651-18205=5024446, hh:=319
+481: setchar111 ("6F) h:=5024446+327680=5352126, hh:=340
+482: setchar46 ("2E) h:=5352126+182043=5534169, hh:=352
+[=> piyo.]
+483: pop
+level 1:(h=0,v=9306112,w=0,x=0,y=786432,z=0,hh=0,vv=589)
+484: y0 786432 v:=9306112+786432=10092544, vv:=639
+485: push
+level 1:(h=0,v=10092544,w=0,x=0,y=786432,z=0,hh=0,vv=639)
+486: push
+level 2:(h=0,v=10092544,w=0,x=0,y=786432,z=0,hh=0,vv=639)
+487: fntnum1 current font is min10
+488: set2 8739("2223) type=0 h:=0+630598=630598, hh:=40
+[∣]
+491: pop
+level 2:(h=0,v=10092544,w=0,x=0,y=786432,z=0,hh=0,vv=639)
+492: right3 630598 h:=0+630598=630598, hh:=40
+[ ]
+496: fntnum0 current font is ec-lmr10
+497: setchar48 ("30) h:=630598+327680=958278, hh:=61
+498: setchar120 ("78) h:=958278+345898=1304176, hh:=83
+499: setchar70 ("46) h:=1304176+427818=1731994, hh:=110
+500: setchar70 ("46) h:=1731994+427818=2159812, hh:=137
+501: setchar46 ("2E) h:=2159812+182043=2341855, hh:=149
+[0xFF.]
+502: pop
+level 1:(h=0,v=10092544,w=0,x=0,y=786432,z=0,hh=0,vv=639)
+503: y0 786432 v:=10092544+786432=10878976, vv:=689
+504: push
+level 1:(h=0,v=10878976,w=0,x=0,y=786432,z=0,hh=0,vv=689)
+505: right3 1310720 h:=0+1310720=1310720, hh:=83
+[ ]
+509: fntnum1 current font is min10
+510: set2 9250("2422) type=0 h:=1310720+630598=1941318, hh:=123
+[␢]
+513: fntnum0 current font is ec-lmr10
+514: setchar44 ("2C) h:=1941318+182043=2123361, hh:=135
+515: w3 218453 h:=2123361+218453=2341814, hh:=148
+519: setchar40 ("28) h:=2341814+254870=2596684, hh:=164
+520: setchar92 ("5C) h:=2596684+327680=2924364, hh:=185
+[, (\]
+521: set1 255("FF) h:=2924364+327680=3252044, hh:=206
+523: set1 197("C5) h:=3252044+491520=3743564, hh:=237
+525: set1 255("FF) h:=3743564+327680=4071244, hh:=258
+527: set1 191("BF) h:=4071244+491520=4562764, hh:=289
+529: setchar58 ("3A) h:=4562764+182043=4744807, hh:=301
+530: setchar109 ("6D) h:=4744807+546111=5290918, hh:=336
+531: setchar97 ("61) h:=5290918+327680=5618598, hh:=357
+532: setchar99 ("63) h:=5618598+291275=5909873, hh:=375
+533: setchar114 ("72) h:=5909873+256683=6166556, hh:=391
+534: setchar111 ("6F) h:=6166556+327680=6494236, hh:=412
+535: setchar58 ("3A) h:=6494236+182043=6676279, hh:=424
+536: setchar45 ("2D) h:=6676279+218453=6894732, hh:=438
+537: setchar62 ("3E) h:=6894732+509738=7404470, hh:=470
+[:macro:->]
+538: fntnum1 current font is min10
+539: set2 9250("2422) type=0 h:=7404470+630598=8035068, hh:=510
+[␢]
+542: fntnum0 current font is ec-lmr10
+543: setchar41 ("29) h:=8035068+254870=8289938, hh:=526
+544: setchar44 ("2C) h:=8289938+182043=8471981, hh:=538
+545: w0 218453 h:=8471981+218453=8690434, hh:=550
+546: setchar92 ("5C) h:=8690434+327680=9018114, hh:=571
+[), \]
+547: set1 255("FF) h:=9018114+327680=9345794, hh:=592
+549: set1 197("C5) h:=9345794+491520=9837314, hh:=623
+551: set1 255("FF) h:=9837314+327680=10164994, hh:=644
+553: set1 191("BF) h:=10164994+491520=10656514, hh:=675
+555: setchar44 ("2C) h:=10656514+182043=10838557, hh:=687
+556: w0 218453 h:=10838557+218453=11057010, hh:=700
+557: setchar109 ("6D) h:=11057010+546111=11603121, hh:=735
+558: setchar97 ("61) h:=11603121+327680=11930801, hh:=756
+559: setchar99 ("63) h:=11930801+291275=12222076, hh:=774
+560: setchar114 ("72) h:=12222076+256683=12478759, hh:=790
+561: setchar111 ("6F) h:=12478759+327680=12806439, hh:=811
+562: setchar58 ("3A) h:=12806439+182043=12988482, hh:=823
+563: setchar45 ("2D) h:=12988482+218453=13206935, hh:=837
+564: setchar62 ("3E) h:=13206935+509738=13716673, hh:=869
+565: setchar40 ("28) h:=13716673+254870=13971543, hh:=885
+566: setchar92 ("5C) h:=13971543+327680=14299223, hh:=906
+[, macro:->(\]
+567: set1 255("FF) h:=14299223+327680=14626903, hh:=927
+569: set1 197("C5) h:=14626903+491520=15118423, hh:=958
+571: set1 255("FF) h:=15118423+327680=15446103, hh:=979
+573: set1 191("BF) h:=15446103+491520=15937623, hh:=1010
+575: setchar58 ("3A) h:=15937623+182043=16119666, hh:=1022
+576: setchar109 ("6D) h:=16119666+546111=16665777, hh:=1057
+577: setchar97 ("61) h:=16665777+327680=16993457, hh:=1078
+578: setchar99 ("63) h:=16993457+291275=17284732, hh:=1096
+579: setchar114 ("72) h:=17284732+256683=17541415, hh:=1112
+580: setchar111 ("6F) h:=17541415+327680=17869095, hh:=1133
+581: setchar58 ("3A) h:=17869095+182043=18051138, hh:=1145
+582: setchar45 ("2D) h:=18051138+218453=18269591, hh:=1159
+583: setchar62 ("3E) h:=18269591+509738=18779329, hh:=1191
+[:macro:->]
+584: fntnum1 current font is min10
+585: set2 9250("2422) type=0 h:=18779329+630598=19409927, hh:=1231
+[␢]
+588: fntnum0 current font is ec-lmr10
+589: setchar41 ("29) h:=19409927+254870=19664797, hh:=1247
+590: setchar46 ("2E) h:=19664797+182043=19846840, hh:=1259
+[).]
+591: pop
+level 1:(h=0,v=10878976,w=0,x=0,y=786432,z=0,hh=0,vv=689)
+592: y0 786432 v:=10878976+786432=11665408, vv:=739
+593: push
+level 1:(h=0,v=11665408,w=0,x=0,y=786432,z=0,hh=0,vv=739)
+594: right3 1310720 h:=0+1310720=1310720, hh:=83
+[ ]
+598: fntnum1 current font is min10
+599: set2 9252("2424) type=0 h:=1310720+630598=1941318, hh:=123
+[␤]
+602: fntnum0 current font is ec-lmr10
+603: setchar44 ("2C) h:=1941318+182043=2123361, hh:=135
+604: w3 218453 h:=2123361+218453=2341814, hh:=148
+608: setchar40 ("28) h:=2341814+254870=2596684, hh:=164
+609: setchar92 ("5C) h:=2596684+327680=2924364, hh:=185
+[, (\]
+610: set1 255("FF) h:=2924364+327680=3252044, hh:=206
+612: setchar58 ("3A) h:=3252044+182043=3434087, hh:=218
+613: setchar109 ("6D) h:=3434087+546111=3980198, hh:=253
+614: setchar97 ("61) h:=3980198+327680=4307878, hh:=274
+615: setchar99 ("63) h:=4307878+291275=4599153, hh:=292
+616: setchar114 ("72) h:=4599153+256683=4855836, hh:=308
+617: setchar111 ("6F) h:=4855836+327680=5183516, hh:=329
+618: setchar58 ("3A) h:=5183516+182043=5365559, hh:=341
+619: setchar45 ("2D) h:=5365559+218453=5584012, hh:=355
+620: setchar62 ("3E) h:=5584012+509738=6093750, hh:=387
+[:macro:->]
+621: fntnum1 current font is min10
+622: set2 9252("2424) type=0 h:=6093750+630598=6724348, hh:=427
+[␤]
+625: fntnum0 current font is ec-lmr10
+626: setchar41 ("29) h:=6724348+254870=6979218, hh:=443
+627: setchar44 ("2C) h:=6979218+182043=7161261, hh:=455
+628: w0 218453 h:=7161261+218453=7379714, hh:=467
+629: setchar92 ("5C) h:=7379714+327680=7707394, hh:=488
+[), \]
+630: set1 255("FF) h:=7707394+327680=8035074, hh:=509
+632: setchar44 ("2C) h:=8035074+182043=8217117, hh:=521
+633: w0 218453 h:=8217117+218453=8435570, hh:=534
+634: setchar109 ("6D) h:=8435570+546111=8981681, hh:=569
+635: setchar97 ("61) h:=8981681+327680=9309361, hh:=590
+636: setchar99 ("63) h:=9309361+291275=9600636, hh:=608
+637: setchar114 ("72) h:=9600636+256683=9857319, hh:=624
+638: setchar111 ("6F) h:=9857319+327680=10184999, hh:=645
+639: setchar58 ("3A) h:=10184999+182043=10367042, hh:=657
+640: setchar45 ("2D) h:=10367042+218453=10585495, hh:=671
+641: setchar62 ("3E) h:=10585495+509738=11095233, hh:=703
+642: setchar40 ("28) h:=11095233+254870=11350103, hh:=719
+643: setchar92 ("5C) h:=11350103+327680=11677783, hh:=740
+[, macro:->(\]
+644: set1 255("FF) h:=11677783+327680=12005463, hh:=761
+646: setchar58 ("3A) h:=12005463+182043=12187506, hh:=773
+647: setchar109 ("6D) h:=12187506+546111=12733617, hh:=808
+648: setchar97 ("61) h:=12733617+327680=13061297, hh:=829
+649: setchar99 ("63) h:=13061297+291275=13352572, hh:=847
+650: setchar114 ("72) h:=13352572+256683=13609255, hh:=863
+651: setchar111 ("6F) h:=13609255+327680=13936935, hh:=884
+652: setchar58 ("3A) h:=13936935+182043=14118978, hh:=896
+653: setchar45 ("2D) h:=14118978+218453=14337431, hh:=910
+654: setchar62 ("3E) h:=14337431+509738=14847169, hh:=942
+[:macro:->]
+655: fntnum1 current font is min10
+656: set2 9252("2424) type=0 h:=14847169+630598=15477767, hh:=982
+[␤]
+659: fntnum0 current font is ec-lmr10
+660: setchar41 ("29) h:=15477767+254870=15732637, hh:=998
+661: setchar46 ("2E) h:=15732637+182043=15914680, hh:=1010
+[).]
+662: pop
+level 1:(h=0,v=11665408,w=0,x=0,y=786432,z=0,hh=0,vv=739)
+663: y0 786432 v:=11665408+786432=12451840, vv:=789
+664: push
+level 1:(h=0,v=12451840,w=0,x=0,y=786432,z=0,hh=0,vv=789)
+665: push
+level 2:(h=0,v=12451840,w=0,x=0,y=786432,z=0,hh=0,vv=789)
+666: fntnum1 current font is min10
+667: set2 8739("2223) type=0 h:=0+630598=630598, hh:=40
+[∣]
+670: pop
+level 2:(h=0,v=12451840,w=0,x=0,y=786432,z=0,hh=0,vv=789)
+671: right3 630598 h:=0+630598=630598, hh:=40
+[ ]
+675: fntnum0 current font is ec-lmr10
+676: setchar99 ("63) h:=630598+291275=921873, hh:=58
+677: setchar111 ("6F) h:=921873+327680=1249553, hh:=79
+678: setchar110 ("6E) h:=1249553+364085=1613638, hh:=102
+679: right2 -18205 h:=1613638-18205=1595433, hh:=101
+682: setchar116 ("74) h:=1595433+254870=1850303, hh:=117
+683: setchar101 ("65) h:=1850303+291275=2141578, hh:=135
+684: setchar120 ("78) h:=2141578+345898=2487476, hh:=157
+685: setchar116 ("74) h:=2487476+254870=2742346, hh:=173
+686: setchar115 ("73) h:=2742346+258506=3000852, hh:=189
+687: setchar46 ("2E) h:=3000852+182043=3182895, hh:=201
+[contexts.]
+688: pop
+level 1:(h=0,v=12451840,w=0,x=0,y=786432,z=0,hh=0,vv=789)
+689: y0 786432 v:=12451840+786432=13238272, vv:=839
+690: push
+level 1:(h=0,v=13238272,w=0,x=0,y=786432,z=0,hh=0,vv=839)
+691: right3 1310720 h:=0+1310720=1310720, hh:=83
+[ ]
+695: fntnum1 current font is min10
+696: set2 9250("2422) type=0 h:=1310720+630598=1941318, hh:=123
+[␢]
+699: fntnum0 current font is ec-lmr10
+700: set1 197("C5) h:=1941318+491520=2432838, hh:=154
+702: set1 191("BF) h:=2432838+491520=2924358, hh:=185
+704: w3 218453 h:=2924358+218453=3142811, hh:=199
+708: setchar97 ("61) h:=3142811+327680=3470491, hh:=220
+[ a]
+709: set1 197("C5) h:=3470491+491520=3962011, hh:=251
+711: set1 191("BF) h:=3962011+491520=4453531, hh:=282
+713: w0 218453 h:=4453531+218453=4671984, hh:=296
+[ ]
+714: set1 197("C5) h:=4671984+491520=5163504, hh:=327
+716: set1 191("BF) h:=5163504+491520=5655024, hh:=358
+718: w0 218453 h:=5655024+218453=5873477, hh:=372
+[ ]
+719: fntnum1 current font is min10
+720: set2 17727("453F) type=0 h:=5873477+630598=6504075, hh:=412
+723: w0 218453 h:=6504075+218453=6722528, hh:=426
+[䔿 ]
+724: fntnum0 current font is ec-lmr10
+725: set1 197("C5) h:=6722528+491520=7214048, hh:=457
+727: set1 191("BF) h:=7214048+491520=7705568, hh:=488
+729: w0 218453 h:=7705568+218453=7924021, hh:=502
+[ ]
+730: set1 197("C5) h:=7924021+491520=8415541, hh:=533
+732: set1 191("BF) h:=8415541+491520=8907061, hh:=564
+734: w0 218453 h:=8907061+218453=9125514, hh:=578
+[ ]
+735: fntnum1 current font is min10
+736: set2 17727("453F) type=0 h:=9125514+630598=9756112, hh:=618
+739: x3 436906 h:=9756112+436906=10193018, hh:=646
+[䔿 ]
+743: fntnum0 current font is ec-lmr10
+744: set1 197("C5) h:=10193018+491520=10684538, hh:=677
+746: set1 191("BF) h:=10684538+491520=11176058, hh:=708
+748: w0 218453 h:=11176058+218453=11394511, hh:=722
+[ ]
+749: fntnum1 current font is min10
+750: set2 17727("453F) type=0 h:=11394511+630598=12025109, hh:=762
+753: x0 436906 h:=12025109+436906=12462015, hh:=789
+754: set2 9252("2424) type=0 h:=12462015+630598=13092613, hh:=829
+[䔿 ␤]
+757: fntnum0 current font is ec-lmr10
+758: setchar109 ("6D) h:=13092613+546111=13638724, hh:=864
+759: setchar97 ("61) h:=13638724+327680=13966404, hh:=885
+760: setchar99 ("63) h:=13966404+291275=14257679, hh:=903
+761: setchar114 ("72) h:=14257679+256683=14514362, hh:=919
+762: setchar111 ("6F) h:=14514362+327680=14842042, hh:=940
+763: setchar58 ("3A) h:=14842042+182043=15024085, hh:=952
+764: setchar45 ("2D) h:=15024085+218453=15242538, hh:=966
+765: setchar62 ("3E) h:=15242538+509738=15752276, hh:=998
+[macro:->]
+766: set1 197("C5) h:=15752276+491520=16243796, hh:=1029
+768: set1 191("BF) h:=16243796+491520=16735316, hh:=1060
+770: w0 218453 h:=16735316+218453=16953769, hh:=1074
+[ ]
+771: set1 197("C5) h:=16953769+491520=17445289, hh:=1105
+773: set1 191("BF) h:=17445289+491520=17936809, hh:=1136
+775: w0 218453 h:=17936809+218453=18155262, hh:=1150
+[ ]
+776: fntnum1 current font is min10
+777: set2 17727("453F) type=0 h:=18155262+630598=18785860, hh:=1190
+[䔿]
+780: pop
+level 1:(h=0,v=13238272,w=0,x=0,y=786432,z=0,hh=0,vv=839)
+781: y0 786432 v:=13238272+786432=14024704, vv:=888
+782: push
+level 1:(h=0,v=14024704,w=0,x=0,y=786432,z=0,hh=0,vv=888)
+783: right3 1310720 h:=0+1310720=1310720, hh:=83
+787: set2 9250("2422) type=0 h:=1310720+630598=1941318, hh:=123
+[ ␢]
+790: fntnum0 current font is ec-lmr10
+791: set1 197("C5) h:=1941318+491520=2432838, hh:=154
+793: set1 191("BF) h:=2432838+491520=2924358, hh:=185
+795: w3 218453 h:=2924358+218453=3142811, hh:=199
+[ ]
+799: set1 197("C5) h:=3142811+491520=3634331, hh:=230
+801: set1 191("BF) h:=3634331+491520=4125851, hh:=261
+803: fntnum1 current font is min10
+804: set2 17727("453F) type=0 h:=4125851+630598=4756449, hh:=301
+[䔿]
+807: fntnum0 current font is ec-lmr10
+808: set1 197("C5) h:=4756449+491520=5247969, hh:=332
+810: set1 191("BF) h:=5247969+491520=5739489, hh:=363
+812: w0 218453 h:=5739489+218453=5957942, hh:=377
+813: setchar97 ("61) h:=5957942+327680=6285622, hh:=398
+[ a]
+814: set1 197("C5) h:=6285622+491520=6777142, hh:=429
+816: set1 191("BF) h:=6777142+491520=7268662, hh:=460
+818: w0 218453 h:=7268662+218453=7487115, hh:=474
+[ ]
+819: set1 197("C5) h:=7487115+491520=7978635, hh:=505
+821: set1 191("BF) h:=7978635+491520=8470155, hh:=536
+823: w0 218453 h:=8470155+218453=8688608, hh:=550
+[ ]
+824: fntnum1 current font is min10
+825: set2 17727("453F) type=0 h:=8688608+630598=9319206, hh:=590
+828: w0 218453 h:=9319206+218453=9537659, hh:=604
+[䔿 ]
+829: fntnum0 current font is ec-lmr10
+830: set1 197("C5) h:=9537659+491520=10029179, hh:=635
+832: set1 191("BF) h:=10029179+491520=10520699, hh:=666
+834: w0 218453 h:=10520699+218453=10739152, hh:=680
+[ ]
+835: set1 197("C5) h:=10739152+491520=11230672, hh:=711
+837: set1 191("BF) h:=11230672+491520=11722192, hh:=742
+839: w0 218453 h:=11722192+218453=11940645, hh:=756
+[ ]
+840: fntnum1 current font is min10
+841: set2 17727("453F) type=0 h:=11940645+630598=12571243, hh:=796
+844: right3 436906 h:=12571243+436906=13008149, hh:=824
+[䔿 ]
+848: fntnum0 current font is ec-lmr10
+849: set1 197("C5) h:=13008149+491520=13499669, hh:=855
+851: set1 191("BF) h:=13499669+491520=13991189, hh:=886
+853: w0 218453 h:=13991189+218453=14209642, hh:=900
+[ ]
+854: fntnum1 current font is min10
+855: set2 17727("453F) type=0 h:=14209642+630598=14840240, hh:=940
+858: w0 218453 h:=14840240+218453=15058693, hh:=954
+859: set2 9252("2424) type=0 h:=15058693+630598=15689291, hh:=994
+[䔿 ␤]
+862: fntnum0 current font is ec-lmr10
+863: set1 255("FF) h:=15689291+327680=16016971, hh:=1015
+865: pop
+level 1:(h=0,v=14024704,w=0,x=0,y=786432,z=0,hh=0,vv=888)
+866: y0 786432 v:=14024704+786432=14811136, vv:=938
+867: push
+level 1:(h=0,v=14811136,w=0,x=0,y=786432,z=0,hh=0,vv=938)
+868: right3 1310720 h:=0+1310720=1310720, hh:=83
+[ ]
+872: fntnum1 current font is min10
+873: set2 9252("2424) type=0 h:=1310720+630598=1941318, hh:=123
+[␤]
+876: pop
+level 1:(h=0,v=14811136,w=0,x=0,y=786432,z=0,hh=0,vv=938)
+877: y0 786432 v:=14811136+786432=15597568, vv:=988
+878: push
+level 1:(h=0,v=15597568,w=0,x=0,y=786432,z=0,hh=0,vv=988)
+879: right3 1310720 h:=0+1310720=1310720, hh:=83
+[ ]
+883: fntnum0 current font is ec-lmr10
+884: setchar46 ("2E) h:=1310720+182043=1492763, hh:=95
+885: setchar97 ("61) h:=1492763+327680=1820443, hh:=116
+886: setchar46 ("2E) h:=1820443+182043=2002486, hh:=128
+887: setchar46 ("2E) h:=2002486+182043=2184529, hh:=140
+888: setchar46 ("2E) h:=2184529+182043=2366572, hh:=152
+889: setchar99 ("63) h:=2366572+291275=2657847, hh:=170
+890: setchar46 ("2E) h:=2657847+182043=2839890, hh:=182
+891: setchar98 ("62) h:=2839890+364085=3203975, hh:=205
+892: setchar46 ("2E) h:=3203975+182043=3386018, hh:=216
+893: setchar46 ("2E) h:=3386018+182043=3568061, hh:=228
+[.a...c.b..]
+894: pop
+level 1:(h=0,v=15597568,w=0,x=0,y=786432,z=0,hh=0,vv=988)
+895: y0 786432 v:=15597568+786432=16384000, vv:=1038
+896: push
+level 1:(h=0,v=16384000,w=0,x=0,y=786432,z=0,hh=0,vv=1038)
+897: right3 1310720 h:=0+1310720=1310720, hh:=83
+901: setchar46 ("2E) h:=1310720+182043=1492763, hh:=95
+902: setchar97 ("61) h:=1492763+327680=1820443, hh:=116
+903: setchar46 ("2E) h:=1820443+182043=2002486, hh:=128
+904: setchar46 ("2E) h:=2002486+182043=2184529, hh:=140
+905: setchar46 ("2E) h:=2184529+182043=2366572, hh:=152
+906: setchar99 ("63) h:=2366572+291275=2657847, hh:=170
+907: setchar46 ("2E) h:=2657847+182043=2839890, hh:=182
+908: setchar98 ("62) h:=2839890+364085=3203975, hh:=205
+909: setchar46 ("2E) h:=3203975+182043=3386018, hh:=216
+910: setchar46 ("2E) h:=3386018+182043=3568061, hh:=228
+[ .a...c.b..]
+911: pop
+level 1:(h=0,v=16384000,w=0,x=0,y=786432,z=0,hh=0,vv=1038)
+912: y0 786432 v:=16384000+786432=17170432, vv:=1088
+913: push
+level 1:(h=0,v=17170432,w=0,x=0,y=786432,z=0,hh=0,vv=1088)
+914: push
+level 2:(h=0,v=17170432,w=0,x=0,y=786432,z=0,hh=0,vv=1088)
+915: fntnum1 current font is min10
+916: set2 8739("2223) type=0 h:=0+630598=630598, hh:=40
+[∣]
+919: pop
+level 2:(h=0,v=17170432,w=0,x=0,y=786432,z=0,hh=0,vv=1088)
+920: right3 630598 h:=0+630598=630598, hh:=40
+[ ]
+924: fntnum0 current font is ec-lmr10
+925: setchar99 ("63) h:=630598+291275=921873, hh:=58
+926: setchar115 ("73) h:=921873+258506=1180379, hh:=74
+927: setchar110 ("6E) h:=1180379+364085=1544464, hh:=97
+928: setchar97 ("61) h:=1544464+327680=1872144, hh:=118
+929: setchar109 ("6D) h:=1872144+546111=2418255, hh:=153
+930: setchar101 ("65) h:=2418255+291275=2709530, hh:=171
+931: setchar50 ("32) h:=2709530+327680=3037210, hh:=192
+932: setchar46 ("2E) h:=3037210+182043=3219253, hh:=204
+[csname2.]
+933: pop
+level 1:(h=0,v=17170432,w=0,x=0,y=786432,z=0,hh=0,vv=1088)
+934: y0 786432 v:=17170432+786432=17956864, vv:=1137
+935: push
+level 1:(h=0,v=17956864,w=0,x=0,y=786432,z=0,hh=0,vv=1137)
+936: right3 1310720 h:=0+1310720=1310720, hh:=83
+940: setchar83 ("53) h:=1310720+364085=1674805, hh:=106
+941: setchar72 ("48) h:=1674805+491520=2166325, hh:=137
+942: setchar79 ("4F) h:=2166325+509738=2676063, hh:=169
+943: right2 -18205 h:=2676063-18205=2657858, hh:=168
+946: setchar87 ("57) h:=2657858+673578=3331436, hh:=211
+947: right3 218453 h:=3331436+218453=3549889, hh:=225
+951: setchar46 ("2E) h:=3549889+182043=3731932, hh:=237
+952: setchar46 ("2E) h:=3731932+182043=3913975, hh:=249
+953: setchar46 ("2E) h:=3913975+182043=4096018, hh:=261
+[ SHOW ...]
+954: pop
+level 1:(h=0,v=17956864,w=0,x=0,y=786432,z=0,hh=0,vv=1137)
+955: y0 786432 v:=17956864+786432=18743296, vv:=1187
+956: push
+level 1:(h=0,v=18743296,w=0,x=0,y=786432,z=0,hh=0,vv=1187)
+957: right3 1310720 h:=0+1310720=1310720, hh:=83
+961: setchar83 ("53) h:=1310720+364085=1674805, hh:=106
+962: setchar84 ("54) h:=1674805+473301=2148106, hh:=136
+963: setchar82 ("52) h:=2148106+482410=2630516, hh:=167
+964: setchar73 ("49) h:=2630516+236650=2867166, hh:=182
+965: setchar78 ("4E) h:=2867166+491520=3358686, hh:=213
+966: setchar71 ("47) h:=3358686+514261=3872947, hh:=246
+967: right3 218453 h:=3872947+218453=4091400, hh:=259
+971: setchar92 ("5C) h:=4091400+327680=4419080, hh:=280
+[ STRING \]
+972: set1 197("C5) h:=4419080+491520=4910600, hh:=311
+974: set1 191("BF) h:=4910600+491520=5402120, hh:=342
+976: setchar46 ("2E) h:=5402120+182043=5584163, hh:=354
+977: setchar92 ("5C) h:=5584163+327680=5911843, hh:=375
+[.\]
+978: fntnum1 current font is min10
+979: set2 17727("453F) type=0 h:=5911843+630598=6542441, hh:=415
+[䔿]
+982: fntnum0 current font is ec-lmr10
+983: setchar46 ("2E) h:=6542441+182043=6724484, hh:=427
+984: setchar92 ("5C) h:=6724484+327680=7052164, hh:=448
+[.\]
+985: set1 197("C5) h:=7052164+491520=7543684, hh:=479
+987: set1 191("BF) h:=7543684+491520=8035204, hh:=510
+989: setchar46 ("2E) h:=8035204+182043=8217247, hh:=522
+990: setchar92 ("5C) h:=8217247+327680=8544927, hh:=543
+[.\]
+991: fntnum1 current font is min10
+992: set2 17727("453F) type=0 h:=8544927+630598=9175525, hh:=583
+[䔿]
+995: pop
+level 1:(h=0,v=18743296,w=0,x=0,y=786432,z=0,hh=0,vv=1187)
+996: y0 786432 v:=18743296+786432=19529728, vv:=1237
+997: push
+level 1:(h=0,v=19529728,w=0,x=0,y=786432,z=0,hh=0,vv=1237)
+998: right3 1310720 h:=0+1310720=1310720, hh:=83
+[ ]
+1002: fntnum0 current font is ec-lmr10
+1003: setchar77 ("4D) h:=1310720+600768=1911488, hh:=121
+1004: setchar69 ("45) h:=1911488+446005=2357493, hh:=149
+1005: setchar65 ("41) h:=2357493+491520=2849013, hh:=180
+1006: setchar78 ("4E) h:=2849013+491520=3340533, hh:=211
+1007: setchar73 ("49) h:=3340533+236650=3577183, hh:=226
+1008: setchar78 ("4E) h:=3577183+491520=4068703, hh:=257
+1009: setchar71 ("47) h:=4068703+514261=4582964, hh:=290
+1010: right3 218453 h:=4582964+218453=4801417, hh:=304
+1014: setchar109 ("6D) h:=4801417+546111=5347528, hh:=339
+1015: setchar97 ("61) h:=5347528+327680=5675208, hh:=360
+1016: setchar99 ("63) h:=5675208+291275=5966483, hh:=378
+1017: setchar114 ("72) h:=5966483+256683=6223166, hh:=394
+1018: setchar111 ("6F) h:=6223166+327680=6550846, hh:=415
+1019: setchar58 ("3A) h:=6550846+182043=6732889, hh:=427
+1020: setchar45 ("2D) h:=6732889+218453=6951342, hh:=441
+1021: setchar62 ("3E) h:=6951342+509738=7461080, hh:=473
+1022: setchar97 ("61) h:=7461080+327680=7788760, hh:=494
+1023: setchar46 ("2E) h:=7788760+182043=7970803, hh:=506
+1024: setchar109 ("6D) h:=7970803+546111=8516914, hh:=541
+1025: setchar97 ("61) h:=8516914+327680=8844594, hh:=562
+1026: setchar99 ("63) h:=8844594+291275=9135869, hh:=580
+1027: setchar114 ("72) h:=9135869+256683=9392552, hh:=596
+1028: setchar111 ("6F) h:=9392552+327680=9720232, hh:=617
+1029: setchar58 ("3A) h:=9720232+182043=9902275, hh:=629
+1030: setchar45 ("2D) h:=9902275+218453=10120728, hh:=643
+1031: setchar62 ("3E) h:=10120728+509738=10630466, hh:=675
+1032: setchar98 ("62) h:=10630466+364085=10994551, hh:=698
+1033: setchar46 ("2E) h:=10994551+182043=11176594, hh:=710
+1034: setchar109 ("6D) h:=11176594+546111=11722705, hh:=745
+1035: setchar97 ("61) h:=11722705+327680=12050385, hh:=765
+1036: setchar99 ("63) h:=12050385+291275=12341660, hh:=783
+1037: setchar114 ("72) h:=12341660+256683=12598343, hh:=799
+1038: setchar111 ("6F) h:=12598343+327680=12926023, hh:=820
+1039: setchar58 ("3A) h:=12926023+182043=13108066, hh:=832
+1040: setchar45 ("2D) h:=13108066+218453=13326519, hh:=846
+1041: setchar62 ("3E) h:=13326519+509738=13836257, hh:=878
+1042: setchar97 ("61) h:=13836257+327680=14163937, hh:=899
+1043: setchar46 ("2E) h:=14163937+182043=14345980, hh:=911
+1044: setchar109 ("6D) h:=14345980+546111=14892091, hh:=945
+1045: setchar97 ("61) h:=14892091+327680=15219771, hh:=966
+1046: setchar99 ("63) h:=15219771+291275=15511046, hh:=984
+1047: setchar114 ("72) h:=15511046+256683=15767729, hh:=1000
+1048: setchar111 ("6F) h:=15767729+327680=16095409, hh:=1021
+1049: setchar58 ("3A) h:=16095409+182043=16277452, hh:=1033
+1050: setchar45 ("2D) h:=16277452+218453=16495905, hh:=1047
+1051: setchar62 ("3E) h:=16495905+509738=17005643, hh:=1079
+1052: setchar98 ("62) h:=17005643+364085=17369728, hh:=1102
+1053: setchar46 ("2E) h:=17369728+182043=17551771, hh:=1114
+[MEANING macro:->a.macro:->b.macro:->a.macro:->b.]
+1054: pop
+level 1:(h=0,v=19529728,w=0,x=0,y=786432,z=0,hh=0,vv=1237)
+1055: pop
+level 0:(h=0,v=42152922,w=0,x=0,y=0,z=0,hh=0,vv=2670)
+1056: down3 1572864 v:=42152922+1572864=43725786, vv:=2770
+1060: eop
diff --git a/Build/source/texk/web2c/euptexdir/tests/eup-pknj-ua.typ b/Build/source/texk/web2c/euptexdir/tests/eup-pknj-ua.typ
new file mode 100644
index 00000000000..d126d4c505e
--- /dev/null
+++ b/Build/source/texk/web2c/euptexdir/tests/eup-pknj-ua.typ
@@ -0,0 +1,829 @@
+Options selected:
+ Starting page = *
+ Maximum number of pages = 1000000
+ Output level = 4 (the works)
+ Resolution = 300.00000000 pixels per inch
+numerator/denominator=25400000/473628672
+magnification=1000; 0.00006334 pixels per DVI unit
+' TeX output 20XX.XX.XX'
+Postamble starts at byte 1062.
+maxv=43725786, maxh=30785863, maxstackdepth=3, totalpages=1
+Font 1: umin10 (JFM)---loaded at size 655360 DVI units
+Font 0: ec-lmr10---loaded at size 655360 DVI units
+
+42: beginning of page 39003
+87: push
+level 0:(h=0,v=0,w=0,x=0,y=0,z=0,hh=0,vv=0)
+88: down3 -917504 v:=0-917504=-917504, vv:=-58
+92: pop
+level 0:(h=0,v=0,w=0,x=0,y=0,z=0,hh=0,vv=0)
+93: down4 42152922 v:=0+42152922=42152922, vv:=2670
+98: push
+level 0:(h=0,v=42152922,w=0,x=0,y=0,z=0,hh=0,vv=2670)
+99: down4 -41497562 v:=42152922-41497562=655360, vv:=42
+104: push
+level 1:(h=0,v=655360,w=0,x=0,y=0,z=0,hh=0,vv=42)
+105: right3 1310720 h:=0+1310720=1310720, hh:=83
+[ ]
+109: fntdef1 0: ec-lmr10
+133: fntnum0 current font is ec-lmr10
+134: setchar65 ("41) h:=1310720+491520=1802240, hh:=114
+135: setchar58 ("3A) h:=1802240+182043=1984283, hh:=126
+136: w3 218453 h:=1984283+218453=2202736, hh:=140
+[A: ]
+140: set1 197("C5) h:=2202736+491520=2694256, hh:=171
+142: set1 191("BF) h:=2694256+491520=3185776, hh:=202
+144: w0 218453 h:=3185776+218453=3404229, hh:=216
+[ ]
+145: set1 197("C5) h:=3404229+491520=3895749, hh:=247
+147: set1 191("BF) h:=3895749+491520=4387269, hh:=278
+149: w0 218453 h:=4387269+218453=4605722, hh:=292
+[ ]
+150: fntdef1 1: umin10
+172: fntnum1 current font is umin10
+173: set2 39003("985B) type=0 h:=4605722+630598=5236320, hh:=332
+[顛]
+176: pop
+level 1:(h=0,v=655360,w=0,x=0,y=0,z=0,hh=0,vv=42)
+177: y3 786432 v:=655360+786432=1441792, vv:=91
+181: push
+level 1:(h=0,v=1441792,w=0,x=0,y=786432,z=0,hh=0,vv=91)
+182: push
+level 2:(h=0,v=1441792,w=0,x=0,y=786432,z=0,hh=0,vv=91)
+183: set2 9632("25A0) type=0 h:=0+630598=630598, hh:=40
+[■]
+186: pop
+level 2:(h=0,v=1441792,w=0,x=0,y=786432,z=0,hh=0,vv=91)
+187: right3 630598 h:=0+630598=630598, hh:=40
+[ ]
+191: fntnum0 current font is ec-lmr10
+192: setchar92 ("5C) h:=630598+327680=958278, hh:=61
+193: setchar109 ("6D) h:=958278+546111=1504389, hh:=96
+194: setchar101 ("65) h:=1504389+291275=1795664, hh:=114
+195: setchar97 ("61) h:=1795664+327680=2123344, hh:=135
+196: setchar110 ("6E) h:=2123344+364085=2487429, hh:=158
+197: setchar105 ("69) h:=2487429+182043=2669472, hh:=170
+198: setchar110 ("6E) h:=2669472+364085=3033557, hh:=193
+199: setchar103 ("67) h:=3033557+327680=3361237, hh:=214
+200: setchar46 ("2E) h:=3361237+182043=3543280, hh:=226
+[\meaning.]
+201: pop
+level 1:(h=0,v=1441792,w=0,x=0,y=786432,z=0,hh=0,vv=91)
+202: y0 786432 v:=1441792+786432=2228224, vv:=141
+203: push
+level 1:(h=0,v=2228224,w=0,x=0,y=786432,z=0,hh=0,vv=141)
+204: right3 1310720 h:=0+1310720=1310720, hh:=83
+208: setchar109 ("6D) h:=1310720+546111=1856831, hh:=118
+209: setchar97 ("61) h:=1856831+327680=2184511, hh:=139
+210: setchar99 ("63) h:=2184511+291275=2475786, hh:=157
+211: setchar114 ("72) h:=2475786+256683=2732469, hh:=173
+212: setchar111 ("6F) h:=2732469+327680=3060149, hh:=194
+213: setchar58 ("3A) h:=3060149+182043=3242192, hh:=206
+214: setchar45 ("2D) h:=3242192+218453=3460645, hh:=220
+215: setchar62 ("3E) h:=3460645+509738=3970383, hh:=252
+216: setchar109 ("6D) h:=3970383+546111=4516494, hh:=287
+217: setchar97 ("61) h:=4516494+327680=4844174, hh:=308
+218: setchar99 ("63) h:=4844174+291275=5135449, hh:=326
+219: setchar114 ("72) h:=5135449+256683=5392132, hh:=342
+220: setchar111 ("6F) h:=5392132+327680=5719812, hh:=363
+221: setchar58 ("3A) h:=5719812+182043=5901855, hh:=375
+222: setchar45 ("2D) h:=5901855+218453=6120308, hh:=389
+223: setchar62 ("3E) h:=6120308+509738=6630046, hh:=421
+[ macro:->macro:->]
+224: set1 197("C5) h:=6630046+491520=7121566, hh:=452
+226: set1 191("BF) h:=7121566+491520=7613086, hh:=483
+228: w3 218453 h:=7613086+218453=7831539, hh:=496
+[ ]
+232: set1 197("C5) h:=7831539+491520=8323059, hh:=527
+234: set1 191("BF) h:=8323059+491520=8814579, hh:=558
+236: w0 218453 h:=8814579+218453=9033032, hh:=572
+[ ]
+237: fntnum1 current font is umin10
+238: set2 39003("985B) type=0 h:=9033032+630598=9663630, hh:=612
+[顛]
+241: pop
+level 1:(h=0,v=2228224,w=0,x=0,y=786432,z=0,hh=0,vv=141)
+242: y0 786432 v:=2228224+786432=3014656, vv:=191
+243: push
+level 1:(h=0,v=3014656,w=0,x=0,y=786432,z=0,hh=0,vv=191)
+244: right3 1310720 h:=0+1310720=1310720, hh:=83
+[ ]
+248: fntnum0 current font is ec-lmr10
+249: setchar109 ("6D) h:=1310720+546111=1856831, hh:=118
+250: setchar97 ("61) h:=1856831+327680=2184511, hh:=139
+251: setchar99 ("63) h:=2184511+291275=2475786, hh:=157
+252: setchar114 ("72) h:=2475786+256683=2732469, hh:=173
+253: setchar111 ("6F) h:=2732469+327680=3060149, hh:=194
+254: setchar58 ("3A) h:=3060149+182043=3242192, hh:=206
+255: setchar45 ("2D) h:=3242192+218453=3460645, hh:=220
+256: setchar62 ("3E) h:=3460645+509738=3970383, hh:=252
+[macro:->]
+257: set1 197("C5) h:=3970383+491520=4461903, hh:=283
+259: set1 191("BF) h:=4461903+491520=4953423, hh:=314
+261: w3 218453 h:=4953423+218453=5171876, hh:=328
+[ ]
+265: set1 197("C5) h:=5171876+491520=5663396, hh:=359
+267: set1 191("BF) h:=5663396+491520=6154916, hh:=390
+269: w0 218453 h:=6154916+218453=6373369, hh:=404
+[ ]
+270: fntnum1 current font is umin10
+271: set2 39003("985B) type=0 h:=6373369+630598=7003967, hh:=444
+[顛]
+274: pop
+level 1:(h=0,v=3014656,w=0,x=0,y=786432,z=0,hh=0,vv=191)
+275: y0 786432 v:=3014656+786432=3801088, vv:=241
+276: push
+level 1:(h=0,v=3801088,w=0,x=0,y=786432,z=0,hh=0,vv=241)
+277: push
+level 2:(h=0,v=3801088,w=0,x=0,y=786432,z=0,hh=0,vv=241)
+278: set2 9632("25A0) type=0 h:=0+630598=630598, hh:=40
+[■]
+281: pop
+level 2:(h=0,v=3801088,w=0,x=0,y=786432,z=0,hh=0,vv=241)
+282: right3 630598 h:=0+630598=630598, hh:=40
+[ ]
+286: fntnum0 current font is ec-lmr10
+287: setchar92 ("5C) h:=630598+327680=958278, hh:=61
+288: setchar106 ("6A) h:=958278+200245=1158523, hh:=74
+289: setchar111 ("6F) h:=1158523+327680=1486203, hh:=95
+290: setchar98 ("62) h:=1486203+364085=1850288, hh:=118
+291: setchar110 ("6E) h:=1850288+364085=2214373, hh:=141
+292: setchar97 ("61) h:=2214373+327680=2542053, hh:=162
+293: setchar109 ("6D) h:=2542053+546111=3088164, hh:=197
+294: setchar101 ("65) h:=3088164+291275=3379439, hh:=215
+295: setchar46 ("2E) h:=3379439+182043=3561482, hh:=227
+[\jobname.]
+296: pop
+level 1:(h=0,v=3801088,w=0,x=0,y=786432,z=0,hh=0,vv=241)
+297: y0 786432 v:=3801088+786432=4587520, vv:=291
+298: push
+level 1:(h=0,v=4587520,w=0,x=0,y=786432,z=0,hh=0,vv=291)
+299: push
+level 2:(h=0,v=4587520,w=0,x=0,y=786432,z=0,hh=0,vv=291)
+300: fntnum1 current font is umin10
+301: set2 9632("25A0) type=0 h:=0+630598=630598, hh:=40
+[■]
+304: pop
+level 2:(h=0,v=4587520,w=0,x=0,y=786432,z=0,hh=0,vv=291)
+305: right3 630598 h:=0+630598=630598, hh:=40
+[ ]
+309: fntnum0 current font is ec-lmr10
+310: setchar111 ("6F) h:=630598+327680=958278, hh:=61
+311: setchar110 ("6E) h:=958278+364085=1322363, hh:=84
+312: setchar101 ("65) h:=1322363+291275=1613638, hh:=102
+313: setchar108 ("6C) h:=1613638+182043=1795681, hh:=114
+314: setchar101 ("65) h:=1795681+291275=2086956, hh:=132
+315: setchar116 ("74) h:=2086956+254870=2341826, hh:=148
+316: setchar116 ("74) h:=2341826+254870=2596696, hh:=164
+317: setchar101 ("65) h:=2596696+291275=2887971, hh:=182
+318: setchar114 ("72) h:=2887971+256683=3144654, hh:=198
+319: setchar46 ("2E) h:=3144654+182043=3326697, hh:=210
+[oneletter.]
+320: pop
+level 1:(h=0,v=4587520,w=0,x=0,y=786432,z=0,hh=0,vv=291)
+321: y0 786432 v:=4587520+786432=5373952, vv:=340
+322: push
+level 1:(h=0,v=5373952,w=0,x=0,y=786432,z=0,hh=0,vv=340)
+323: right3 1310720 h:=0+1310720=1310720, hh:=83
+327: setchar92 ("5C) h:=1310720+327680=1638400, hh:=104
+[ \]
+328: set1 196("C4) h:=1638400+491520=2129920, hh:=135
+330: setchar46 ("2E) h:=2129920+182043=2311963, hh:=147
+331: setchar92 ("5C) h:=2311963+327680=2639643, hh:=168
+[.\]
+332: set1 197("C5) h:=2639643+491520=3131163, hh:=199
+334: setchar46 ("2E) h:=3131163+182043=3313206, hh:=211
+335: setchar92 ("5C) h:=3313206+327680=3640886, hh:=232
+[.\]
+336: set1 255("FF) h:=3640886+327680=3968566, hh:=253
+338: setchar46 ("2E) h:=3968566+182043=4150609, hh:=265
+339: right3 291271 h:=4150609+291271=4441880, hh:=281
+[. ]
+343: set1 191("BF) h:=4441880+491520=4933400, hh:=312
+345: pop
+level 1:(h=0,v=5373952,w=0,x=0,y=786432,z=0,hh=0,vv=340)
+346: y0 786432 v:=5373952+786432=6160384, vv:=390
+347: push
+level 1:(h=0,v=6160384,w=0,x=0,y=786432,z=0,hh=0,vv=390)
+348: push
+level 2:(h=0,v=6160384,w=0,x=0,y=786432,z=0,hh=0,vv=390)
+349: fntnum1 current font is umin10
+350: set2 9632("25A0) type=0 h:=0+630598=630598, hh:=40
+[■]
+353: pop
+level 2:(h=0,v=6160384,w=0,x=0,y=786432,z=0,hh=0,vv=390)
+354: right3 630598 h:=0+630598=630598, hh:=40
+[ ]
+358: fntnum0 current font is ec-lmr10
+359: setchar99 ("63) h:=630598+291275=921873, hh:=58
+360: setchar115 ("73) h:=921873+258506=1180379, hh:=74
+361: setchar110 ("6E) h:=1180379+364085=1544464, hh:=97
+362: setchar97 ("61) h:=1544464+327680=1872144, hh:=118
+363: setchar109 ("6D) h:=1872144+546111=2418255, hh:=153
+364: setchar101 ("65) h:=2418255+291275=2709530, hh:=171
+365: setchar49 ("31) h:=2709530+327680=3037210, hh:=192
+366: setchar46 ("2E) h:=3037210+182043=3219253, hh:=204
+[csname1.]
+367: pop
+level 1:(h=0,v=6160384,w=0,x=0,y=786432,z=0,hh=0,vv=390)
+368: y0 786432 v:=6160384+786432=6946816, vv:=440
+369: push
+level 1:(h=0,v=6946816,w=0,x=0,y=786432,z=0,hh=0,vv=440)
+370: right3 1310720 h:=0+1310720=1310720, hh:=83
+374: setchar92 ("5C) h:=1310720+327680=1638400, hh:=104
+[ \]
+375: set1 197("C5) h:=1638400+491520=2129920, hh:=135
+377: set1 191("BF) h:=2129920+491520=2621440, hh:=166
+379: setchar92 ("5C) h:=2621440+327680=2949120, hh:=187
+[\]
+380: set1 197("C5) h:=2949120+491520=3440640, hh:=218
+382: set1 191("BF) h:=3440640+491520=3932160, hh:=249
+384: setchar44 ("2C) h:=3932160+182043=4114203, hh:=261
+385: w3 218453 h:=4114203+218453=4332656, hh:=274
+389: setchar92 ("5C) h:=4332656+327680=4660336, hh:=295
+[, \]
+390: set1 197("C5) h:=4660336+491520=5151856, hh:=326
+392: set1 191("BF) h:=5151856+491520=5643376, hh:=357
+394: setchar44 ("2C) h:=5643376+182043=5825419, hh:=369
+395: w0 218453 h:=5825419+218453=6043872, hh:=383
+396: setchar92 ("5C) h:=6043872+327680=6371552, hh:=404
+[, \]
+397: fntnum1 current font is umin10
+398: set2 39003("985B) type=0 h:=6371552+630598=7002150, hh:=444
+[顛]
+401: pop
+level 1:(h=0,v=6946816,w=0,x=0,y=786432,z=0,hh=0,vv=440)
+402: y0 786432 v:=6946816+786432=7733248, vv:=490
+403: push
+level 1:(h=0,v=7733248,w=0,x=0,y=786432,z=0,hh=0,vv=490)
+404: right3 1310720 h:=0+1310720=1310720, hh:=83
+[ ]
+408: fntnum0 current font is ec-lmr10
+409: setchar92 ("5C) h:=1310720+327680=1638400, hh:=104
+[\]
+410: fntnum1 current font is umin10
+411: set2 12354("3042) type=0 h:=1638400+630598=2268998, hh:=144
+[あ]
+414: fntnum0 current font is ec-lmr10
+415: setchar61 ("3D) h:=2268998+509738=2778736, hh:=176
+416: setchar62 ("3E) h:=2778736+509738=3288474, hh:=208
+417: right3 218453 h:=3288474+218453=3506927, hh:=222
+421: setchar104 ("68) h:=3506927+364085=3871012, hh:=245
+422: setchar111 ("6F) h:=3871012+327680=4198692, hh:=266
+423: setchar103 ("67) h:=4198692+327680=4526372, hh:=287
+424: setchar101 ("65) h:=4526372+291275=4817647, hh:=305
+425: setchar44 ("2C) h:=4817647+182043=4999690, hh:=317
+[=> hoge,]
+426: pop
+level 1:(h=0,v=7733248,w=0,x=0,y=786432,z=0,hh=0,vv=490)
+427: y0 786432 v:=7733248+786432=8519680, vv:=540
+428: push
+level 1:(h=0,v=8519680,w=0,x=0,y=786432,z=0,hh=0,vv=540)
+429: right3 1310720 h:=0+1310720=1310720, hh:=83
+433: setchar92 ("5C) h:=1310720+327680=1638400, hh:=104
+[ \]
+434: set1 227("E3) h:=1638400+327680=1966080, hh:=125
+436: set1 129("81) h:=1966080+491520=2457600, hh:=156
+438: w2 -18205 h:=2457600-18205=2439395, hh:=155
+441: set1 130("82) h:=2439395+473301=2912696, hh:=185
+443: setchar61 ("3D) h:=2912696+509738=3422434, hh:=217
+444: setchar62 ("3E) h:=3422434+509738=3932172, hh:=249
+445: right3 218453 h:=3932172+218453=4150625, hh:=263
+449: setchar112 ("70) h:=4150625+364085=4514710, hh:=286
+450: setchar105 ("69) h:=4514710+182043=4696753, hh:=298
+451: setchar121 ("79) h:=4696753+345898=5042651, hh:=320
+452: w0 -18205 h:=5042651-18205=5024446, hh:=319
+453: setchar111 ("6F) h:=5024446+327680=5352126, hh:=340
+454: setchar44 ("2C) h:=5352126+182043=5534169, hh:=352
+[=> piyo,]
+455: pop
+level 1:(h=0,v=8519680,w=0,x=0,y=786432,z=0,hh=0,vv=540)
+456: y0 786432 v:=8519680+786432=9306112, vv:=589
+457: push
+level 1:(h=0,v=9306112,w=0,x=0,y=786432,z=0,hh=0,vv=589)
+458: right3 1310720 h:=0+1310720=1310720, hh:=83
+462: setchar92 ("5C) h:=1310720+327680=1638400, hh:=104
+[ \]
+463: set1 227("E3) h:=1638400+327680=1966080, hh:=125
+465: set1 129("81) h:=1966080+491520=2457600, hh:=156
+467: w2 -18205 h:=2457600-18205=2439395, hh:=155
+470: set1 130("82) h:=2439395+473301=2912696, hh:=185
+472: setchar61 ("3D) h:=2912696+509738=3422434, hh:=217
+473: setchar62 ("3E) h:=3422434+509738=3932172, hh:=249
+474: right3 218453 h:=3932172+218453=4150625, hh:=263
+478: setchar112 ("70) h:=4150625+364085=4514710, hh:=286
+479: setchar105 ("69) h:=4514710+182043=4696753, hh:=298
+480: setchar121 ("79) h:=4696753+345898=5042651, hh:=320
+481: w0 -18205 h:=5042651-18205=5024446, hh:=319
+482: setchar111 ("6F) h:=5024446+327680=5352126, hh:=340
+483: setchar46 ("2E) h:=5352126+182043=5534169, hh:=352
+[=> piyo.]
+484: pop
+level 1:(h=0,v=9306112,w=0,x=0,y=786432,z=0,hh=0,vv=589)
+485: y0 786432 v:=9306112+786432=10092544, vv:=639
+486: push
+level 1:(h=0,v=10092544,w=0,x=0,y=786432,z=0,hh=0,vv=639)
+487: push
+level 2:(h=0,v=10092544,w=0,x=0,y=786432,z=0,hh=0,vv=639)
+488: fntnum1 current font is umin10
+489: set2 9632("25A0) type=0 h:=0+630598=630598, hh:=40
+[■]
+492: pop
+level 2:(h=0,v=10092544,w=0,x=0,y=786432,z=0,hh=0,vv=639)
+493: right3 630598 h:=0+630598=630598, hh:=40
+[ ]
+497: fntnum0 current font is ec-lmr10
+498: setchar48 ("30) h:=630598+327680=958278, hh:=61
+499: setchar120 ("78) h:=958278+345898=1304176, hh:=83
+500: setchar70 ("46) h:=1304176+427818=1731994, hh:=110
+501: setchar70 ("46) h:=1731994+427818=2159812, hh:=137
+502: setchar46 ("2E) h:=2159812+182043=2341855, hh:=149
+[0xFF.]
+503: pop
+level 1:(h=0,v=10092544,w=0,x=0,y=786432,z=0,hh=0,vv=639)
+504: y0 786432 v:=10092544+786432=10878976, vv:=689
+505: push
+level 1:(h=0,v=10878976,w=0,x=0,y=786432,z=0,hh=0,vv=689)
+506: right3 1310720 h:=0+1310720=1310720, hh:=83
+[ ]
+510: fntnum1 current font is umin10
+511: set2 12354("3042) type=0 h:=1310720+630598=1941318, hh:=123
+[あ]
+514: fntnum0 current font is ec-lmr10
+515: setchar44 ("2C) h:=1941318+182043=2123361, hh:=135
+516: w3 218453 h:=2123361+218453=2341814, hh:=148
+520: setchar40 ("28) h:=2341814+254870=2596684, hh:=164
+521: setchar92 ("5C) h:=2596684+327680=2924364, hh:=185
+[, (\]
+522: set1 255("FF) h:=2924364+327680=3252044, hh:=206
+524: set1 197("C5) h:=3252044+491520=3743564, hh:=237
+526: set1 255("FF) h:=3743564+327680=4071244, hh:=258
+528: set1 191("BF) h:=4071244+491520=4562764, hh:=289
+530: setchar58 ("3A) h:=4562764+182043=4744807, hh:=301
+531: setchar109 ("6D) h:=4744807+546111=5290918, hh:=336
+532: setchar97 ("61) h:=5290918+327680=5618598, hh:=357
+533: setchar99 ("63) h:=5618598+291275=5909873, hh:=375
+534: setchar114 ("72) h:=5909873+256683=6166556, hh:=391
+535: setchar111 ("6F) h:=6166556+327680=6494236, hh:=412
+536: setchar58 ("3A) h:=6494236+182043=6676279, hh:=424
+537: setchar45 ("2D) h:=6676279+218453=6894732, hh:=438
+538: setchar62 ("3E) h:=6894732+509738=7404470, hh:=470
+[:macro:->]
+539: fntnum1 current font is umin10
+540: set2 12354("3042) type=0 h:=7404470+630598=8035068, hh:=510
+[あ]
+543: fntnum0 current font is ec-lmr10
+544: setchar41 ("29) h:=8035068+254870=8289938, hh:=526
+545: setchar44 ("2C) h:=8289938+182043=8471981, hh:=538
+546: w0 218453 h:=8471981+218453=8690434, hh:=550
+547: setchar92 ("5C) h:=8690434+327680=9018114, hh:=571
+[), \]
+548: set1 255("FF) h:=9018114+327680=9345794, hh:=592
+550: set1 197("C5) h:=9345794+491520=9837314, hh:=623
+552: set1 255("FF) h:=9837314+327680=10164994, hh:=644
+554: set1 191("BF) h:=10164994+491520=10656514, hh:=675
+556: setchar44 ("2C) h:=10656514+182043=10838557, hh:=687
+557: w0 218453 h:=10838557+218453=11057010, hh:=700
+558: setchar109 ("6D) h:=11057010+546111=11603121, hh:=735
+559: setchar97 ("61) h:=11603121+327680=11930801, hh:=756
+560: setchar99 ("63) h:=11930801+291275=12222076, hh:=774
+561: setchar114 ("72) h:=12222076+256683=12478759, hh:=790
+562: setchar111 ("6F) h:=12478759+327680=12806439, hh:=811
+563: setchar58 ("3A) h:=12806439+182043=12988482, hh:=823
+564: setchar45 ("2D) h:=12988482+218453=13206935, hh:=837
+565: setchar62 ("3E) h:=13206935+509738=13716673, hh:=869
+566: setchar40 ("28) h:=13716673+254870=13971543, hh:=885
+567: setchar92 ("5C) h:=13971543+327680=14299223, hh:=906
+[, macro:->(\]
+568: set1 255("FF) h:=14299223+327680=14626903, hh:=927
+570: set1 197("C5) h:=14626903+491520=15118423, hh:=958
+572: set1 255("FF) h:=15118423+327680=15446103, hh:=979
+574: set1 191("BF) h:=15446103+491520=15937623, hh:=1010
+576: setchar58 ("3A) h:=15937623+182043=16119666, hh:=1022
+577: setchar109 ("6D) h:=16119666+546111=16665777, hh:=1057
+578: setchar97 ("61) h:=16665777+327680=16993457, hh:=1078
+579: setchar99 ("63) h:=16993457+291275=17284732, hh:=1096
+580: setchar114 ("72) h:=17284732+256683=17541415, hh:=1112
+581: setchar111 ("6F) h:=17541415+327680=17869095, hh:=1133
+582: setchar58 ("3A) h:=17869095+182043=18051138, hh:=1145
+583: setchar45 ("2D) h:=18051138+218453=18269591, hh:=1159
+584: setchar62 ("3E) h:=18269591+509738=18779329, hh:=1191
+[:macro:->]
+585: fntnum1 current font is umin10
+586: set2 12354("3042) type=0 h:=18779329+630598=19409927, hh:=1231
+[あ]
+589: fntnum0 current font is ec-lmr10
+590: setchar41 ("29) h:=19409927+254870=19664797, hh:=1247
+591: setchar46 ("2E) h:=19664797+182043=19846840, hh:=1259
+[).]
+592: pop
+level 1:(h=0,v=10878976,w=0,x=0,y=786432,z=0,hh=0,vv=689)
+593: y0 786432 v:=10878976+786432=11665408, vv:=739
+594: push
+level 1:(h=0,v=11665408,w=0,x=0,y=786432,z=0,hh=0,vv=739)
+595: right3 1310720 h:=0+1310720=1310720, hh:=83
+[ ]
+599: fntnum1 current font is umin10
+600: set2 12356("3044) type=0 h:=1310720+630598=1941318, hh:=123
+[い]
+603: fntnum0 current font is ec-lmr10
+604: setchar44 ("2C) h:=1941318+182043=2123361, hh:=135
+605: w3 218453 h:=2123361+218453=2341814, hh:=148
+609: setchar40 ("28) h:=2341814+254870=2596684, hh:=164
+610: setchar92 ("5C) h:=2596684+327680=2924364, hh:=185
+[, (\]
+611: set1 255("FF) h:=2924364+327680=3252044, hh:=206
+613: setchar58 ("3A) h:=3252044+182043=3434087, hh:=218
+614: setchar109 ("6D) h:=3434087+546111=3980198, hh:=253
+615: setchar97 ("61) h:=3980198+327680=4307878, hh:=274
+616: setchar99 ("63) h:=4307878+291275=4599153, hh:=292
+617: setchar114 ("72) h:=4599153+256683=4855836, hh:=308
+618: setchar111 ("6F) h:=4855836+327680=5183516, hh:=329
+619: setchar58 ("3A) h:=5183516+182043=5365559, hh:=341
+620: setchar45 ("2D) h:=5365559+218453=5584012, hh:=355
+621: setchar62 ("3E) h:=5584012+509738=6093750, hh:=387
+[:macro:->]
+622: fntnum1 current font is umin10
+623: set2 12356("3044) type=0 h:=6093750+630598=6724348, hh:=427
+[い]
+626: fntnum0 current font is ec-lmr10
+627: setchar41 ("29) h:=6724348+254870=6979218, hh:=443
+628: setchar44 ("2C) h:=6979218+182043=7161261, hh:=455
+629: w0 218453 h:=7161261+218453=7379714, hh:=467
+630: setchar92 ("5C) h:=7379714+327680=7707394, hh:=488
+[), \]
+631: set1 255("FF) h:=7707394+327680=8035074, hh:=509
+633: setchar44 ("2C) h:=8035074+182043=8217117, hh:=521
+634: w0 218453 h:=8217117+218453=8435570, hh:=534
+635: setchar109 ("6D) h:=8435570+546111=8981681, hh:=569
+636: setchar97 ("61) h:=8981681+327680=9309361, hh:=590
+637: setchar99 ("63) h:=9309361+291275=9600636, hh:=608
+638: setchar114 ("72) h:=9600636+256683=9857319, hh:=624
+639: setchar111 ("6F) h:=9857319+327680=10184999, hh:=645
+640: setchar58 ("3A) h:=10184999+182043=10367042, hh:=657
+641: setchar45 ("2D) h:=10367042+218453=10585495, hh:=671
+642: setchar62 ("3E) h:=10585495+509738=11095233, hh:=703
+643: setchar40 ("28) h:=11095233+254870=11350103, hh:=719
+644: setchar92 ("5C) h:=11350103+327680=11677783, hh:=740
+[, macro:->(\]
+645: set1 255("FF) h:=11677783+327680=12005463, hh:=761
+647: setchar58 ("3A) h:=12005463+182043=12187506, hh:=773
+648: setchar109 ("6D) h:=12187506+546111=12733617, hh:=808
+649: setchar97 ("61) h:=12733617+327680=13061297, hh:=829
+650: setchar99 ("63) h:=13061297+291275=13352572, hh:=847
+651: setchar114 ("72) h:=13352572+256683=13609255, hh:=863
+652: setchar111 ("6F) h:=13609255+327680=13936935, hh:=884
+653: setchar58 ("3A) h:=13936935+182043=14118978, hh:=896
+654: setchar45 ("2D) h:=14118978+218453=14337431, hh:=910
+655: setchar62 ("3E) h:=14337431+509738=14847169, hh:=942
+[:macro:->]
+656: fntnum1 current font is umin10
+657: set2 12356("3044) type=0 h:=14847169+630598=15477767, hh:=982
+[い]
+660: fntnum0 current font is ec-lmr10
+661: setchar41 ("29) h:=15477767+254870=15732637, hh:=998
+662: setchar46 ("2E) h:=15732637+182043=15914680, hh:=1010
+[).]
+663: pop
+level 1:(h=0,v=11665408,w=0,x=0,y=786432,z=0,hh=0,vv=739)
+664: y0 786432 v:=11665408+786432=12451840, vv:=789
+665: push
+level 1:(h=0,v=12451840,w=0,x=0,y=786432,z=0,hh=0,vv=789)
+666: push
+level 2:(h=0,v=12451840,w=0,x=0,y=786432,z=0,hh=0,vv=789)
+667: fntnum1 current font is umin10
+668: set2 9632("25A0) type=0 h:=0+630598=630598, hh:=40
+[■]
+671: pop
+level 2:(h=0,v=12451840,w=0,x=0,y=786432,z=0,hh=0,vv=789)
+672: right3 630598 h:=0+630598=630598, hh:=40
+[ ]
+676: fntnum0 current font is ec-lmr10
+677: setchar99 ("63) h:=630598+291275=921873, hh:=58
+678: setchar111 ("6F) h:=921873+327680=1249553, hh:=79
+679: setchar110 ("6E) h:=1249553+364085=1613638, hh:=102
+680: right2 -18205 h:=1613638-18205=1595433, hh:=101
+683: setchar116 ("74) h:=1595433+254870=1850303, hh:=117
+684: setchar101 ("65) h:=1850303+291275=2141578, hh:=135
+685: setchar120 ("78) h:=2141578+345898=2487476, hh:=157
+686: setchar116 ("74) h:=2487476+254870=2742346, hh:=173
+687: setchar115 ("73) h:=2742346+258506=3000852, hh:=189
+688: setchar46 ("2E) h:=3000852+182043=3182895, hh:=201
+[contexts.]
+689: pop
+level 1:(h=0,v=12451840,w=0,x=0,y=786432,z=0,hh=0,vv=789)
+690: y0 786432 v:=12451840+786432=13238272, vv:=839
+691: push
+level 1:(h=0,v=13238272,w=0,x=0,y=786432,z=0,hh=0,vv=839)
+692: right3 1310720 h:=0+1310720=1310720, hh:=83
+[ ]
+696: fntnum1 current font is umin10
+697: set2 12354("3042) type=0 h:=1310720+630598=1941318, hh:=123
+[あ]
+700: fntnum0 current font is ec-lmr10
+701: set1 197("C5) h:=1941318+491520=2432838, hh:=154
+703: set1 191("BF) h:=2432838+491520=2924358, hh:=185
+705: w3 218453 h:=2924358+218453=3142811, hh:=199
+709: setchar97 ("61) h:=3142811+327680=3470491, hh:=220
+[ a]
+710: set1 197("C5) h:=3470491+491520=3962011, hh:=251
+712: set1 191("BF) h:=3962011+491520=4453531, hh:=282
+714: w0 218453 h:=4453531+218453=4671984, hh:=296
+[ ]
+715: set1 197("C5) h:=4671984+491520=5163504, hh:=327
+717: set1 191("BF) h:=5163504+491520=5655024, hh:=358
+719: w0 218453 h:=5655024+218453=5873477, hh:=372
+[ ]
+720: fntnum1 current font is umin10
+721: set2 39003("985B) type=0 h:=5873477+630598=6504075, hh:=412
+724: w0 218453 h:=6504075+218453=6722528, hh:=426
+[顛 ]
+725: fntnum0 current font is ec-lmr10
+726: set1 197("C5) h:=6722528+491520=7214048, hh:=457
+728: set1 191("BF) h:=7214048+491520=7705568, hh:=488
+730: w0 218453 h:=7705568+218453=7924021, hh:=502
+[ ]
+731: set1 197("C5) h:=7924021+491520=8415541, hh:=533
+733: set1 191("BF) h:=8415541+491520=8907061, hh:=564
+735: w0 218453 h:=8907061+218453=9125514, hh:=578
+[ ]
+736: fntnum1 current font is umin10
+737: set2 39003("985B) type=0 h:=9125514+630598=9756112, hh:=618
+740: x3 436906 h:=9756112+436906=10193018, hh:=646
+[顛 ]
+744: fntnum0 current font is ec-lmr10
+745: set1 197("C5) h:=10193018+491520=10684538, hh:=677
+747: set1 191("BF) h:=10684538+491520=11176058, hh:=708
+749: w0 218453 h:=11176058+218453=11394511, hh:=722
+[ ]
+750: fntnum1 current font is umin10
+751: set2 39003("985B) type=0 h:=11394511+630598=12025109, hh:=762
+754: x0 436906 h:=12025109+436906=12462015, hh:=789
+755: set2 12356("3044) type=0 h:=12462015+630598=13092613, hh:=829
+[顛 い]
+758: fntnum0 current font is ec-lmr10
+759: setchar109 ("6D) h:=13092613+546111=13638724, hh:=864
+760: setchar97 ("61) h:=13638724+327680=13966404, hh:=885
+761: setchar99 ("63) h:=13966404+291275=14257679, hh:=903
+762: setchar114 ("72) h:=14257679+256683=14514362, hh:=919
+763: setchar111 ("6F) h:=14514362+327680=14842042, hh:=940
+764: setchar58 ("3A) h:=14842042+182043=15024085, hh:=952
+765: setchar45 ("2D) h:=15024085+218453=15242538, hh:=966
+766: setchar62 ("3E) h:=15242538+509738=15752276, hh:=998
+[macro:->]
+767: set1 197("C5) h:=15752276+491520=16243796, hh:=1029
+769: set1 191("BF) h:=16243796+491520=16735316, hh:=1060
+771: w0 218453 h:=16735316+218453=16953769, hh:=1074
+[ ]
+772: set1 197("C5) h:=16953769+491520=17445289, hh:=1105
+774: set1 191("BF) h:=17445289+491520=17936809, hh:=1136
+776: w0 218453 h:=17936809+218453=18155262, hh:=1150
+[ ]
+777: fntnum1 current font is umin10
+778: set2 39003("985B) type=0 h:=18155262+630598=18785860, hh:=1190
+[顛]
+781: pop
+level 1:(h=0,v=13238272,w=0,x=0,y=786432,z=0,hh=0,vv=839)
+782: y0 786432 v:=13238272+786432=14024704, vv:=888
+783: push
+level 1:(h=0,v=14024704,w=0,x=0,y=786432,z=0,hh=0,vv=888)
+784: right3 1310720 h:=0+1310720=1310720, hh:=83
+788: set2 12354("3042) type=0 h:=1310720+630598=1941318, hh:=123
+[ あ]
+791: fntnum0 current font is ec-lmr10
+792: set1 197("C5) h:=1941318+491520=2432838, hh:=154
+794: set1 191("BF) h:=2432838+491520=2924358, hh:=185
+796: w3 218453 h:=2924358+218453=3142811, hh:=199
+[ ]
+800: set1 197("C5) h:=3142811+491520=3634331, hh:=230
+802: set1 191("BF) h:=3634331+491520=4125851, hh:=261
+804: fntnum1 current font is umin10
+805: set2 39003("985B) type=0 h:=4125851+630598=4756449, hh:=301
+[顛]
+808: fntnum0 current font is ec-lmr10
+809: set1 197("C5) h:=4756449+491520=5247969, hh:=332
+811: set1 191("BF) h:=5247969+491520=5739489, hh:=363
+813: w0 218453 h:=5739489+218453=5957942, hh:=377
+814: setchar97 ("61) h:=5957942+327680=6285622, hh:=398
+[ a]
+815: set1 197("C5) h:=6285622+491520=6777142, hh:=429
+817: set1 191("BF) h:=6777142+491520=7268662, hh:=460
+819: w0 218453 h:=7268662+218453=7487115, hh:=474
+[ ]
+820: set1 197("C5) h:=7487115+491520=7978635, hh:=505
+822: set1 191("BF) h:=7978635+491520=8470155, hh:=536
+824: w0 218453 h:=8470155+218453=8688608, hh:=550
+[ ]
+825: fntnum1 current font is umin10
+826: set2 39003("985B) type=0 h:=8688608+630598=9319206, hh:=590
+829: w0 218453 h:=9319206+218453=9537659, hh:=604
+[顛 ]
+830: fntnum0 current font is ec-lmr10
+831: set1 197("C5) h:=9537659+491520=10029179, hh:=635
+833: set1 191("BF) h:=10029179+491520=10520699, hh:=666
+835: w0 218453 h:=10520699+218453=10739152, hh:=680
+[ ]
+836: set1 197("C5) h:=10739152+491520=11230672, hh:=711
+838: set1 191("BF) h:=11230672+491520=11722192, hh:=742
+840: w0 218453 h:=11722192+218453=11940645, hh:=756
+[ ]
+841: fntnum1 current font is umin10
+842: set2 39003("985B) type=0 h:=11940645+630598=12571243, hh:=796
+845: right3 436906 h:=12571243+436906=13008149, hh:=824
+[顛 ]
+849: fntnum0 current font is ec-lmr10
+850: set1 197("C5) h:=13008149+491520=13499669, hh:=855
+852: set1 191("BF) h:=13499669+491520=13991189, hh:=886
+854: w0 218453 h:=13991189+218453=14209642, hh:=900
+[ ]
+855: fntnum1 current font is umin10
+856: set2 39003("985B) type=0 h:=14209642+630598=14840240, hh:=940
+859: w0 218453 h:=14840240+218453=15058693, hh:=954
+860: set2 12356("3044) type=0 h:=15058693+630598=15689291, hh:=994
+[顛 い]
+863: fntnum0 current font is ec-lmr10
+864: set1 255("FF) h:=15689291+327680=16016971, hh:=1015
+866: pop
+level 1:(h=0,v=14024704,w=0,x=0,y=786432,z=0,hh=0,vv=888)
+867: y0 786432 v:=14024704+786432=14811136, vv:=938
+868: push
+level 1:(h=0,v=14811136,w=0,x=0,y=786432,z=0,hh=0,vv=938)
+869: right3 1310720 h:=0+1310720=1310720, hh:=83
+[ ]
+873: fntnum1 current font is umin10
+874: set2 12356("3044) type=0 h:=1310720+630598=1941318, hh:=123
+[い]
+877: pop
+level 1:(h=0,v=14811136,w=0,x=0,y=786432,z=0,hh=0,vv=938)
+878: y0 786432 v:=14811136+786432=15597568, vv:=988
+879: push
+level 1:(h=0,v=15597568,w=0,x=0,y=786432,z=0,hh=0,vv=988)
+880: right3 1310720 h:=0+1310720=1310720, hh:=83
+[ ]
+884: fntnum0 current font is ec-lmr10
+885: setchar46 ("2E) h:=1310720+182043=1492763, hh:=95
+886: setchar97 ("61) h:=1492763+327680=1820443, hh:=116
+887: setchar46 ("2E) h:=1820443+182043=2002486, hh:=128
+888: setchar46 ("2E) h:=2002486+182043=2184529, hh:=140
+889: setchar46 ("2E) h:=2184529+182043=2366572, hh:=152
+890: setchar99 ("63) h:=2366572+291275=2657847, hh:=170
+891: setchar46 ("2E) h:=2657847+182043=2839890, hh:=182
+892: setchar98 ("62) h:=2839890+364085=3203975, hh:=205
+893: setchar46 ("2E) h:=3203975+182043=3386018, hh:=216
+894: setchar46 ("2E) h:=3386018+182043=3568061, hh:=228
+[.a...c.b..]
+895: pop
+level 1:(h=0,v=15597568,w=0,x=0,y=786432,z=0,hh=0,vv=988)
+896: y0 786432 v:=15597568+786432=16384000, vv:=1038
+897: push
+level 1:(h=0,v=16384000,w=0,x=0,y=786432,z=0,hh=0,vv=1038)
+898: right3 1310720 h:=0+1310720=1310720, hh:=83
+902: setchar46 ("2E) h:=1310720+182043=1492763, hh:=95
+903: setchar97 ("61) h:=1492763+327680=1820443, hh:=116
+904: setchar46 ("2E) h:=1820443+182043=2002486, hh:=128
+905: setchar46 ("2E) h:=2002486+182043=2184529, hh:=140
+906: setchar46 ("2E) h:=2184529+182043=2366572, hh:=152
+907: setchar99 ("63) h:=2366572+291275=2657847, hh:=170
+908: setchar46 ("2E) h:=2657847+182043=2839890, hh:=182
+909: setchar98 ("62) h:=2839890+364085=3203975, hh:=205
+910: setchar46 ("2E) h:=3203975+182043=3386018, hh:=216
+911: setchar46 ("2E) h:=3386018+182043=3568061, hh:=228
+[ .a...c.b..]
+912: pop
+level 1:(h=0,v=16384000,w=0,x=0,y=786432,z=0,hh=0,vv=1038)
+913: y0 786432 v:=16384000+786432=17170432, vv:=1088
+914: push
+level 1:(h=0,v=17170432,w=0,x=0,y=786432,z=0,hh=0,vv=1088)
+915: push
+level 2:(h=0,v=17170432,w=0,x=0,y=786432,z=0,hh=0,vv=1088)
+916: fntnum1 current font is umin10
+917: set2 9632("25A0) type=0 h:=0+630598=630598, hh:=40
+[■]
+920: pop
+level 2:(h=0,v=17170432,w=0,x=0,y=786432,z=0,hh=0,vv=1088)
+921: right3 630598 h:=0+630598=630598, hh:=40
+[ ]
+925: fntnum0 current font is ec-lmr10
+926: setchar99 ("63) h:=630598+291275=921873, hh:=58
+927: setchar115 ("73) h:=921873+258506=1180379, hh:=74
+928: setchar110 ("6E) h:=1180379+364085=1544464, hh:=97
+929: setchar97 ("61) h:=1544464+327680=1872144, hh:=118
+930: setchar109 ("6D) h:=1872144+546111=2418255, hh:=153
+931: setchar101 ("65) h:=2418255+291275=2709530, hh:=171
+932: setchar50 ("32) h:=2709530+327680=3037210, hh:=192
+933: setchar46 ("2E) h:=3037210+182043=3219253, hh:=204
+[csname2.]
+934: pop
+level 1:(h=0,v=17170432,w=0,x=0,y=786432,z=0,hh=0,vv=1088)
+935: y0 786432 v:=17170432+786432=17956864, vv:=1137
+936: push
+level 1:(h=0,v=17956864,w=0,x=0,y=786432,z=0,hh=0,vv=1137)
+937: right3 1310720 h:=0+1310720=1310720, hh:=83
+941: setchar83 ("53) h:=1310720+364085=1674805, hh:=106
+942: setchar72 ("48) h:=1674805+491520=2166325, hh:=137
+943: setchar79 ("4F) h:=2166325+509738=2676063, hh:=169
+944: right2 -18205 h:=2676063-18205=2657858, hh:=168
+947: setchar87 ("57) h:=2657858+673578=3331436, hh:=211
+948: right3 218453 h:=3331436+218453=3549889, hh:=225
+952: setchar46 ("2E) h:=3549889+182043=3731932, hh:=237
+953: setchar46 ("2E) h:=3731932+182043=3913975, hh:=249
+954: setchar46 ("2E) h:=3913975+182043=4096018, hh:=261
+[ SHOW ...]
+955: pop
+level 1:(h=0,v=17956864,w=0,x=0,y=786432,z=0,hh=0,vv=1137)
+956: y0 786432 v:=17956864+786432=18743296, vv:=1187
+957: push
+level 1:(h=0,v=18743296,w=0,x=0,y=786432,z=0,hh=0,vv=1187)
+958: right3 1310720 h:=0+1310720=1310720, hh:=83
+962: setchar83 ("53) h:=1310720+364085=1674805, hh:=106
+963: setchar84 ("54) h:=1674805+473301=2148106, hh:=136
+964: setchar82 ("52) h:=2148106+482410=2630516, hh:=167
+965: setchar73 ("49) h:=2630516+236650=2867166, hh:=182
+966: setchar78 ("4E) h:=2867166+491520=3358686, hh:=213
+967: setchar71 ("47) h:=3358686+514261=3872947, hh:=246
+968: right3 218453 h:=3872947+218453=4091400, hh:=259
+972: setchar92 ("5C) h:=4091400+327680=4419080, hh:=280
+[ STRING \]
+973: set1 197("C5) h:=4419080+491520=4910600, hh:=311
+975: set1 191("BF) h:=4910600+491520=5402120, hh:=342
+977: setchar46 ("2E) h:=5402120+182043=5584163, hh:=354
+978: setchar92 ("5C) h:=5584163+327680=5911843, hh:=375
+[.\]
+979: fntnum1 current font is umin10
+980: set2 39003("985B) type=0 h:=5911843+630598=6542441, hh:=415
+[顛]
+983: fntnum0 current font is ec-lmr10
+984: setchar46 ("2E) h:=6542441+182043=6724484, hh:=427
+985: setchar92 ("5C) h:=6724484+327680=7052164, hh:=448
+[.\]
+986: set1 197("C5) h:=7052164+491520=7543684, hh:=479
+988: set1 191("BF) h:=7543684+491520=8035204, hh:=510
+990: setchar46 ("2E) h:=8035204+182043=8217247, hh:=522
+991: setchar92 ("5C) h:=8217247+327680=8544927, hh:=543
+[.\]
+992: fntnum1 current font is umin10
+993: set2 39003("985B) type=0 h:=8544927+630598=9175525, hh:=583
+[顛]
+996: pop
+level 1:(h=0,v=18743296,w=0,x=0,y=786432,z=0,hh=0,vv=1187)
+997: y0 786432 v:=18743296+786432=19529728, vv:=1237
+998: push
+level 1:(h=0,v=19529728,w=0,x=0,y=786432,z=0,hh=0,vv=1237)
+999: right3 1310720 h:=0+1310720=1310720, hh:=83
+[ ]
+1003: fntnum0 current font is ec-lmr10
+1004: setchar77 ("4D) h:=1310720+600768=1911488, hh:=121
+1005: setchar69 ("45) h:=1911488+446005=2357493, hh:=149
+1006: setchar65 ("41) h:=2357493+491520=2849013, hh:=180
+1007: setchar78 ("4E) h:=2849013+491520=3340533, hh:=211
+1008: setchar73 ("49) h:=3340533+236650=3577183, hh:=226
+1009: setchar78 ("4E) h:=3577183+491520=4068703, hh:=257
+1010: setchar71 ("47) h:=4068703+514261=4582964, hh:=290
+1011: right3 218453 h:=4582964+218453=4801417, hh:=304
+1015: setchar109 ("6D) h:=4801417+546111=5347528, hh:=339
+1016: setchar97 ("61) h:=5347528+327680=5675208, hh:=360
+1017: setchar99 ("63) h:=5675208+291275=5966483, hh:=378
+1018: setchar114 ("72) h:=5966483+256683=6223166, hh:=394
+1019: setchar111 ("6F) h:=6223166+327680=6550846, hh:=415
+1020: setchar58 ("3A) h:=6550846+182043=6732889, hh:=427
+1021: setchar45 ("2D) h:=6732889+218453=6951342, hh:=441
+1022: setchar62 ("3E) h:=6951342+509738=7461080, hh:=473
+1023: setchar97 ("61) h:=7461080+327680=7788760, hh:=494
+1024: setchar46 ("2E) h:=7788760+182043=7970803, hh:=506
+1025: setchar109 ("6D) h:=7970803+546111=8516914, hh:=541
+1026: setchar97 ("61) h:=8516914+327680=8844594, hh:=562
+1027: setchar99 ("63) h:=8844594+291275=9135869, hh:=580
+1028: setchar114 ("72) h:=9135869+256683=9392552, hh:=596
+1029: setchar111 ("6F) h:=9392552+327680=9720232, hh:=617
+1030: setchar58 ("3A) h:=9720232+182043=9902275, hh:=629
+1031: setchar45 ("2D) h:=9902275+218453=10120728, hh:=643
+1032: setchar62 ("3E) h:=10120728+509738=10630466, hh:=675
+1033: setchar98 ("62) h:=10630466+364085=10994551, hh:=698
+1034: setchar46 ("2E) h:=10994551+182043=11176594, hh:=710
+1035: setchar109 ("6D) h:=11176594+546111=11722705, hh:=745
+1036: setchar97 ("61) h:=11722705+327680=12050385, hh:=765
+1037: setchar99 ("63) h:=12050385+291275=12341660, hh:=783
+1038: setchar114 ("72) h:=12341660+256683=12598343, hh:=799
+1039: setchar111 ("6F) h:=12598343+327680=12926023, hh:=820
+1040: setchar58 ("3A) h:=12926023+182043=13108066, hh:=832
+1041: setchar45 ("2D) h:=13108066+218453=13326519, hh:=846
+1042: setchar62 ("3E) h:=13326519+509738=13836257, hh:=878
+1043: setchar97 ("61) h:=13836257+327680=14163937, hh:=899
+1044: setchar46 ("2E) h:=14163937+182043=14345980, hh:=911
+1045: setchar109 ("6D) h:=14345980+546111=14892091, hh:=945
+1046: setchar97 ("61) h:=14892091+327680=15219771, hh:=966
+1047: setchar99 ("63) h:=15219771+291275=15511046, hh:=984
+1048: setchar114 ("72) h:=15511046+256683=15767729, hh:=1000
+1049: setchar111 ("6F) h:=15767729+327680=16095409, hh:=1021
+1050: setchar58 ("3A) h:=16095409+182043=16277452, hh:=1033
+1051: setchar45 ("2D) h:=16277452+218453=16495905, hh:=1047
+1052: setchar62 ("3E) h:=16495905+509738=17005643, hh:=1079
+1053: setchar98 ("62) h:=17005643+364085=17369728, hh:=1102
+1054: setchar46 ("2E) h:=17369728+182043=17551771, hh:=1114
+[MEANING macro:->a.macro:->b.macro:->a.macro:->b.]
+1055: pop
+level 1:(h=0,v=19529728,w=0,x=0,y=786432,z=0,hh=0,vv=1237)
+1056: pop
+level 0:(h=0,v=42152922,w=0,x=0,y=0,z=0,hh=0,vv=2670)
+1057: down3 1572864 v:=42152922+1572864=43725786, vv:=2770
+1061: eop
diff --git a/Build/source/texk/web2c/euptexdir/tests/free_ixsp.log b/Build/source/texk/web2c/euptexdir/tests/free_ixsp.log
new file mode 100644
index 00000000000..05639d0ffdc
--- /dev/null
+++ b/Build/source/texk/web2c/euptexdir/tests/free_ixsp.log
@@ -0,0 +1,29 @@
+entering extended mode
+ restricted \write18 enabled.
+ %&-line parsing enabled.
+**eup-free_ixsp.tex
+(./eup-free_ixsp.tex
+\@tempcnta=\count26
+\@tempcntb=\count27
+\@tempcntc=\count28
+\CNT=\count29
+\CNTA=\count30
+0. 1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. 17. 18. 19. 20. 21.
+22. 23. 24. 25. 26. 27. 28. 29. 30. 31. 32. 33. 34. 35. 36. 37. 38. 39. 40.
+41. 42. 43. 44. 45. 46. 47. 48. 49. 50. 51. 52. 53. 54. 55. 56. 57. 58. 59.
+60. 61. 62. 63. 64. 65. 66. 67. 68. 69. 70. 71. 72. 73. 74. 75. 76. 77. 78.
+79. 80. 81. 82. 83. 84. 85. 86. 87. 88. 89. 90. 91. 92. 93. 94. 95. 96. 97.
+98. 99. 100. 101. 102. 103. 104. 105. 106. 107. 108. 109. 110. 111. 112. 113.
+114. 115. 116. 117. 118. 119. 120. 121. 122. 123. 124. 125. 126. 127. 128.
+129. 130. 131. 132. 133. 134. 135. 136. 137. 138. 139. 140. 141. 142. 143.
+144. 145. 146. 147. 148. 149. 150. 151. 152. 153. 154. 155. 156. 157. 158.
+159. 160. 161. 162. 163. 164. 165. 166. 167. 168. 169. 170. 171. 172. 173.
+174. 175. 176. 177. 178. 179. 180. 181. 182. 183. 184. 185. 186. 187. 188.
+189. 190. 191. 192. 193. 194. 195. 196. 197. 198. 199. 200. 201. 202. 203.
+204. 205. 206. 207. 208. 209. 210. 211. 212. 213. 214. 215. 216. 217. 218.
+219. 220. 221. 222. 223. 224. 225. 226. 227. 228. 229. 230. 231. 232. 233.
+234. 235. 236. 237. 238. 239. 240. 241. 242. 243. 244. 245. 246. 247. 248.
+249. 250. 251. 252. 253. 254. 255.
+\CNTB=\count31
+ )
+No pages of output.
diff --git a/Build/source/texk/web2c/ptexdir/tests/free_ixsp.tex b/Build/source/texk/web2c/euptexdir/tests/free_ixsp.tex
index 9a3b2766415..4fb69d84406 100644
--- a/Build/source/texk/web2c/ptexdir/tests/free_ixsp.tex
+++ b/Build/source/texk/web2c/euptexdir/tests/free_ixsp.tex
@@ -1,7 +1,8 @@
%#!eptex -ini -etex
-\let\dump\relax
-\batchmode
-\input plain
+%\let\dump\relax
+%\batchmode
+%\input plain
+\font\cmr=cmr10\cmr
\errorstopmode
\catcode`@=11
diff --git a/Build/source/texk/web2c/euptexdir/tests/free_pena.log b/Build/source/texk/web2c/euptexdir/tests/free_pena.log
new file mode 100644
index 00000000000..73d7df615a4
--- /dev/null
+++ b/Build/source/texk/web2c/euptexdir/tests/free_pena.log
@@ -0,0 +1,29 @@
+entering extended mode
+ restricted \write18 enabled.
+ %&-line parsing enabled.
+**eup-free_pena.tex
+(./eup-free_pena.tex
+\@tempcnta=\count26
+\@tempcntb=\count27
+\@tempcntc=\count28
+\CNT=\count29
+\CNTA=\count30
+0. 1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. 17. 18. 19. 20. 21.
+22. 23. 24. 25. 26. 27. 28. 29. 30. 31. 32. 33. 34. 35. 36. 37. 38. 39. 40.
+41. 42. 43. 44. 45. 46. 47. 48. 49. 50. 51. 52. 53. 54. 55. 56. 57. 58. 59.
+60. 61. 62. 63. 64. 65. 66. 67. 68. 69. 70. 71. 72. 73. 74. 75. 76. 77. 78.
+79. 80. 81. 82. 83. 84. 85. 86. 87. 88. 89. 90. 91. 92. 93. 94. 95. 96. 97.
+98. 99. 100. 101. 102. 103. 104. 105. 106. 107. 108. 109. 110. 111. 112. 113.
+114. 115. 116. 117. 118. 119. 120. 121. 122. 123. 124. 125. 126. 127. 128.
+129. 130. 131. 132. 133. 134. 135. 136. 137. 138. 139. 140. 141. 142. 143.
+144. 145. 146. 147. 148. 149. 150. 151. 152. 153. 154. 155. 156. 157. 158.
+159. 160. 161. 162. 163. 164. 165. 166. 167. 168. 169. 170. 171. 172. 173.
+174. 175. 176. 177. 178. 179. 180. 181. 182. 183. 184. 185. 186. 187. 188.
+189. 190. 191. 192. 193. 194. 195. 196. 197. 198. 199. 200. 201. 202. 203.
+204. 205. 206. 207. 208. 209. 210. 211. 212. 213. 214. 215. 216. 217. 218.
+219. 220. 221. 222. 223. 224. 225. 226. 227. 228. 229. 230. 231. 232. 233.
+234. 235. 236. 237. 238. 239. 240. 241. 242. 243. 244. 245. 246. 247. 248.
+249. 250. 251. 252. 253. 254. 255.
+\CNTB=\count31
+ )
+No pages of output.
diff --git a/Build/source/texk/web2c/ptexdir/tests/free_pena.tex b/Build/source/texk/web2c/euptexdir/tests/free_pena.tex
index 184720a80a2..f7d2b51bcad 100644
--- a/Build/source/texk/web2c/ptexdir/tests/free_pena.tex
+++ b/Build/source/texk/web2c/euptexdir/tests/free_pena.tex
@@ -1,7 +1,15 @@
%#!eptex -ini -etex
-\let\dump\relax
-\batchmode
-\input plain
+%\let\dump\relax
+%\batchmode
+%\input plain
+\font\cmr=cmr10\cmr
+\ifx\kanjiskip\undefined\else
+ \ifnum\jis"2121="3000
+ \jfont\jpy=umin10 at 10pt\jpy
+ \else
+ \jfont\jpy=min10 at 10pt\jpy
+ \fi
+\fi
\errorstopmode
\catcode`@=11
diff --git a/Build/source/texk/web2c/ptexdir/tests/inhibitglue.tex b/Build/source/texk/web2c/euptexdir/tests/inhibitglue.tex
index 2c76a9782a8..6dd55dbdadf 100644
--- a/Build/source/texk/web2c/ptexdir/tests/inhibitglue.tex
+++ b/Build/source/texk/web2c/euptexdir/tests/inhibitglue.tex
@@ -1,4 +1,14 @@
%#!ptex2pdf -e inhibitglue.tex
+\font\cmr=cmr10\cmr
+\font\tentt=cmtt10\tentt
+\ifx\kanjiskip\undefined\else
+ \ifnum\jis"2121="3000
+ \jfont\jpy=umin10 at 10pt\jpy
+ \else
+ \jfont\jpy=min10 at 10pt\jpy
+ \fi
+\fi
+
\parindent0pt
\def\head#1{\par\noindent{\tentt ■\string#1}\par}
\noautospacing \noautoxspacing
@@ -82,13 +92,13 @@
あ)\adj\inhibitglue\adj (あ\par % 全角【TL17二分】
あ)\adj (あ\par % 全角
-\head{\math}
-\def\math{$ $}
-あ)\inhibitglue (あ\par % ベタ
-あ)\inhibitglue\math (あ\par % 二分
-あ)\math\inhibitglue (あ\par % 二分
-あ)\math\inhibitglue\math (あ\par % 全角【TL17二分】
-あ)\math (あ\par % 全角
+%\head{\math}
+%\def\math{$ $}
+%あ)\inhibitglue (あ\par % ベタ
+%あ)\inhibitglue\math (あ\par % 二分
+%あ)\math\inhibitglue (あ\par % 二分
+%あ)\math\inhibitglue\math (あ\par % 全角【TL17二分】
+%あ)\math (あ\par % 全角
%%% unexpandable tokens with discardable nodes (glue, kern, penalty)
diff --git a/Build/source/texk/web2c/euptexdir/tests/jctrlsym.log b/Build/source/texk/web2c/euptexdir/tests/jctrlsym.log
new file mode 100644
index 00000000000..c919554f580
--- /dev/null
+++ b/Build/source/texk/web2c/euptexdir/tests/jctrlsym.log
@@ -0,0 +1,85 @@
+entering extended mode
+ restricted \write18 enabled.
+ %&-line parsing enabled.
+**eup-jctrlsym.tex
+(./eup-jctrlsym.tex
+■control symbol
+> \X=macro:
+->\+.
+l.27 \show\X
+ \relax\message{(\meaning\X)}
+
+(macro:->\+)
+> \Y=macro:
+->\+.
+l.28 \show\Y
+ \relax\message{(\meaning\Y)}
+
+(macro:->\+) ■control word
+> \X=macro:
+->\+ .
+l.39 \show\X
+ \relax\message{(\meaning\X)}
+
+(macro:->\+ )
+> \Y=macro:
+->\+ .
+l.40 \show\Y
+ \relax\message{(\meaning\Y)}
+
+(macro:->\+ ) ■control word
+> \X=macro:
+->\ABC .
+l.48 \show\X
+ \relax\message{(\meaning\X)}
+
+(macro:->\ABC )
+> \Y=macro:
+->\あいう .
+l.49 \show\Y
+ \relax\message{(\meaning\Y)}
+
+(macro:->\あいう ) ■csname
+> \X=macro:
+->\:AB .
+l.62 \show\X
+ \relax\message{(\meaning\X)}
+
+(macro:->\:AB )
+> \Y=macro:
+->\!あい .
+l.63 \show\Y
+ \relax\message{(\meaning\Y)}
+
+(macro:->\!あい )
+> \X=macro:
+->\AB: .
+l.69 \show\X
+ \relax\message{(\meaning\X)}
+
+(macro:->\AB: )
+> \Y=macro:
+->\あい! .
+l.70 \show\Y
+ \relax\message{(\meaning\Y)}
+
+(macro:->\あい! ) ■not_cjk
+> \X=macro:
+->\^^e3^^82^^a2 .
+l.81 \show\X
+ \relax\message{(\meaning\X)}
+
+(macro:->\^^e3^^82^^a2 )
+> \Y=macro:
+->\^^ef^^bc^^81 .
+l.82 \show\Y
+ \relax\message{(\meaning\Y)}
+
+(macro:->\^^ef^^bc^^81 ) ■行末の和文^^e3^^82^^b3^^e3^^83^^b3^^e3^^83^^88
+^^e3^^83^^ad^^e3^^83^^bc^^e3^^83^^ab^^e3^^82^^b7^^e3^^83^^b3^^e3^^83^^9c^^e3^^8
+3^^ab
+Missing character: There is no ^^ef in font cmr10!
+Missing character: There is no ^^bc in font cmr10!
+Missing character: There is no ^^8c in font cmr10!
+ [1] )
+Output written on eup-jctrlsym.dvi (1 page, 372 bytes).
diff --git a/Build/source/texk/web2c/euptexdir/tests/kinsoku_search.log b/Build/source/texk/web2c/euptexdir/tests/kinsoku_search.log
new file mode 100644
index 00000000000..be8dbe27583
--- /dev/null
+++ b/Build/source/texk/web2c/euptexdir/tests/kinsoku_search.log
@@ -0,0 +1,84 @@
+entering extended mode
+ restricted \write18 enabled.
+ %&-line parsing enabled.
+**eup-kinsoku_search.tex
+(./eup-kinsoku_search.tex{into \tracingassigns=1}
+{changing kinsoku table 1, type=no, code=0}
+{into kinsoku table 1, type=pre, code=12289}
+{changing kinsoku table 1, penalty=0}
+{into kinsoku table 1, penalty=10000}
+{changing kinsoku table 2, type=no, code=0}
+{into kinsoku table 2, type=pre, code=12290}
+{changing kinsoku table 2, penalty=0}
+{into kinsoku table 2, penalty=10000}
+{changing kinsoku table 3, type=no, code=0}
+{into kinsoku table 3, type=pre, code=12353}
+{changing kinsoku table 3, penalty=0}
+{into kinsoku table 3, penalty=150}
+{changing kinsoku table 4, type=no, code=0}
+{into kinsoku table 4, type=pre, code=12355}
+{changing kinsoku table 4, penalty=0}
+{into kinsoku table 4, penalty=150}
+> 0.
+l.11 \showthe\currentgrouplevel
+ % > 0.
+
+> 150.
+l.12 \showthe\prebreakpenalty`ぃ
+ % > 150.
+
+{changing kinsoku table 4, type=pre, code=12355}
+{into kinsoku table 4, type=unused, code=0}
+> 0.
+l.15 \showthe\prebreakpenalty`ぃ
+ % > 0. ← OK(hash=4読出)
+
+{changing kinsoku table 4, type=unused, code=0}
+{into kinsoku table 4, type=pre, code=12355}
+{changing kinsoku table 4, penalty=150}
+{into kinsoku table 4, penalty=123}
+> 123.
+l.18 \showthe\prebreakpenalty`ぃ
+ % > 123. ← OK(hash=4読出)
+
+{changing kinsoku table 3, type=pre, code=12353}
+{into kinsoku table 3, type=unused, code=0}
+> 123.
+l.21 \showthe\prebreakpenalty`ぃ
+ % > 123. ← OK(hash=4読出)
+
+{changing kinsoku table 4, type=pre, code=12355}
+{into kinsoku table 4, type=unused, code=0}
+> 0.
+l.27 \showthe\prebreakpenalty`ぃ
+ % > 0. ← 修正(削除済み)
+
+{changing kinsoku table 3, type=unused, code=0}
+{into kinsoku table 3, type=pre, code=12355}
+{changing kinsoku table 3, penalty=150}
+{into kinsoku table 3, penalty=234}
+> 234.
+l.30 \showthe\prebreakpenalty`ぃ
+ % > 234. ← OK(hash=3読出)
+
+{changing kinsoku table 3, type=pre, code=12355}
+{into kinsoku table 3, type=unused, code=0}
+> 0.
+l.33 \showthe\prebreakpenalty`ぃ
+ % > 0. ← 修正(削除済み)
+
+{changing kinsoku table 3, type=unused, code=0}
+{into kinsoku table 3, type=pre, code=12353}
+{changing kinsoku table 3, penalty=234}
+{into kinsoku table 3, penalty=345}
+> 0.
+l.36 \showthe\prebreakpenalty`ぃ
+ % > 0. ← 修正(削除済み)
+
+{reassigning kinsoku table 4, type=unused, code=0}
+> 0.
+l.41 \showthe\prebreakpenalty`ぃ
+ % > 0. ← OK(削除済み)
+
+ )
+No pages of output.
diff --git a/Build/source/texk/web2c/ptexdir/tests/kinsoku_search.tex b/Build/source/texk/web2c/euptexdir/tests/kinsoku_search.tex
index e59445e7d9c..e59445e7d9c 100644
--- a/Build/source/texk/web2c/ptexdir/tests/kinsoku_search.tex
+++ b/Build/source/texk/web2c/euptexdir/tests/kinsoku_search.tex
diff --git a/Build/source/texk/web2c/euptexdir/tests/kinsoku_table.log b/Build/source/texk/web2c/euptexdir/tests/kinsoku_table.log
new file mode 100644
index 00000000000..46968151697
--- /dev/null
+++ b/Build/source/texk/web2c/euptexdir/tests/kinsoku_table.log
@@ -0,0 +1,38 @@
+entering extended mode
+ restricted \write18 enabled.
+ %&-line parsing enabled.
+**eup-kinsoku_table.tex
+(./eup-kinsoku_table.tex
+\fuga=\count26
+\dflt=\count27
+
+\hoge=\count28
+12288 12291 12294 12297 12300 12303 12306 12309 12312 12315 12318 12321 12324
+12327 12330 12333 12336 12339 12342 12345 12348 12351 12354 12357 12360 12363
+12366 12369 12372 12375 12378 12381 12384 12387 12390 12393 12396 12399 12402
+12405 12408 12411 12414 12417 12420 12423 12426 12429 12432 12435 12438 12441
+12444 12447 12450 12453 12456 12459 12462 12465 12468 12471 12474 12477 12480
+12483 12486 12489 12492 12495 12498 12501 12504 12507 12510 12513 12516 12519
+12522 12525 12528 12531 12534 12537 12540 12543 12546 12549 12552 12555 12558
+12561 12564 12567 12570 12573 12576 12579 12582 12585 12588 12591 12594 12597
+12600 12603 12606 12609 12612 12615 12618 12621 12624 12627 12630 12633 12636
+12639 12642 12645 12648 12651 12654 12657 12660 12663 12666 12669 12672 12675
+12678 12681 12684 12687 12690 12693 12696 12699 12702 12705 12708 12711 12714
+12717 12720 12723 12726 12729 12732 12735 12738 12741 12744 12747 12750 12753
+12756 12759 12762 12765 12768 12771 12774 12777 12780 12783 12786 12789 12792
+12795 12798 12801 12804 12807 12810 12813 12816 12819 12822 12825 12828 12831
+12834 12837 12840 12843 12846 12849 12852 12855 12858 12861 12864 12867 12870
+12873 12876 12879 12882 12885 12888 12891 12894 12897 12900 12903 12906 12909
+12912 12915 12918 12921 12924 12927 12930 12933 12936 12939 12942 12945 12948
+12951 12954 12957 12960 12963 12966 12969 12972 12975 12978 12981 12984 12987
+12990 12993 12996 12999 13002 13005 13008 13011 13014 13017 13020 13023 13026
+13029 13032 13035 13038 13041 13044 13047 13050 13053 <2, 0>
+! KINSOKU table is full!!.
+<to be read again>
+ \relax
+l.23 \global\prebreakpenalty"4000=1\relax
+ % error
+I'm skipping this control sequences.
+
+<0, 0> <2, 0> <0, 1> <0, 0> <0, 1, 0> <0, 0, 1> )
+No pages of output.
diff --git a/Build/source/texk/web2c/ptexdir/tests/kinsoku_table.tex b/Build/source/texk/web2c/euptexdir/tests/kinsoku_table.tex
index 52b21fdbf86..a064af9fc41 100644
--- a/Build/source/texk/web2c/ptexdir/tests/kinsoku_table.tex
+++ b/Build/source/texk/web2c/euptexdir/tests/kinsoku_table.tex
@@ -1,6 +1,7 @@
%#!uptex -ini
-\let\dump\relax
-\input plain
+%\let\dump\relax
+%\input plain
+
\newcount\fuga\newcount\dflt
\fuga="3300
\iffalse
diff --git a/Build/source/texk/web2c/euptexdir/tests/pknj-e.log b/Build/source/texk/web2c/euptexdir/tests/pknj-e.log
new file mode 100644
index 00000000000..a38e4fec2ff
--- /dev/null
+++ b/Build/source/texk/web2c/euptexdir/tests/pknj-e.log
@@ -0,0 +1,224 @@
+entering extended mode
+ restricted \write18 enabled.
+ %&-line parsing enabled.
+**printkanji
+(./printkanji.tex
+\openout1 = `eup-pknj-e.out'.
+
+
+{MSG ^^c5^^bf ^^c5^^bf 顛 }
+{TOT ^^c5^^bf ^^c5^^bf 顛 }
+> \A=macro:
+->^^c5^^bf ^^c5^^bf 顛 .
+l.29 \show\A
+
+
+■\meaning. {MSG macro:->macro:->^^c5^^bf ^^c5^^bf 顛 }
+{TOT macro:->macro:->^^c5^^bf ^^c5^^bf 顛 }
+> \B=macro:
+->macro:->^^c5^^bf ^^c5^^bf 顛 .
+l.40 \show\B
+
+
+■\jobname. {MSG *あ*eup-pknj-e*:eup-pknj-e:*macro:->:eup-pknj-e:*}
+{TOT *あ*eup-pknj-e*:eup-pknj-e:*macro:->:eup-pknj-e:*}
+
+■oneletter. {MSG \catcode`\^^c5= 197}
+{TOT \catcode`\^^c5= 197}
+{MSG \catcode`^^c5 = 197}
+{TOT \catcode`^^c5 = 197}
+{MSG \catcode`^^c5^^bf = 197}
+{TOT \catcode`^^c5^^bf = 197}
+{MSG \catcode`顛 = 50623}
+{TOT \catcode`顛 = 50623}
+■csname1.
+> \顛=macro:
+->hoge.
+l.72 \show\顛
+
+
+> \^^c5^^bf=macro:
+->piyo.
+l.73 \show\^^c5^^bf
+
+
+> \^^c5^^bf=macro:
+->piyo.
+<recently read> \^^c5^^bf
+
+l.74 \expandafter\show\csname ^^c5^^bf\endcsname
+
+
+{MSG \顛=>macro:->hoge}
+{TOT \顛=>macro:->hoge}
+{MSG \^^c5^^bf=>macro:->piyo}
+{TOT \^^c5^^bf=>macro:->piyo}
+{MSG \^^c5^^bf=>macro:->piyo}
+{TOT \^^c5^^bf=>macro:->piyo}
+{MSG \^^c5^^bf, \^^c5^^bf, \^^c5^^bf, \顛.}
+{TOT \^^c5^^bf, \^^c5^^bf, \^^c5^^bf, \顛.}
+
+{MSG \あ=> hoge, \^^a4^^a2=> piyo. \^^a4^^a2=> piyo. }
+{TOT \あ=> hoge, \^^a4^^a2=> piyo. \^^a4^^a2=> piyo. }
+■0xFF.
+{MSG あ, ^^c5^^bf ^^c5^^bf 顛 , \^^ff^^c5^^ff^^bf, macro:->(\^^ff^^c5^^ff^^bf:m
+acro:->あ).}
+{TOT あ, ^^c5^^bf ^^c5^^bf 顛 , \^^ff^^c5^^ff^^bf, macro:->(\^^ff^^c5^^ff^^bf:m
+acro:->あ).}
+{MSG い, (\^^ff:macro:->い), \^^ff, macro:->(\^^ff:macro:->い).}
+{TOT い, (\^^ff:macro:->い), \^^ff, macro:->(\^^ff:macro:->い).}
+■contexts.
+! Undefined control sequence.
+\@->\A \undefined
+
+\a ->^^c5^^bf a^^c5^^bf ^^c5^^bf 顛 \@
+ ^^c5^^bf 顛
+\+#1->\^^c5^^bf \顛 \^^ff^^c5^^ff^^bf #1
+ \^^ff macro:->^^c5^^bf ^^c5^^bf 顛 ...
+l.136 \+\a
+
+The control sequence at the end of the top line
+of your error message was never \def'ed. If you have
+misspelled it (e.g., `\hobx'), type `I' and the correct
+spelling (e.g., `I\hbox'). Otherwise just continue,
+and I'll forget about whatever was undefined.
+
+! Undefined control sequence.
+\@->\A \undefined
+
+\a ->^^c5^^bf a^^c5^^bf ^^c5^^bf 顛 \@
+ ^^c5^^bf 顛
+l.138 ...5^^ff^^bf ^^c5^^bf \^^c5^^bf ^^c5^^bf顛\a
+ \^^ff ^^ff\^^c5^^bf
+The control sequence at the end of the top line
+of your error message was never \def'ed. If you have
+misspelled it (e.g., `\hobx'), type `I' and the correct
+spelling (e.g., `I\hbox'). Otherwise just continue,
+and I'll forget about whatever was undefined.
+
+! Undefined control sequence.
+l.140 \^^c5
+ \^^ff\^^c4\^^fe
+The control sequence at the end of the top line
+of your error message was never \def'ed. If you have
+misspelled it (e.g., `\hobx'), type `I' and the correct
+spelling (e.g., `I\hbox'). Otherwise just continue,
+and I'll forget about whatever was undefined.
+
+! Undefined control sequence.
+l.140 \^^c5\^^ff\^^c4
+ \^^fe
+The control sequence at the end of the top line
+of your error message was never \def'ed. If you have
+misspelled it (e.g., `\hobx'), type `I' and the correct
+spelling (e.g., `I\hbox'). Otherwise just continue,
+and I'll forget about whatever was undefined.
+
+! Undefined control sequence.
+l.140 \^^c5\^^ff\^^c4\^^fe
+
+The control sequence at the end of the top line
+of your error message was never \def'ed. If you have
+misspelled it (e.g., `\hobx'), type `I' and the correct
+spelling (e.g., `I\hbox'). Otherwise just continue,
+and I'll forget about whatever was undefined.
+
+\^^c5^^bf\^^ff^^c5
+! Undefined control sequence.
+\b ->\^^c5^^be
+ .\^^c5^^bf .\^^c5^^bd ..\転 .\顛 .\貼 .
+l.154 \b
+
+The control sequence at the end of the top line
+of your error message was never \def'ed. If you have
+misspelled it (e.g., `\hobx'), type `I' and the correct
+spelling (e.g., `I\hbox'). Otherwise just continue,
+and I'll forget about whatever was undefined.
+
+! Undefined control sequence.
+\b ->\^^c5^^be .\^^c5^^bf .\^^c5^^bd
+ ..\転 .\顛 .\貼 .
+l.154 \b
+
+The control sequence at the end of the top line
+of your error message was never \def'ed. If you have
+misspelled it (e.g., `\hobx'), type `I' and the correct
+spelling (e.g., `I\hbox'). Otherwise just continue,
+and I'll forget about whatever was undefined.
+
+! Undefined control sequence.
+\b ...5^^be .\^^c5^^bf .\^^c5^^bd ..\転 .\顛 .\貼
+ .
+l.154 \b
+
+The control sequence at the end of the top line
+of your error message was never \def'ed. If you have
+misspelled it (e.g., `\hobx'), type `I' and the correct
+spelling (e.g., `I\hbox'). Otherwise just continue,
+and I'll forget about whatever was undefined.
+
+! Undefined control sequence.
+l.156 \^^c5^^be
+ .\^^c5^^bf.\^^c5^^bd..\転.\顛.\貼.
+The control sequence at the end of the top line
+of your error message was never \def'ed. If you have
+misspelled it (e.g., `\hobx'), type `I' and the correct
+spelling (e.g., `I\hbox'). Otherwise just continue,
+and I'll forget about whatever was undefined.
+
+! Undefined control sequence.
+l.156 \^^c5^^be.\^^c5^^bf.\^^c5^^bd
+ ..\転.\顛.\貼.
+The control sequence at the end of the top line
+of your error message was never \def'ed. If you have
+misspelled it (e.g., `\hobx'), type `I' and the correct
+spelling (e.g., `I\hbox'). Otherwise just continue,
+and I'll forget about whatever was undefined.
+
+! Undefined control sequence.
+l.156 \^^c5^^be.\^^c5^^bf.\^^c5^^bd..\転.\顛.\貼
+ .
+The control sequence at the end of the top line
+of your error message was never \def'ed. If you have
+misspelled it (e.g., `\hobx'), type `I' and the correct
+spelling (e.g., `I\hbox'). Otherwise just continue,
+and I'll forget about whatever was undefined.
+
+■csname2.
+> \^^c5^^bf=macro:
+->a.
+l.162 SHOW \show\^^c5^^bf
+ .\show\顛.%
+
+> \顛=macro:
+->b.
+l.162 SHOW \show\^^c5^^bf.\show\顛
+ .%
+
+> \^^c5^^bf=macro:
+->a.
+<recently read> \^^c5^^bf
+
+l.163 \expandafter\show\csname ^^c5^^bf\endcsname
+ .%
+
+> \顛=macro:
+->b.
+<recently read> \顛
+
+l.164 \expandafter\show\csname 顛\endcsname
+
+
+\^^e3^^81^^82\あ [50623]
+Memory usage before: 1790&5515; after: 130&5080; still untouched: 4992452
+ )
+Here is how much of TeX's memory you used:
+ 25 strings out of 497XXX
+ 143 string characters out of 6215XXX
+ 7548 words of memory out of 5000000
+ 1090 multiletter control sequences out of 15000+600000
+ 3271 words of font info for 2 fonts, out of 8000000 for 9000
+ 0 hyphenation exceptions out of 8191
+ 6i,4n,2p,97b,26s stack positions out of 10000i,1000n,20000p,200000b,200000s
+
+Output written on eup-pknj-e.dvi (1 page, 1140 bytes).
diff --git a/Build/source/texk/web2c/euptexdir/tests/pknj-s.log b/Build/source/texk/web2c/euptexdir/tests/pknj-s.log
new file mode 100644
index 00000000000..4fea4e53a4a
--- /dev/null
+++ b/Build/source/texk/web2c/euptexdir/tests/pknj-s.log
@@ -0,0 +1,224 @@
+entering extended mode
+ restricted \write18 enabled.
+ %&-line parsing enabled.
+**printkanji
+(./printkanji.tex
+\openout1 = `eup-pknj-s.out'.
+
+
+{MSG ^^c5^^bf ^^c5^^bf 顛 }
+{TOT ^^c5^^bf ^^c5^^bf 顛 }
+> \A=macro:
+->^^c5^^bf ^^c5^^bf 顛 .
+l.29 \show\A
+
+
+■\meaning. {MSG macro:->macro:->^^c5^^bf ^^c5^^bf 顛 }
+{TOT macro:->macro:->^^c5^^bf ^^c5^^bf 顛 }
+> \B=macro:
+->macro:->^^c5^^bf ^^c5^^bf 顛 .
+l.40 \show\B
+
+
+■\jobname. {MSG *あ*eup-pknj-s*:eup-pknj-s:*macro:->:eup-pknj-s:*}
+{TOT *あ*eup-pknj-s*:eup-pknj-s:*macro:->:eup-pknj-s:*}
+
+■oneletter. {MSG \catcode`\^^c5= 197}
+{TOT \catcode`\^^c5= 197}
+{MSG \catcode`^^c5 = 197}
+{TOT \catcode`^^c5 = 197}
+{MSG \catcode`^^c5^^bf = 197}
+{TOT \catcode`^^c5^^bf = 197}
+{MSG \catcode`顛 = 37726}
+{TOT \catcode`顛 = 37726}
+■csname1.
+> \顛=macro:
+->hoge.
+l.72 \show\顛
+
+
+> \^^c5^^bf=macro:
+->piyo.
+l.73 \show\^^c5^^bf
+
+
+> \^^c5^^bf=macro:
+->piyo.
+<recently read> \^^c5^^bf
+
+l.74 \expandafter\show\csname ^^c5^^bf\endcsname
+
+
+{MSG \顛=>macro:->hoge}
+{TOT \顛=>macro:->hoge}
+{MSG \^^c5^^bf=>macro:->piyo}
+{TOT \^^c5^^bf=>macro:->piyo}
+{MSG \^^c5^^bf=>macro:->piyo}
+{TOT \^^c5^^bf=>macro:->piyo}
+{MSG \^^c5^^bf, \^^c5^^bf, \^^c5^^bf, \顛.}
+{TOT \^^c5^^bf, \^^c5^^bf, \^^c5^^bf, \顛.}
+
+{MSG \あ=> hoge, \^^e3^^81^^82=> piyo. \^^e3^^81^^82=> piyo. }
+{TOT \あ=> hoge, \^^e3^^81^^82=> piyo. \^^e3^^81^^82=> piyo. }
+■0xFF.
+{MSG あ, ^^c5^^bf ^^c5^^bf 顛 , \^^ff^^c5^^ff^^bf, macro:->(\^^ff^^c5^^ff^^bf:m
+acro:->あ).}
+{TOT あ, ^^c5^^bf ^^c5^^bf 顛 , \^^ff^^c5^^ff^^bf, macro:->(\^^ff^^c5^^ff^^bf:m
+acro:->あ).}
+{MSG い, (\^^ff:macro:->い), \^^ff, macro:->(\^^ff:macro:->い).}
+{TOT い, (\^^ff:macro:->い), \^^ff, macro:->(\^^ff:macro:->い).}
+■contexts.
+! Undefined control sequence.
+\@->\A \undefined
+
+\a ->^^c5^^bf a^^c5^^bf ^^c5^^bf 顛 \@
+ ^^c5^^bf 顛
+\+#1->\^^c5^^bf \顛 \^^ff^^c5^^ff^^bf #1
+ \^^ff macro:->^^c5^^bf ^^c5^^bf 顛 ...
+l.136 \+\a
+
+The control sequence at the end of the top line
+of your error message was never \def'ed. If you have
+misspelled it (e.g., `\hobx'), type `I' and the correct
+spelling (e.g., `I\hbox'). Otherwise just continue,
+and I'll forget about whatever was undefined.
+
+! Undefined control sequence.
+\@->\A \undefined
+
+\a ->^^c5^^bf a^^c5^^bf ^^c5^^bf 顛 \@
+ ^^c5^^bf 顛
+l.138 ...5^^ff^^bf ^^c5^^bf \^^c5^^bf ^^c5^^bf顛\a
+ \^^ff ^^ff\^^c5^^bf
+The control sequence at the end of the top line
+of your error message was never \def'ed. If you have
+misspelled it (e.g., `\hobx'), type `I' and the correct
+spelling (e.g., `I\hbox'). Otherwise just continue,
+and I'll forget about whatever was undefined.
+
+! Undefined control sequence.
+l.140 \^^c5
+ \^^ff\^^c4\^^fe
+The control sequence at the end of the top line
+of your error message was never \def'ed. If you have
+misspelled it (e.g., `\hobx'), type `I' and the correct
+spelling (e.g., `I\hbox'). Otherwise just continue,
+and I'll forget about whatever was undefined.
+
+! Undefined control sequence.
+l.140 \^^c5\^^ff\^^c4
+ \^^fe
+The control sequence at the end of the top line
+of your error message was never \def'ed. If you have
+misspelled it (e.g., `\hobx'), type `I' and the correct
+spelling (e.g., `I\hbox'). Otherwise just continue,
+and I'll forget about whatever was undefined.
+
+! Undefined control sequence.
+l.140 \^^c5\^^ff\^^c4\^^fe
+
+The control sequence at the end of the top line
+of your error message was never \def'ed. If you have
+misspelled it (e.g., `\hobx'), type `I' and the correct
+spelling (e.g., `I\hbox'). Otherwise just continue,
+and I'll forget about whatever was undefined.
+
+\^^c5^^bf\^^ff^^c5
+! Undefined control sequence.
+\b ->\^^c5^^be
+ .\^^c5^^bf .\^^c5^^bd ..\転 .\顛 .\貼 .
+l.154 \b
+
+The control sequence at the end of the top line
+of your error message was never \def'ed. If you have
+misspelled it (e.g., `\hobx'), type `I' and the correct
+spelling (e.g., `I\hbox'). Otherwise just continue,
+and I'll forget about whatever was undefined.
+
+! Undefined control sequence.
+\b ->\^^c5^^be .\^^c5^^bf .\^^c5^^bd
+ ..\転 .\顛 .\貼 .
+l.154 \b
+
+The control sequence at the end of the top line
+of your error message was never \def'ed. If you have
+misspelled it (e.g., `\hobx'), type `I' and the correct
+spelling (e.g., `I\hbox'). Otherwise just continue,
+and I'll forget about whatever was undefined.
+
+! Undefined control sequence.
+\b ...5^^be .\^^c5^^bf .\^^c5^^bd ..\転 .\顛 .\貼
+ .
+l.154 \b
+
+The control sequence at the end of the top line
+of your error message was never \def'ed. If you have
+misspelled it (e.g., `\hobx'), type `I' and the correct
+spelling (e.g., `I\hbox'). Otherwise just continue,
+and I'll forget about whatever was undefined.
+
+! Undefined control sequence.
+l.156 \^^c5^^be
+ .\^^c5^^bf.\^^c5^^bd..\転.\顛.\貼.
+The control sequence at the end of the top line
+of your error message was never \def'ed. If you have
+misspelled it (e.g., `\hobx'), type `I' and the correct
+spelling (e.g., `I\hbox'). Otherwise just continue,
+and I'll forget about whatever was undefined.
+
+! Undefined control sequence.
+l.156 \^^c5^^be.\^^c5^^bf.\^^c5^^bd
+ ..\転.\顛.\貼.
+The control sequence at the end of the top line
+of your error message was never \def'ed. If you have
+misspelled it (e.g., `\hobx'), type `I' and the correct
+spelling (e.g., `I\hbox'). Otherwise just continue,
+and I'll forget about whatever was undefined.
+
+! Undefined control sequence.
+l.156 \^^c5^^be.\^^c5^^bf.\^^c5^^bd..\転.\顛.\貼
+ .
+The control sequence at the end of the top line
+of your error message was never \def'ed. If you have
+misspelled it (e.g., `\hobx'), type `I' and the correct
+spelling (e.g., `I\hbox'). Otherwise just continue,
+and I'll forget about whatever was undefined.
+
+■csname2.
+> \^^c5^^bf=macro:
+->a.
+l.162 SHOW \show\^^c5^^bf
+ .\show\顛.%
+
+> \顛=macro:
+->b.
+l.162 SHOW \show\^^c5^^bf.\show\顛
+ .%
+
+> \^^c5^^bf=macro:
+->a.
+<recently read> \^^c5^^bf
+
+l.163 \expandafter\show\csname ^^c5^^bf\endcsname
+ .%
+
+> \顛=macro:
+->b.
+<recently read> \顛
+
+l.164 \expandafter\show\csname 顛\endcsname
+
+
+\^^e3^^81^^82\あ [37726]
+Memory usage before: 1798&5518; after: 130&5081; still untouched: 4992449
+ )
+Here is how much of TeX's memory you used:
+ 24 strings out of 497XXX
+ 141 string characters out of 6215XXX
+ 7551 words of memory out of 5000000
+ 1089 multiletter control sequences out of 15000+600000
+ 3271 words of font info for 2 fonts, out of 8000000 for 9000
+ 0 hyphenation exceptions out of 8191
+ 6i,4n,2p,97b,26s stack positions out of 10000i,1000n,20000p,200000b,200000s
+
+Output written on eup-pknj-s.dvi (1 page, 1148 bytes).
diff --git a/Build/source/texk/web2c/euptexdir/tests/pknj-u.log b/Build/source/texk/web2c/euptexdir/tests/pknj-u.log
new file mode 100644
index 00000000000..f456fc4c18e
--- /dev/null
+++ b/Build/source/texk/web2c/euptexdir/tests/pknj-u.log
@@ -0,0 +1,224 @@
+entering extended mode
+ restricted \write18 enabled.
+ %&-line parsing enabled.
+**printkanji
+(./printkanji.tex
+\openout1 = `eup-pknj-u.out'.
+
+
+{MSG ^^c5^^bf ^^c5^^bf 顛 }
+{TOT ^^c5^^bf ^^c5^^bf 顛 }
+> \A=macro:
+->^^c5^^bf ^^c5^^bf 顛 .
+l.29 \show\A
+
+
+■\meaning. {MSG macro:->macro:->^^c5^^bf ^^c5^^bf 顛 }
+{TOT macro:->macro:->^^c5^^bf ^^c5^^bf 顛 }
+> \B=macro:
+->macro:->^^c5^^bf ^^c5^^bf 顛 .
+l.40 \show\B
+
+
+■\jobname. {MSG *あ*eup-pknj-u*:eup-pknj-u:*macro:->:eup-pknj-u:*}
+{TOT *あ*eup-pknj-u*:eup-pknj-u:*macro:->:eup-pknj-u:*}
+
+■oneletter. {MSG \catcode`\^^c5= 197}
+{TOT \catcode`\^^c5= 197}
+{MSG \catcode`^^c5 = 197}
+{TOT \catcode`^^c5 = 197}
+{MSG \catcode`^^c5^^bf = 197}
+{TOT \catcode`^^c5^^bf = 197}
+{MSG \catcode`顛 = 39003}
+{TOT \catcode`顛 = 39003}
+■csname1.
+> \顛=macro:
+->hoge.
+l.72 \show\顛
+
+
+> \^^c5^^bf=macro:
+->piyo.
+l.73 \show\^^c5^^bf
+
+
+> \^^c5^^bf=macro:
+->piyo.
+<recently read> \^^c5^^bf
+
+l.74 \expandafter\show\csname ^^c5^^bf\endcsname
+
+
+{MSG \顛=>macro:->hoge}
+{TOT \顛=>macro:->hoge}
+{MSG \^^c5^^bf=>macro:->piyo}
+{TOT \^^c5^^bf=>macro:->piyo}
+{MSG \^^c5^^bf=>macro:->piyo}
+{TOT \^^c5^^bf=>macro:->piyo}
+{MSG \^^c5^^bf, \^^c5^^bf, \^^c5^^bf, \顛.}
+{TOT \^^c5^^bf, \^^c5^^bf, \^^c5^^bf, \顛.}
+
+{MSG \あ=> hoge, \^^e3^^81^^82=> piyo. \^^e3^^81^^82=> piyo. }
+{TOT \あ=> hoge, \^^e3^^81^^82=> piyo. \^^e3^^81^^82=> piyo. }
+■0xFF.
+{MSG あ, ^^c5^^bf ^^c5^^bf 顛 , \^^ff^^c5^^ff^^bf, macro:->(\^^ff^^c5^^ff^^bf
+:macro:->あ).}
+{TOT あ, ^^c5^^bf ^^c5^^bf 顛 , \^^ff^^c5^^ff^^bf, macro:->(\^^ff^^c5^^ff^^bf
+:macro:->あ).}
+{MSG い, (\^^ff:macro:->い), \^^ff, macro:->(\^^ff:macro:->い).}
+{TOT い, (\^^ff:macro:->い), \^^ff, macro:->(\^^ff:macro:->い).}
+■contexts.
+! Undefined control sequence.
+\@->\A \undefined
+
+\a ->^^c5^^bf a^^c5^^bf ^^c5^^bf 顛 \@
+ ^^c5^^bf 顛
+\+#1->\^^c5^^bf \顛 \^^ff^^c5^^ff^^bf #1
+ \^^ff macro:->^^c5^^bf ^^c5^^bf ...
+l.136 \+\a
+
+The control sequence at the end of the top line
+of your error message was never \def'ed. If you have
+misspelled it (e.g., `\hobx'), type `I' and the correct
+spelling (e.g., `I\hbox'). Otherwise just continue,
+and I'll forget about whatever was undefined.
+
+! Undefined control sequence.
+\@->\A \undefined
+
+\a ->^^c5^^bf a^^c5^^bf ^^c5^^bf 顛 \@
+ ^^c5^^bf 顛
+l.138 ...^^ff^^bf ^^c5^^bf \^^c5^^bf ^^c5^^bf顛\a
+ \^^ff ^^ff\^^c5^^bf
+The control sequence at the end of the top line
+of your error message was never \def'ed. If you have
+misspelled it (e.g., `\hobx'), type `I' and the correct
+spelling (e.g., `I\hbox'). Otherwise just continue,
+and I'll forget about whatever was undefined.
+
+! Undefined control sequence.
+l.140 \^^c5
+ \^^ff\^^c4\^^fe
+The control sequence at the end of the top line
+of your error message was never \def'ed. If you have
+misspelled it (e.g., `\hobx'), type `I' and the correct
+spelling (e.g., `I\hbox'). Otherwise just continue,
+and I'll forget about whatever was undefined.
+
+! Undefined control sequence.
+l.140 \^^c5\^^ff\^^c4
+ \^^fe
+The control sequence at the end of the top line
+of your error message was never \def'ed. If you have
+misspelled it (e.g., `\hobx'), type `I' and the correct
+spelling (e.g., `I\hbox'). Otherwise just continue,
+and I'll forget about whatever was undefined.
+
+! Undefined control sequence.
+l.140 \^^c5\^^ff\^^c4\^^fe
+
+The control sequence at the end of the top line
+of your error message was never \def'ed. If you have
+misspelled it (e.g., `\hobx'), type `I' and the correct
+spelling (e.g., `I\hbox'). Otherwise just continue,
+and I'll forget about whatever was undefined.
+
+\^^c5^^bf\^^ff^^c5
+! Undefined control sequence.
+\b ->\^^c5^^be
+ .\^^c5^^bf .\^^c5^^bd ..\転 .\顛 .\貼 .
+l.154 \b
+
+The control sequence at the end of the top line
+of your error message was never \def'ed. If you have
+misspelled it (e.g., `\hobx'), type `I' and the correct
+spelling (e.g., `I\hbox'). Otherwise just continue,
+and I'll forget about whatever was undefined.
+
+! Undefined control sequence.
+\b ->\^^c5^^be .\^^c5^^bf .\^^c5^^bd
+ ..\転 .\顛 .\貼 .
+l.154 \b
+
+The control sequence at the end of the top line
+of your error message was never \def'ed. If you have
+misspelled it (e.g., `\hobx'), type `I' and the correct
+spelling (e.g., `I\hbox'). Otherwise just continue,
+and I'll forget about whatever was undefined.
+
+! Undefined control sequence.
+\b ...be .\^^c5^^bf .\^^c5^^bd ..\転 .\顛 .\貼
+ .
+l.154 \b
+
+The control sequence at the end of the top line
+of your error message was never \def'ed. If you have
+misspelled it (e.g., `\hobx'), type `I' and the correct
+spelling (e.g., `I\hbox'). Otherwise just continue,
+and I'll forget about whatever was undefined.
+
+! Undefined control sequence.
+l.156 \^^c5^^be
+ .\ſ.\Ž..\転.\顛.\貼.
+The control sequence at the end of the top line
+of your error message was never \def'ed. If you have
+misspelled it (e.g., `\hobx'), type `I' and the correct
+spelling (e.g., `I\hbox'). Otherwise just continue,
+and I'll forget about whatever was undefined.
+
+! Undefined control sequence.
+l.156 \^^c5^^be.\^^c5^^bf.\^^c5^^bd
+ ..\転.\顛.\貼.
+The control sequence at the end of the top line
+of your error message was never \def'ed. If you have
+misspelled it (e.g., `\hobx'), type `I' and the correct
+spelling (e.g., `I\hbox'). Otherwise just continue,
+and I'll forget about whatever was undefined.
+
+! Undefined control sequence.
+l.156 ...5^^be.\^^c5^^bf.\^^c5^^bd..\転.\顛.\貼
+ .
+The control sequence at the end of the top line
+of your error message was never \def'ed. If you have
+misspelled it (e.g., `\hobx'), type `I' and the correct
+spelling (e.g., `I\hbox'). Otherwise just continue,
+and I'll forget about whatever was undefined.
+
+■csname2.
+> \^^c5^^bf=macro:
+->a.
+l.162 SHOW \show\^^c5^^bf
+ .\show\顛.%
+
+> \顛=macro:
+->b.
+l.162 SHOW \show\^^c5^^bf.\show\顛
+ .%
+
+> \^^c5^^bf=macro:
+->a.
+<recently read> \^^c5^^bf
+
+l.163 \expandafter\show\csname ^^c5^^bf\endcsname
+ .%
+
+> \顛=macro:
+->b.
+<recently read> \顛
+
+l.164 \expandafter\show\csname 顛\endcsname
+
+
+\^^e3^^81^^82\あ [39003]
+Memory usage before: 1798&5518; after: 130&5081; still untouched: 4992449
+ )
+Here is how much of TeX's memory you used:
+ 24 strings out of 497XXX
+ 146 string characters out of 6215XXX
+ 7551 words of memory out of 5000000
+ 1089 multiletter control sequences out of 15000+600000
+ 3297 words of font info for 2 fonts, out of 8000000 for 9000
+ 0 hyphenation exceptions out of 8191
+ 6i,4n,2p,97b,26s stack positions out of 10000i,1000n,20000p,200000b,200000s
+
+Output written on eup-pknj-u.dvi (1 page, 1148 bytes).
diff --git a/Build/source/texk/web2c/euptexdir/tests/postpena.log b/Build/source/texk/web2c/euptexdir/tests/postpena.log
new file mode 100644
index 00000000000..6924c21fe99
--- /dev/null
+++ b/Build/source/texk/web2c/euptexdir/tests/postpena.log
@@ -0,0 +1,43 @@
+entering extended mode
+ restricted \write18 enabled.
+ %&-line parsing enabled.
+**eup-postpena.tex
+(./eup-postpena.tex
+
+### yoko direction, restricted horizontal mode entered at line 15
+\displace 0.0
+\jpy #
+\penalty 200(for kinsoku)
+\jpy 」
+\glue(refer from jfm) 4.58203 minus 2.291
+\jpy 「
+\penalty -1000
+spacefactor 1000
+### yoko direction, vertical mode entered at line 0
+prevdepth ignored
+
+! OK.
+<recently read> \showlists
+
+l.15 ...ox0=\hbox{#」「\penalty-1000\showlists
+ }
+
+
+### yoko direction, restricted horizontal mode entered at line 18
+\displace 0.0
+\jpy #
+\jpy 」
+\glue(refer from jfm) 4.58203 minus 2.291
+\cmr a
+\penalty 200(for kinsoku)
+\jpy あ
+spacefactor 1000
+### yoko direction, vertical mode entered at line 0
+prevdepth ignored
+
+! OK.
+l.18 ...box{#\unpenalty 」a\relax あ\showlists
+ }
+
+ )
+No pages of output.
diff --git a/Build/source/texk/web2c/ptexdir/tests/postpena.tex b/Build/source/texk/web2c/euptexdir/tests/postpena.tex
index 74f93a1f071..3605fe0c0b3 100644
--- a/Build/source/texk/web2c/ptexdir/tests/postpena.tex
+++ b/Build/source/texk/web2c/euptexdir/tests/postpena.tex
@@ -1,4 +1,12 @@
%% plain pTeX
+\font\cmr=cmr10\cmr
+\ifx\kanjiskip\undefined\else
+ \ifnum\jis"2121="3000
+ \jfont\jpy=umin10 at 10pt\jpy
+ \else
+ \jfont\jpy=min10 at 10pt\jpy
+ \fi
+\fi
\postbreakpenalty`#=200
\postbreakpenalty`a=200
\showboxdepth10000
diff --git a/Build/source/texk/web2c/euptexdir/tests/printkanji.tex b/Build/source/texk/web2c/euptexdir/tests/printkanji.tex
new file mode 100644
index 00000000000..d176d9a53bf
--- /dev/null
+++ b/Build/source/texk/web2c/euptexdir/tests/printkanji.tex
@@ -0,0 +1,176 @@
+%\scrollmode
+\tracingstats=1000
+\noautoxspacing
+\newlinechar=`\^^J
+\font\x=ec-lmr10 \x
+\ifx\kanjiskip\undefined\else
+ \ifnum\jis"2121="3000
+ \jfont\jpy=umin10 at 10pt\jpy
+ \else
+ \jfont\jpy=min10 at 10pt\jpy
+ \fi
+\fi
+
+
+\immediate\openout1=\jobname.out
+\def\MSG#1{%
+ \message{\string{MSG #1\string}}%
+ \immediate\write17{\string{TOT #1\string}}%
+ \immediate\write1{#1}%
+}
+\def\head#1{\message{■#1.}\par\noindent\hbox{■\null}#1.\par}
+\message{^^J}
+
+%================
+\def\A{^^c5^^bf ſ 顛 }
+A: \A
+
+\MSG{\A}
+\show\A
+
+%================
+\par
+\head{\string\meaning}
+
+\edef\B{\meaning\A}
+\meaning\B
+
+\B
+\MSG{\meaning\B}
+\show\B
+
+%================
+\head{\string\jobname}
+\edef\C{:\jobname:}
+\MSG{*あ*\jobname *\C*\meaning\C*}
+\message{^^J}
+
+%================
+\catcode`\^^c5=11
+\catcode`\^^bf=11
+\catcode`\^^e1=11
+\catcode`\^^e3=11
+\catcode`\^^81=11
+\catcode`\^^82=11
+
+\head{oneletter}
+
+\string\^^c4.\string\^^c5.\string\^^ff.
+\count0=`\^^c5%
+\MSG{\string\catcode`\string\^^c5 = \the\count0}%
+\count0=`^^c5%
+\MSG{\string\catcode`^^c5 = \the\count0}%
+\count0=`^^c5^^bf%
+\MSG{\string\catcode`^^c5^^bf = \the\count0}%
+\count0=`顛%
+\MSG{\string\catcode`顛 = \the\count0}%
+
+%================
+\head{csname1}
+
+{\def\顛{hoge}\def\^^c5^^bf{piyo}
+\show\顛
+\show\^^c5^^bf
+\expandafter\show\csname ^^c5^^bf\endcsname
+\MSG{\string\顛=>\meaning\顛}
+\MSG{\string\^^c5^^bf=>\meaning\^^c5^^bf}
+\MSG{\expandafter\string\csname ^^c5^^bf\endcsname
+ =>\expandafter\meaning\csname ^^c5^^bf\endcsname}}
+
+\string\^^c5^^bf
+\expandafter\string\csname ^^c5^^bf\endcsname,
+\expandafter\string\csname ſ\endcsname,
+\expandafter\string\csname 顛\endcsname
+\MSG{\string\^^c5^^bf,
+ \expandafter\string\csname ^^c5^^bf\endcsname,
+ \expandafter\string\csname ſ\endcsname,
+ \expandafter\string\csname 顛\endcsname.}
+
+%================
+
+\def\あ{hoge}
+\message{^^J}
+\def\TEST#1#2{%
+ \expandafter\def\csname#2\endcsname{piyo}
+ \par\toks0={#2}
+ \expandafter\string\csname #1\endcsname => \csname #1\endcsname,\par
+ \expandafter\string\csname #2\endcsname => \csname #2\endcsname,\par
+ \expandafter\string\csname \the\toks0\endcsname => \csname \the\toks0\endcsname.
+ \MSG{%
+ \expandafter\string\csname #1\endcsname => \csname #1\endcsname,
+ \expandafter\string\csname #2\endcsname => \csname #2\endcsname.
+ \expandafter\string\csname \the\toks0\endcsname => \csname \the\toks0\endcsname.
+ }%
+}
+\ifnum\euc"A4A2="A4A2\relax
+ \TEST{あ}{^^a4^^a2}
+\else
+ \TEST{あ}{^^e3^^81^^82}
+\fi
+
+%================
+\head{0xFF}
+\catcode"FF=11
+\message{^^J}
+
+\def\^^ff^^c5^^ff^^bf{あ}
+\edef\E{(\string\^^ff^^c5^^ff^^bf:\meaning\^^ff^^c5^^ff^^bf)}
+\^^ff^^c5^^ff^^bf, \E, \string\^^ff^^c5^^ff^^bf, \meaning\E.
+\MSG{\^^ff^^c5^^ff^^bf, \A, \string\^^ff^^c5^^ff^^bf, \meaning\E.}
+
+\def\^^ff{い}
+\edef\E{(\string\^^ff :\meaning\^^ff)}
+\^^ff, \E, \string\^^ff, \meaning\E.
+\MSG{\^^ff, \E, \string\^^ff, \meaning\E.}
+
+\immediate\closeout1
+
+%========
+\head{contexts}
+
+\errorcontextlines=10000
+\def\@{\A\undefined}
+\edef\a{^^c5^^bf a\A\noexpand\@ ſ 顛 }
+\edef\+#1{\noexpand\^^c5^^bf\noexpand\顛\noexpand\^^ff^^c5^^ff^^bf#1
+ \noexpand\^^ff\meaning\A\noexpand\^^c5^^bf\noexpand\顛}
+\+\a
+
+\^^ff^^c5^^ff^^bf ^^c5^^bf \^^c5^^bf ſ顛\a \^^ff ^^ff\^^c5^^bf
+
+\^^c5\^^ff\^^c4\^^fe
+
+\catcode`\^^c5=11
+\catcode`\^^be=11
+\catcode`\^^bf=11
+\catcode`\^^bd=11
+\catcode`\^^ff=11
+
+\def\^^c5^^bf{a}
+\def\顛{b}
+\def\転{c}
+\message{\string\^^c5^^bf \string\^^ff^^c5}
+
+\def\b{\ž.\ſ.\Ž..\転.\顛.\貼.}
+\b
+
+\ž.\ſ.\Ž..\転.\顛.\貼.
+
+
+%========
+\head{csname2}
+
+SHOW \show\ſ.\show\顛.%
+\expandafter\show\csname ſ\endcsname.%
+\expandafter\show\csname 顛\endcsname
+
+STRING \string\ſ.\string\顛.%
+\expandafter\string\csname ſ\endcsname.%
+\expandafter\string\csname 顛\endcsname
+
+MEANING \meaning\ſ.\meaning\顛.%
+\expandafter\meaning\csname ſ\endcsname.%
+\expandafter\meaning\csname 顛\endcsname.%
+
+\message{\string\^^e3^^81^^82 \string\あ}
+
+\end
diff --git a/Build/source/texk/web2c/euptexdir/tests/ucs-e.log b/Build/source/texk/web2c/euptexdir/tests/ucs-e.log
new file mode 100644
index 00000000000..684d2af0fa6
--- /dev/null
+++ b/Build/source/texk/web2c/euptexdir/tests/ucs-e.log
@@ -0,0 +1,29 @@
+entering extended mode
+ restricted \write18 enabled.
+ %&-line parsing enabled.
+**eup-ucs
+(./eup-ucs.tex
+<EUC>
+\ucs.
+\cntA=\count26
+162: 41457(¢)
+163: 41458(£)
+165: 41455(¥)
+167: 41464(§)
+168: 41391(¨)
+172: 41676(¬)
+176: 41451(°)
+177: 41438(±)
+180: 41389(´)
+182: 41721(¶)
+215: 41439(×)
+247: 41440(÷)
+\toucs.
+-1, -1, -1, -1, -1
+-1, 34821, 34722
+12354, 29081, -1
+41393, 41405, 41409, 41410, 41412, 41437, 41455, 41457, 41458, 41676
+41393, 41405, 41409, 41410, 41412, 41437, 41455, 41457, 41458, 41676
+41393, 41405, 41409, 41410, 41412, 41437, 41455, 41457, 41458, 41676
+ )
+No pages of output.
diff --git a/Build/source/texk/web2c/euptexdir/tests/ucs-s.log b/Build/source/texk/web2c/euptexdir/tests/ucs-s.log
new file mode 100644
index 00000000000..a8255446072
--- /dev/null
+++ b/Build/source/texk/web2c/euptexdir/tests/ucs-s.log
@@ -0,0 +1,29 @@
+entering extended mode
+ restricted \write18 enabled.
+ %&-line parsing enabled.
+**eup-ucs
+(./eup-ucs.tex
+<SJIS>
+\ucs.
+\cntA=\count26
+162: 33169(¢)
+163: 33170(£)
+165: 33167(¥)
+167: 33176(§)
+168: 33102(¨)
+172: 33226(¬)
+176: 33163(°)
+177: 33149(±)
+180: 33100(´)
+182: 33271(¶)
+215: 33150(×)
+247: 33152(÷)
+\toucs.
+-1, -1, -1, -1, -1
+12450, 29081, -1
+-1, -1, -1
+33104, 33116, 33120, 33121, 33123, 33148, 33167, 33169, 33170, 33226
+33104, 33116, 33120, 33121, 33123, 33148, 33167, 33169, 33170, 33226
+33104, 33116, 33120, 33121, 33123, 33148, 33167, 33169, 33170, 33226
+ )
+No pages of output.
diff --git a/Build/source/texk/web2c/euptexdir/tests/ucs-u.log b/Build/source/texk/web2c/euptexdir/tests/ucs-u.log
new file mode 100644
index 00000000000..c4dfe33b080
--- /dev/null
+++ b/Build/source/texk/web2c/euptexdir/tests/ucs-u.log
@@ -0,0 +1,529 @@
+entering extended mode
+ restricted \write18 enabled.
+ %&-line parsing enabled.
+**eup-ucs
+(./eup-ucs.tex
+<uptex>
+\ucs.
+\cntA=\count26
+0: 0(^^@)
+1: 1(^^A)
+2: 2(^^B)
+3: 3(^^C)
+4: 4(^^D)
+5: 5(^^E)
+6: 6(^^F)
+7: 7(^^G)
+8: 8(^^H)
+9: 9(^^I)
+10: 10(^^J)
+11: 11(^^K)
+12: 12(^^L)
+13: 13(^^M)
+14: 14(^^N)
+15: 15(^^O)
+16: 16(^^P)
+17: 17(^^Q)
+18: 18(^^R)
+19: 19(^^S)
+20: 20(^^T)
+21: 21(^^U)
+22: 22(^^V)
+23: 23(^^W)
+24: 24(^^X)
+25: 25(^^Y)
+26: 26(^^Z)
+27: 27(^^[)
+28: 28(^^\)
+29: 29(^^])
+30: 30(^^^)
+31: 31(^^_)
+32: 32( )
+33: 33(!)
+34: 34(")
+35: 35(#)
+36: 36($)
+37: 37(%)
+38: 38(&)
+39: 39(')
+40: 40(()
+41: 41())
+42: 42(*)
+43: 43(+)
+44: 44(,)
+45: 45(-)
+46: 46(.)
+47: 47(/)
+48: 48(0)
+49: 49(1)
+50: 50(2)
+51: 51(3)
+52: 52(4)
+53: 53(5)
+54: 54(6)
+55: 55(7)
+56: 56(8)
+57: 57(9)
+58: 58(:)
+59: 59(;)
+60: 60(<)
+61: 61(=)
+62: 62(>)
+63: 63(?)
+64: 64(@)
+65: 65(A)
+66: 66(B)
+67: 67(C)
+68: 68(D)
+69: 69(E)
+70: 70(F)
+71: 71(G)
+72: 72(H)
+73: 73(I)
+74: 74(J)
+75: 75(K)
+76: 76(L)
+77: 77(M)
+78: 78(N)
+79: 79(O)
+80: 80(P)
+81: 81(Q)
+82: 82(R)
+83: 83(S)
+84: 84(T)
+85: 85(U)
+86: 86(V)
+87: 87(W)
+88: 88(X)
+89: 89(Y)
+90: 90(Z)
+91: 91([)
+92: 92(\)
+93: 93(])
+94: 94(^)
+95: 95(_)
+96: 96(`)
+97: 97(a)
+98: 98(b)
+99: 99(c)
+100: 100(d)
+101: 101(e)
+102: 102(f)
+103: 103(g)
+104: 104(h)
+105: 105(i)
+106: 106(j)
+107: 107(k)
+108: 108(l)
+109: 109(m)
+110: 110(n)
+111: 111(o)
+112: 112(p)
+113: 113(q)
+114: 114(r)
+115: 115(s)
+116: 116(t)
+117: 117(u)
+118: 118(v)
+119: 119(w)
+120: 120(x)
+121: 121(y)
+122: 122(z)
+123: 123({)
+124: 124(|)
+125: 125(})
+126: 126(~)
+127: 127(^^?)
+128: 128(^^80)
+129: 129(^^81)
+130: 130(^^82)
+131: 131(^^83)
+132: 132(^^84)
+133: 133(^^85)
+134: 134(^^86)
+135: 135(^^87)
+136: 136(^^88)
+137: 137(^^89)
+138: 138(^^8a)
+139: 139(^^8b)
+140: 140(^^8c)
+141: 141(^^8d)
+142: 142(^^8e)
+143: 143(^^8f)
+144: 144(^^90)
+145: 145(^^91)
+146: 146(^^92)
+147: 147(^^93)
+148: 148(^^94)
+149: 149(^^95)
+150: 150(^^96)
+151: 151(^^97)
+152: 152(^^98)
+153: 153(^^99)
+154: 154(^^9a)
+155: 155(^^9b)
+156: 156(^^9c)
+157: 157(^^9d)
+158: 158(^^9e)
+159: 159(^^9f)
+160: 160(^^a0)
+161: 161(^^a1)
+162: 162(^^a2)
+163: 163(^^a3)
+164: 164(^^a4)
+165: 165(^^a5)
+166: 166(^^a6)
+167: 167(^^a7)
+168: 168(^^a8)
+169: 169(^^a9)
+170: 170(^^aa)
+171: 171(^^ab)
+172: 172(^^ac)
+173: 173(^^ad)
+174: 174(^^ae)
+175: 175(^^af)
+176: 176(^^b0)
+177: 177(^^b1)
+178: 178(^^b2)
+179: 179(^^b3)
+180: 180(^^b4)
+181: 181(^^b5)
+182: 182(^^b6)
+183: 183(^^b7)
+184: 184(^^b8)
+185: 185(^^b9)
+186: 186(^^ba)
+187: 187(^^bb)
+188: 188(^^bc)
+189: 189(^^bd)
+190: 190(^^be)
+191: 191(^^bf)
+192: 192(^^c0)
+193: 193(^^c1)
+194: 194(^^c2)
+195: 195(^^c3)
+196: 196(^^c4)
+197: 197(^^c5)
+198: 198(^^c6)
+199: 199(^^c7)
+200: 200(^^c8)
+201: 201(^^c9)
+202: 202(^^ca)
+203: 203(^^cb)
+204: 204(^^cc)
+205: 205(^^cd)
+206: 206(^^ce)
+207: 207(^^cf)
+208: 208(^^d0)
+209: 209(^^d1)
+210: 210(^^d2)
+211: 211(^^d3)
+212: 212(^^d4)
+213: 213(^^d5)
+214: 214(^^d6)
+215: 215(^^d7)
+216: 216(^^d8)
+217: 217(^^d9)
+218: 218(^^da)
+219: 219(^^db)
+220: 220(^^dc)
+221: 221(^^dd)
+222: 222(^^de)
+223: 223(^^df)
+224: 224(^^e0)
+225: 225(^^e1)
+226: 226(^^e2)
+227: 227(^^e3)
+228: 228(^^e4)
+229: 229(^^e5)
+230: 230(^^e6)
+231: 231(^^e7)
+232: 232(^^e8)
+233: 233(^^e9)
+234: 234(^^ea)
+235: 235(^^eb)
+236: 236(^^ec)
+237: 237(^^ed)
+238: 238(^^ee)
+239: 239(^^ef)
+240: 240(^^f0)
+241: 241(^^f1)
+242: 242(^^f2)
+243: 243(^^f3)
+244: 244(^^f4)
+245: 245(^^f5)
+246: 246(^^f6)
+247: 247(^^f7)
+248: 248(^^f8)
+249: 249(^^f9)
+250: 250(^^fa)
+251: 251(^^fb)
+252: 252(^^fc)
+253: 253(^^fd)
+254: 254(^^fe)
+255: 255(^^ff)
+\toucs.
+0: 0
+1: 1
+2: 2
+3: 3
+4: 4
+5: 5
+6: 6
+7: 7
+8: 8
+9: 9
+10: 10
+11: 11
+12: 12
+13: 13
+14: 14
+15: 15
+16: 16
+17: 17
+18: 18
+19: 19
+20: 20
+21: 21
+22: 22
+23: 23
+24: 24
+25: 25
+26: 26
+27: 27
+28: 28
+29: 29
+30: 30
+31: 31
+32: 32
+33: 33
+34: 34
+35: 35
+36: 36
+37: 37
+38: 38
+39: 39
+40: 40
+41: 41
+42: 42
+43: 43
+44: 44
+45: 45
+46: 46
+47: 47
+48: 48
+49: 49
+50: 50
+51: 51
+52: 52
+53: 53
+54: 54
+55: 55
+56: 56
+57: 57
+58: 58
+59: 59
+60: 60
+61: 61
+62: 62
+63: 63
+64: 64
+65: 65
+66: 66
+67: 67
+68: 68
+69: 69
+70: 70
+71: 71
+72: 72
+73: 73
+74: 74
+75: 75
+76: 76
+77: 77
+78: 78
+79: 79
+80: 80
+81: 81
+82: 82
+83: 83
+84: 84
+85: 85
+86: 86
+87: 87
+88: 88
+89: 89
+90: 90
+91: 91
+92: 92
+93: 93
+94: 94
+95: 95
+96: 96
+97: 97
+98: 98
+99: 99
+100: 100
+101: 101
+102: 102
+103: 103
+104: 104
+105: 105
+106: 106
+107: 107
+108: 108
+109: 109
+110: 110
+111: 111
+112: 112
+113: 113
+114: 114
+115: 115
+116: 116
+117: 117
+118: 118
+119: 119
+120: 120
+121: 121
+122: 122
+123: 123
+124: 124
+125: 125
+126: 126
+127: 127
+128: 128
+129: 129
+130: 130
+131: 131
+132: 132
+133: 133
+134: 134
+135: 135
+136: 136
+137: 137
+138: 138
+139: 139
+140: 140
+141: 141
+142: 142
+143: 143
+144: 144
+145: 145
+146: 146
+147: 147
+148: 148
+149: 149
+150: 150
+151: 151
+152: 152
+153: 153
+154: 154
+155: 155
+156: 156
+157: 157
+158: 158
+159: 159
+160: 160
+161: 161
+162: 162
+163: 163
+164: 164
+165: 165
+166: 166
+167: 167
+168: 168
+169: 169
+170: 170
+171: 171
+172: 172
+173: 173
+174: 174
+175: 175
+176: 176
+177: 177
+178: 178
+179: 179
+180: 180
+181: 181
+182: 182
+183: 183
+184: 184
+185: 185
+186: 186
+187: 187
+188: 188
+189: 189
+190: 190
+191: 191
+192: 192
+193: 193
+194: 194
+195: 195
+196: 196
+197: 197
+198: 198
+199: 199
+200: 200
+201: 201
+202: 202
+203: 203
+204: 204
+205: 205
+206: 206
+207: 207
+208: 208
+209: 209
+210: 210
+211: 211
+212: 212
+213: 213
+214: 214
+215: 215
+216: 216
+217: 217
+218: 218
+219: 219
+220: 220
+221: 221
+222: 222
+223: 223
+224: 224
+225: 225
+226: 226
+227: 227
+228: 228
+229: 229
+230: 230
+231: 231
+232: 232
+233: 233
+234: 234
+235: 235
+236: 236
+237: 237
+238: 238
+239: 239
+240: 240
+241: 241
+242: 242
+243: 243
+244: 244
+245: 245
+246: 246
+247: 247
+248: 248
+249: 249
+250: 250
+251: 251
+252: 252
+253: 253
+254: 254
+255: 255
+0, 128, 215, 167, 65536
+33601, 60068, 60069
+42146, 62630, 62631
+65507, 8213, 12316, 8214, 8230, 8722, 65509, 65504, 65505, 65506
+8254, 8212, 12316, 8214, 8230, 8722, 165, 162, 163, 172
+65507, 8213, 65374, 8741, 8943, 65293, 65509, 65504, 65505, 65506
+ )
+No pages of output.
diff --git a/Build/source/texk/web2c/euptexdir/tests/ucs.tex b/Build/source/texk/web2c/euptexdir/tests/ucs.tex
new file mode 100644
index 00000000000..520a7485d57
--- /dev/null
+++ b/Build/source/texk/web2c/euptexdir/tests/ucs.tex
@@ -0,0 +1,36 @@
+%#!eptex
+
+\def\typeout{\immediate\write17}
+
+\ifnum`あ="A4A2 \typeout{<EUC>}
+\else\ifnum`ア="8341 \typeout{<SJIS>}
+\else\typeout{<uptex>}\fi\fi
+
+\typeout{\string\ucs.}
+\newcount\cntA
+\loop\ifnum\cntA<256
+ \ifnum0>\ucs\cntA\else\typeout{\the\cntA: \ucs\cntA %
+ \ifx\Uchar\undefined\else(\Uchar\ucs\cntA)\fi}\fi
+ \advance\cntA1
+\repeat
+
+
+\typeout{\string\toucs.}
+\cntA=0
+\loop\ifnum\cntA<256
+ \ifnum0>\toucs\cntA\else\typeout{\the\cntA: \toucs\cntA}\fi
+ \advance\cntA1
+\repeat
+\typeout{\toucs"0, \toucs"80, \toucs"D7, \toucs"A7, \toucs"10000}
+\typeout{\toucs"8341, \toucs"EAA4, \toucs"EAA5}
+\typeout{\toucs"A4A2, \toucs"F4A6, \toucs"F4A7}
+\typeout{\jis"2131, \jis"213D, \jis"2141, \jis"2142, \jis"2144,
+ \jis"215D, \jis"216F, \jis"2171, \jis"2172, \jis"224C}
+\typeout{\ucs"203E, \ucs"2014, \ucs"301C, \ucs"2016, \ucs"2026,
+ \ucs"2212, \ucs"00A5, \ucs"00A2, \ucs"00A3, \ucs"00AC}
+\typeout{\ucs"FFE3, \ucs"2015, \ucs"FF5E, \ucs"2225, \ucs"22EF,
+ \ucs"FF0D, \ucs"FFE5, \ucs"FFE0, \ucs"FFE1, \ucs"FFE2}
+
+
+\bye
+
diff --git a/Build/source/texk/web2c/gftodvi.ch b/Build/source/texk/web2c/gftodvi.ch
index 90ecd85da2a..5b895f02ae0 100644
--- a/Build/source/texk/web2c/gftodvi.ch
+++ b/Build/source/texk/web2c/gftodvi.ch
@@ -35,6 +35,12 @@
% Revision 0.3 84/11/17 23:51:56 richards
% Base version for GFtoDVI Version 0.3
+@x [0] l.29
+\def\(#1){} % this is used to make section names sort themselves better
+@y
+\def\({} % this is used to make section names sort themselves better
+@z
+
@x [0] l.32
\def\title{GF$\,$\lowercase{to}$\,$DVI}
@y
@@ -77,7 +83,7 @@ procedure initialize; {this procedure gets things started properly}
const @<Constants in the outer block@>@/
type @<Types in the outer block@>@/
var @<Globals in the outer block@>@/
-@<Define |parse_arguments|@>
+@<Define \(|parse_arguments|@>
procedure initialize; {this procedure gets things started properly}
var @!i,@!j,@!m,@!n:integer; {loop indices for initializations}
begin
@@ -612,7 +618,7 @@ Parse a Unix-style command line.
@d argument_is (#) == (strcmp (long_options[option_index].name, #) = 0)
-@<Define |parse_arguments|@> =
+@<Define \(|parse_arguments|@> =
procedure parse_arguments;
const n_options = 4; {Pascal won't count array lengths for us.}
var @!long_options: array[0..n_options] of getopt_struct;
diff --git a/Build/source/texk/web2c/gftopk.ch b/Build/source/texk/web2c/gftopk.ch
index 1afa9119ef7..f892e4ba309 100644
--- a/Build/source/texk/web2c/gftopk.ch
+++ b/Build/source/texk/web2c/gftopk.ch
@@ -11,6 +11,12 @@
% on the origin of fonts in both gf and pk formats. The program runs
% silently unless it is given the -v switch in the command line.
+@x [0] l.41
+\def\(#1){} % this is used to make section names sort themselves better
+@y
+\def\({} % this is used to make section names sort themselves better
+@z
+
@x [0] l.44
\def\title{GFtoPK}
@y
@@ -63,7 +69,7 @@ produced only when the \.{-v} command line flag is presented.
const @<Constants in the outer block@>@/
type @<Types in the outer block@>@/
var @<Globals in the outer block@>@/
-@<Define |parse_arguments|@>
+@<Define \(|parse_arguments|@>
procedure initialize; {this procedure gets things started properly}
var i:integer; {loop index for initializations}
begin
@@ -82,7 +88,7 @@ begin
@<Labels...@>=final_end;
@y
@ This module is deleted, because it is only useful for
-a non-local goto, which we can't use in C.
+a non-local |goto|\unskip, which we can't use in C.
@z
@x [6] Dynamic allocation of |row| array.
@@ -414,7 +420,7 @@ Parse a Unix-style command line.
@d argument_is (#) == (strcmp (long_options[option_index].name, #) = 0)
@d do_nothing == {empty statement}
-@<Define |parse_arguments|@> =
+@<Define \(|parse_arguments|@> =
procedure parse_arguments;
const n_options = 3; {Pascal won't count array lengths for us.}
var @!long_options: array[0..n_options] of getopt_struct;
diff --git a/Build/source/texk/web2c/gftype.ch b/Build/source/texk/web2c/gftype.ch
index 49d7bf6b3e0..74bc8d4dec1 100644
--- a/Build/source/texk/web2c/gftype.ch
+++ b/Build/source/texk/web2c/gftype.ch
@@ -15,6 +15,12 @@
% turns on images. There is no terminal input to this program.
% Output is to stdout, and may, of course, be redirected.
+@x [0] l.27
+\def\(#1){} % this is used to make section names sort themselves better
+@y
+\def\({} % this is used to make section names sort themselves better
+@z
+
@x [0] l.30
\def\title{GFtype}
@y
@@ -57,7 +63,7 @@ procedure initialize; {this procedure gets things started properly}
const @<Constants in the outer block@>@/
type @<Types in the outer block@>@/
var @<Globals in the outer block@>@/
-@<Define |parse_arguments|@>
+@<Define \(|parse_arguments|@>
procedure initialize; {this procedure gets things started properly}
var i:integer; {loop index for initializations}
@!bound_default:integer; {temporary for setup}
@@ -78,7 +84,7 @@ begin
@<Labels...@>=final_end;
@y
@ This module is deleted, because it is only useful for
-a non-local goto, which we can't use in C.
+a non-local |goto|\unskip, which we can't use in C.
Instead, we define parameters settable at runtime.
@@ -443,7 +449,7 @@ Parse a Unix-style command line.
@d argument_is (#) == (strcmp (long_options[option_index].name, #) = 0)
@d do_nothing == {empty statement}
-@<Define |parse_arguments|@> =
+@<Define \(|parse_arguments|@> =
procedure parse_arguments;
const n_options = 4; {Pascal won't count array lengths for us.}
var @!long_options: array[0..n_options] of getopt_struct;
diff --git a/Build/source/texk/web2c/hitexdir/hitex.w b/Build/source/texk/web2c/hitexdir/hitex.w
index 7351c672e2d..cad0605c82f 100644
--- a/Build/source/texk/web2c/hitexdir/hitex.w
+++ b/Build/source/texk/web2c/hitexdir/hitex.w
@@ -224,7 +224,6 @@
@s alpha_file int
@s byte_file int
@s word_file int
-@s int8_t int
@s uint8_t int
@s int16_t int
@s uint16_t int
@@ -250,13 +249,9 @@
@s line normal
@s to do
-\def\contentspagenumber{0}
-\def\tocpages{3} % HiTeX has a long ToC
-\pageno=\contentspagenumber \advance \pageno by \tocpages
-
@* Introduction.
This is Hi\TeX, a program derived from \TeX, extending its capabilities
-using \eTeX\ and \Prote, and adding functions common to other engines from
+using \eTeX and \Prote, and adding functions common to other engines from
the \TeX\ Live distribution. Hi\TeX\ writes output files in
the \HINT\ file format. Like \TeX, it is
a document compiler intended to produce typesetting of high
@@ -380,7 +375,7 @@ known as `\Prote'.
@d Prote_banner "This is Prote, Version " Prote_version_string
/*printed when \Prote\ starts*/
@#
-@d banner "This is HiTeX, Version 3.141592653"@|
+@d banner "This is HiTeX, Version 3.141592653"
eTeX_version_string"-"HINT_VERSION_STRING" "TL_VERSION
/*printed when \TeX\ starts*/
@@ -436,7 +431,7 @@ portions of the program.
The program starts with inserting header files and occassionaly a function
must be placed before declaring \TeX's macros, because the function
-uses identifiers that \TeX\ will declare as macros.
+uses identifiers that \TeX will declare as macros.
@p @<Header files and function declarations@>@;
@h
@@ -447,7 +442,7 @@ enum {@+@<Constants in the outer block@>@+};
@#
static void initialize(void) /*this procedure gets things started properly*/
{@+@<Local variables for initialization@>@;
- @<Initialize whatever \TeX\ might access@>@;
+ @<Initialize whatever \TeX\ might access@>;
} @#
@<Basic printing procedures@>@;
@<Error handling procedures@>@;
@@ -634,7 +629,7 @@ because some fussy \PASCAL\ compilers will complain about redundant labels.
@d negate(A) A=-A /*change the sign of a variable*/
@d loop @+while (true) @+ /*repeat over and over until a |goto| happens*/
@f loop else
- /*\.{WEB}'s |loop| acts like `\ignorespaces|while true do|\unskip'*/
+ /*\.{WEB}'s |else| acts like `\ignorespaces|while true do|\unskip'*/
@d do_nothing /*empty statement*/
@d empty 0 /*symbolic name for a null constant*/
@@ -1334,7 +1329,7 @@ while (j < str_start[s+1])
result=true;
not_found: return result;
}
-@#@<Declare \Prote\ procedures for strings@>@;
+@t\4@>@<Declare \Prote\ procedures for strings@>@;
@ The initial values of |str_pool|, |str_start|, |pool_ptr|,
and |str_ptr| are computed by the \.{INITEX} program, based in part
@@ -1425,8 +1420,6 @@ declaration.
@<Forward declarations@>=
static int s_no(const char *str);
-@ (Empty section to keep numbering intact.)
-
@* On-line and off-line printing.
Messages that are sent to a user's terminal and to the transcript-log file
are produced by several `|print|' procedures. These procedures will
@@ -1498,10 +1491,8 @@ for terminal output, and it is possible to adhere to those conventions
by changing |wterm|, |wterm_ln|, and |wterm_cr| in this section.
@^system dependencies@>
-@s sizeof x
-
@<Basic printing procedures@>=
-#define @[put(F)@] @[fwrite(&((F).d),sizeof((F).d),1,(F).f)@]
+#define @[put(F)@] @[fwrite(&((F).d)@],@[sizeof((F).d),1,(F).f)@]@;
#define @[get(F)@] @[fread(&((F).d),sizeof((F).d),1,(F).f)@]
#define @[pascal_close(F)@] @[fclose((F).f)@]
@@ -1708,8 +1699,8 @@ if (n < 0)
}
}
}
-do {dig[k]=n%10;n=n/10;incr(k);
-} while (!(n==0));
+@/do@+{dig[k]=n%10;n=n/10;incr(k);
+}@+ while (!(n==0));
print_the_digs(k);
}
@@ -1727,8 +1718,8 @@ print_char('0'+(n%10));
/*prints a positive integer in hexadecimal form*/
{@+int k; /*index to current digit; we assume that $0\le n<16^{22}$*/
k=0;print_char('"');
-do {dig[k]=n%16;n=n/16;incr(k);
-} while (!(n==0));
+@/do@+{dig[k]=n%16;n=n/16;incr(k);
+}@+ while (!(n==0));
print_the_digs(k);
}
@@ -1804,7 +1795,7 @@ $$\vbox{\halign{#\hfil\cr
|help3("This is the first line of my offer to help.")|\cr
|("This is the second line. I'm trying to")|\cr
|("explain the best way for you to proceed.");|\cr
-|error();|\cr}}$$
+|error;|\cr}}$$
A two-line help message would be given using |help2|, etc.; these informal
helps should use simple vocabulary that complements the words used in the
official error message that was printed. (Outside the U.S.A., the help
@@ -2010,6 +2001,7 @@ case '0': case '1': case '2': case '3':
case '4': case '5': case '6': case '7':
case '8': case '9': if (deletions_allowed)
@<Delete \(c)|c-"0"| tokens and |goto resume|@>@;@+break;
+@t\4\4@>@;
#ifdef @!DEBUG
case 'D': {@+debug_help();goto resume;@+}
#endif
@@ -2102,8 +2094,8 @@ show_context();goto resume;
else{@+if (help_ptr==0)
help2("Sorry, I don't know how to help in this situation.",@/
@t\kern1em@>"Maybe you should try asking a human?");
- do {decr(help_ptr);print(help_line[help_ptr]);print_ln();
- } while (!(help_ptr==0));
+ @/do@+{decr(help_ptr);print(help_line[help_ptr]);print_ln();
+ }@+ while (!(help_ptr==0));
}
help4("Sorry, I already gave what help I could...",@/
"Maybe you should try asking a human?",@/
@@ -2272,7 +2264,7 @@ positions from the right end of a binary computer word.
@<Types...@>=
typedef int scaled; /*this type is used for scaled integers*/
-typedef uint32_t nonnegative_integer; /*$0\le x<2^{31}$*/
+typedef int32_t nonnegative_integer; /*$0\le x<2^{31}$*/
typedef int8_t small_number; /*this type is self-explanatory*/
@ The following function is used to create a scaled integer from a given decimal
@@ -2310,9 +2302,9 @@ if (s < 0)
print_int(s/unity); /*print the integer part*/
print_char('.');
s=10*(s%unity)+5;delta=10;
-do {if (delta > unity) s=s+0100000-50000; /*round the last digit*/
+@/do@+{if (delta > unity) s=s+0100000-50000; /*round the last digit*/
print_char('0'+(s/unity));s=10*(s%unity);delta=delta*10;
-} while (!(s <= delta));
+}@+ while (!(s <= delta));
}
@ Physical sizes that a \TeX\ user specifies for portions of documents are
@@ -2429,7 +2421,7 @@ computing at most 1095 distinct values, but that is plenty.
@d inf_bad 10000 /*infinitely bad value*/
-@p @<Declare \Prote\ arithmetic routines@>@;
+@p @<Declare \Prote\ arithmetic routines@>@/
static halfword badness(scaled @!t, scaled @!s) /*compute badness, given |t >= 0|*/
{@+int r; /*approximation to $\alpha t/s$, where $\alpha^3\approx
100\cdot2^{18}$*/
@@ -2464,16 +2456,12 @@ routines cited there must be modified to allow negative glue ratios.)
@d unfix(A) ((double)(A)) /*convert from |glue_ratio| to type |double|*/
@d fix(A) ((glue_ratio)(A)) /*convert from |double| to type |glue_ratio|*/
@d float_constant(A) ((double)(A)) /*convert |int| constant to |double|*/
-@#
-@d perror e@&r@&r@&o@&r /* this is a \.{CWEB} coding trick: */
-@f perror error /* `\&{perror}' will be equivalent to `\&{error}' */
-@f error x /* but `|error|' will not be treated as a reserved word */
@<Types...@>=
#if __SIZEOF_FLOAT__==4
typedef float float32_t;
#else
-#perror @=float type must have size 4@>
+#error @=float type must have size 4@>
#endif
typedef float @!glue_ratio; /*one-word representation of a glue expansion factor*/
@@ -2583,29 +2571,25 @@ typedef uint16_t quarterword; /*1/4 of a word*/
typedef int32_t halfword; /*1/2 of a word*/
typedef int8_t two_choices; /*used when there are two variants in a record*/
typedef int8_t four_choices; /*used when there are four variants in a record*/
-@#
-typedef struct {
+typedef struct { @;@/
halfword @!rh;
union {
halfword @!lh;
struct { quarterword @!b0;quarterword @!b1;} ;
};} two_halves;
-@#
-typedef struct {
+typedef struct { @;@/
quarterword @!b0;
quarterword @!b1;
quarterword @!b2;
quarterword @!b3;
} four_quarters;
-@#
-typedef struct {
+typedef struct { @;@/
union {
int @!i;
glue_ratio @!gr;
two_halves @!hh;
four_quarters @!qqqq;
};} memory_word;
-@#
typedef struct {@+FILE *f;@+memory_word@,d;@+} word_file;
@ When debugging, we may want to print a |memory_word| without knowing
@@ -2788,11 +2772,11 @@ one-word nodes that starts at position |p|.
{@+pointer @!q, @!r; /*list traversers*/
if (p!=null)
{@+r=p;
- do {q=r;r=link(r);
+ @/do@+{q=r;r=link(r);
#ifdef @!STAT
decr(dyn_used);
#endif
- } while (!(r==null)); /*now |q| is the last node on the list*/
+ }@+ while (!(r==null)); /*now |q| is the last node on the list*/
link(q)=avail;avail=p;
}
}
@@ -2837,11 +2821,11 @@ pointer @!q; /*the node physically after node |p|*/
int @!r; /*the newly allocated node, or a candidate for this honor*/
int @!t; /*temporary register*/
restart: p=rover; /*start at some free node in the ring*/
-do {@<Try to allocate within node |p| and its physical successors, and
+@/do@+{@<Try to allocate within node |p| and its physical successors, and
|goto found| if allocation was possible@>;
@^inner loop@>
p=rlink(p); /*move to the next node in the ring*/
-} while (!(p==rover)); /*repeat until the whole list has been traversed*/
+}@+ while (!(p==rover)); /*repeat until the whole list has been traversed*/
if (s==010000000000)
{@+return max_halfword;
}
@@ -3334,7 +3318,7 @@ current \.{\\lineskip}.
pointer @!q; /*the glue specification*/
p=get_node(small_node_size);type(p)=glue_node;subtype(p)=n+1;
leader_ptr(p)=null;@/
-q=@[@<Current |mem| equivalent of glue parameter number |n|@>@];
+q=@<Current |mem| equivalent of glue parameter number |n|@>@t@>;
glue_ptr(p)=q;incr(glue_ref_count(q));
return p;
}
@@ -3374,8 +3358,6 @@ kerns inserted from font information or math mode calculations) or |explicit|
(for kerns inserted from non-math accents) or |mu_glue| (for kerns
inserted from \.{\\mkern} specifications in math formulas).
-@s explicit true
-
@d kern_node 11 /*|type| of a kern node*/
@d explicit 1 /*|subtype| of kern nodes from \.{\\kern} and \.{\\/}*/
@d acc_kern 2 /*|subtype| of kern nodes from accents*/
@@ -3591,7 +3573,7 @@ done1:
@ @<Check variable-size...@>=
p=rover;q=null;clobbered=false;
-do {if ((p >= lo_mem_max)||(p < mem_min)) clobbered=true;
+@/do@+{if ((p >= lo_mem_max)||(p < mem_min)) clobbered=true;
else if ((rlink(p) >= lo_mem_max)||(rlink(p) < mem_min)) clobbered=true;
else if (!(is_empty(p))||(node_size(p) < 2)||@|
(p+node_size(p) > lo_mem_max)||@|(llink(rlink(p))!=p)) clobbered=true;
@@ -3608,7 +3590,7 @@ for (q=p; q<=p+node_size(p)-1; q++) /*mark all locations free*/
is_free[q]=true;
}
q=p;p=rlink(p);
-} while (!(p==rover));
+}@+ while (!(p==rover));
done2:
@ @<Check flags...@>=
@@ -3703,13 +3685,13 @@ while (p > mem_min)
{@+if ((font(p) < font_base)||(font(p) > font_max))
print_char('*');
@.*\relax@>
- else@<Print the font identifier for |font(p)|@>@;
+ else@<Print the font identifier for |font(p)|@>;
print_char(' ');font_in_short_display=font(p);
}
print_ASCII(qo(character(p)));
}
}
- else@<Print a short indication of the contents of node |p|@>@;
+ else@<Print a short indication of the contents of node |p|@>;
p=link(p);
}
}
@@ -3742,7 +3724,7 @@ its reference count, and one to print a rule dimension.
{@+if (p > mem_end) print_esc("CLOBBERED.");
else{@+if ((font(p) < font_base)||(font(p) > font_max)) print_char('*');
@.*\relax@>
- else@<Print the font identifier for |font(p)|@>@;
+ else@<Print the font identifier for |font(p)|@>;
print_char(' ');print_ASCII(qo(character(p)));
}
}
@@ -3882,7 +3864,7 @@ else switch (type(p)) {
case disc_node: @<Display discretionary |p|@>@;@+break;
case mark_node: @<Display mark |p|@>@;@+break;
case adjust_node: @<Display adjustment |p|@>@;@+break;
- @/@t\4@>@<Cases of |show_node_list| that arise in mlists only@>@;
+ @t\4@>@<Cases of |show_node_list| that arise in mlists only@>@;
default:print("Unknown node type!");
}
@@ -4081,7 +4063,7 @@ specification is being withdrawn.
}
@p static void delete_glue_ref(pointer @!p) /*|p| points to a glue specification*/
-fast_delete_glue_ref(p)@;
+fast_delete_glue_ref(p)
static void delete_xdimen_ref(pointer @!p) /*|p| points to a xdimen specification*/
{@+if (p==null) return;
if (xdimen_ref_count(p)==null) free_node(p, xdimen_node_size);
@@ -4122,7 +4104,7 @@ while (p!=null)
flush_node_list(post_break(p));
} @+break;
case adjust_node: flush_node_list(adjust_ptr(p));@+break;
- @/@t\4@>@<Cases of |flush_node_list| that arise in mlists only@>@;
+ @t\4@>@<Cases of |flush_node_list| that arise in mlists only@>@;
default:confusion("flushing");
@:this can't happen flushing}{\quad flushing@>
} @/
@@ -5022,7 +5004,7 @@ else switch (chr_code) {
case every_vbox_loc: print_esc("everyvbox");@+break;
case every_job_loc: print_esc("everyjob");@+break;
case every_cr_loc: print_esc("everycr");@+break;
- @/@t\4@>@<Cases of |assign_toks| for |print_cmd_chr|@>@;
+ @/@<Cases of |assign_toks| for |print_cmd_chr|@>@/
default:print_esc("errhelp");
} @+break;
@@ -5350,7 +5332,7 @@ case right_hyphen_min_code: print_esc("righthyphenmin");@+break;
case holding_inserts_code: print_esc("holdinginserts");@+break;
case error_context_lines_code: print_esc("errorcontextlines");@+break;
case tracing_stack_levels_code: print_esc("tracingstacklevels");@+break;
-@/@t\4@>@<Cases for |print_param|@>@;
+@/@<Cases for |print_param|@>@/
default:print("[unknown integer parameter!]");
}
}
@@ -5503,7 +5485,6 @@ defined by |SOURCE_DATE_EPOCH|.
@^reference time@>
@^system dependencies@>
\TeX\ Live calls |tl_now| to obtain the current time as a |tm| structure.
-@s tm int
@p static void fix_date_and_time(void)
{@+ struct tm *t=tl_now();
time=sys_time= t->tm_hour*60+t->tm_min;/*minutes since midnight*/
@@ -5707,9 +5688,8 @@ print_char('=');print_scaled(eqtb[n].sc);print("pt");
@ Here is a procedure that displays the contents of |eqtb[n]|
symbolically.
-@p @<Declare the procedure called |print_cmd_chr|@>@;
+@p @t\4@>@<Declare the procedure called |print_cmd_chr|@>@;@/
#ifdef @!STAT
-@+@t}\6\4\4{@>
static void show_eqtb(pointer @!n)
{@+if (n < active_base) print_char('?'); /*this can't happen*/
else if (n < glue_base) @<Show equivalent |n|, in region 1 or 2@>@;
@@ -5834,10 +5814,10 @@ found: return p;
@ @<Insert a new control...@>=
{@+if (text(p) > 0)
- {@+do {if (hash_is_full) overflow("hash size", hash_size);
+ {@+@/do@+{if (hash_is_full) overflow("hash size", hash_size);
@:TeX capacity exceeded hash size}{\quad hash size@>
decr(hash_used);
- } while (!(text(hash_used)==0)); /*search for an empty location in |hash|*/
+ }@+ while (!(text(hash_used)==0)); /*search for an empty location in |hash|*/
next(p)=hash_used;p=hash_used;
}
str_room(l);d=cur_length;
@@ -6055,7 +6035,7 @@ case end_group: print_esc("endgroup");@+break;
case ex_space: print_esc(" ");@+break;
case expand_after: switch (chr_code) {
case 0: print_esc("expandafter");@+break;
-@/@t\4@>@<Cases of |expandafter| for |print_cmd_chr|@>@;
+@/@<Cases of |expandafter| for |print_cmd_chr|@>@/
} @+break; /*there are no other cases*/
case halign: print_esc("halign");@+break;
case hrule: print_esc("hrule");@+break;
@@ -6075,17 +6055,17 @@ case no_expand: print_esc("noexpand");@+break;
case non_script: print_esc("nonscript");@+break;
case omit: print_esc("omit");@+break;
case radical: print_esc("radical");@+break;
-case read_to_cs: if (chr_code==0) print_esc("read");
- else @<Cases of |read| for |print_cmd_chr|@>;@+break;
+case read_to_cs: if (chr_code==0) print_esc("read")
+ @<Cases of |read| for |print_cmd_chr|@>;@+break;
case relax: print_esc("relax");@+break;
case set_box: print_esc("setbox");@+break;
case set_prev_graf: print_esc("prevgraf");@+break;
case set_shape: switch (chr_code) {
case par_shape_loc: print_esc("parshape");@+break;
- @/@t\4@>@<Cases of |set_shape| for |print_cmd_chr|@>@;
+ @<Cases of |set_shape| for |print_cmd_chr|@>@;@/
} @+break; /*there are no other cases*/
-case the: if (chr_code==0) print_esc("the");
- else @<Cases of |the| for |print_cmd_chr|@>;@+break;
+case the: if (chr_code==0) print_esc("the")
+ @<Cases of |the| for |print_cmd_chr|@>;@+break;
case toks_register: @<Cases of |toks_register| for |print_cmd_chr|@>@;@+break;
case vadjust: print_esc("vadjust");@+break;
case valign: print_esc("valign");@+break;
@@ -6106,7 +6086,7 @@ Meanwhile, this is a convenient place to catch up on something we were unable
to do before the hash table was defined:
@<Print the font identifier for |font(p)|@>=
-printn_esc(font_id_text(font(p)));
+printn_esc(font_id_text(font(p)))
@* Saving and restoring equivalents.
The nested structure provided by `$\.{\char'173}\ldots\.{\char'175}$' groups
@@ -6291,7 +6271,7 @@ case shape_ref: {@+q=equiv_field(w); /*we need to free a \.{\\parshape} block*/
if (q!=null) free_node(q, info(q)+info(q)+1);
} @+break; /*such a block is |2 n+1| words long, where |n==info(q)|*/
case box_ref: flush_node_list(equiv_field(w));@+break;
-@/@t\4@>@<Cases for |eq_destroy|@>@;
+@/@<Cases for |eq_destroy|@>@/
default:do_nothing;
}
}
@@ -6329,7 +6309,7 @@ the call, since |eq_save| makes the necessary test.
#ifdef @!STAT
#define assign_trace(A, B) if (tracing_assigns > 0) restore_trace(A, B);
#else
-#define assign_trace(A, B) @[@]
+#define assign_trace(A, B)
#endif
static void eq_define(pointer @!p, quarterword @!t, halfword @!e)
@@ -6834,7 +6814,7 @@ case endv: print("end of alignment template");@+break;
case spacer: chr_cmd("blank space ")@;@+break;
case letter: chr_cmd("the letter ")@;@+break;
case other_char: chr_cmd("the character ")@;@+break;
-@/@t\4@>@<Cases of |print_cmd_chr| for symbolic printing of primitives@>@;
+@t\4@>@<Cases of |print_cmd_chr| for symbolic printing of primitives@>@/
default:print("[unknown command code!]");
}
}
@@ -7572,7 +7552,7 @@ actions.
in_open=0;open_parens=0;max_buf_stack=0;
grp_stack[0]=0;if_stack[0]=null;
param_ptr=0;max_param_stack=0;
-first=buf_size;do {buffer[first]=0;decr(first);} while (!(first==0));
+first=buf_size;@/do@+{buffer[first]=0;decr(first);}@+ while (!(first==0));
scanner_status=normal;warning_index=null;first=1;
state=new_line;start=1;index=0;line=0;name=0;
force_eof=false;
@@ -7783,7 +7763,7 @@ and set |state:=mid_line|@>@;@+break;
any_state_plus(sup_mark): @<If this |sup_mark| starts an expanded character
like~\.{\^\^A} or~\.{\^\^df}, then |goto reswitch|, otherwise set |state:=mid_line|@>@;@+break;
any_state_plus(invalid_char): @<Decry the invalid character and |goto restart|@>@;
-@/@t\4@>@<Handle situations involving spaces, braces, changes of state@>@;
+@t\4@>@<Handle situations involving spaces, braces, changes of state@>@;
default:do_nothing;
}
@@ -7942,8 +7922,8 @@ the buffer left two or three places.
}
@ @<Scan ahead in the buffer...@>=
-{@+do {cur_chr=buffer[k];cat=cat_code(cur_chr);incr(k);
-} while (!((cat!=letter)||(k > limit)));
+{@+@/do@+{cur_chr=buffer[k];cat=cat_code(cur_chr);incr(k);
+}@+ while (!((cat!=letter)||(k > limit)));
@<If an expanded...@>;
if (cat!=letter) decr(k);
/*now |k| points to first nonletter*/
@@ -8188,7 +8168,7 @@ case top_bot_mark: @<Insert the \(a)appropriate mark text into the scanner@>@;@+
case expand_after: switch (cur_chr) {
case 0: @<Expand the token after the next token@>@;@+break;
case 1: @<Negate a boolean conditional and |goto reswitch|@>@;@+break;
-@/@t\4@>@<Cases for |expandafter|@>@;
+@/@<Cases for |expandafter|@>@/
} @+break; /*there are no other cases*/
case no_expand: @<Suppress expansion of the next token@>@;@+break;
case cs_name: @<Manufacture a control sequence name@>@;@+break;
@@ -8252,9 +8232,9 @@ the variables |p| and |q| are reserved for token-list building.
@ @<Manufacture a control...@>=
{@+r=get_avail();p=r; /*head of the list of characters*/
incr(incsname_state);
-do {get_x_token();
+@/do@+{get_x_token();
if (cur_cs==0) store_new_token(cur_tok);
-} while (!(cur_cs!=0));
+}@+ while (!(cur_cs!=0));
if (cur_cmd!=end_cs_name) @<Complain about missing \.{\\endcsname}@>;
decr(incsname_state);
@<Look up the characters of list |r| in the hash table, and set |cur_cs|@>;
@@ -8312,15 +8292,15 @@ primitive("endinput", input, 1);@/
@!@:end\_input\_}{\.{\\endinput} primitive@>
@ @<Cases of |print_cmd_chr|...@>=
-case input: if (chr_code==0) print_esc("input");
- else @<Cases of |input| for |print_cmd_chr|@>@;
+case input: if (chr_code==0) print_esc("input")
+ @/@<Cases of |input| for |print_cmd_chr|@>;@/
else print_esc("endinput");@+break;
@ @<Initiate or terminate input...@>=
-if (cur_chr==1) force_eof=true;
-else @<Cases for |input|@>@;
+if (cur_chr==1) force_eof=true
+@/@<Cases for |input|@>;@/
else if (name_in_progress) insert_relax();
-else start_input();
+else start_input()
@ Sometimes the expansion looks too far ahead, so we want to insert
a harmless \.{\\relax} into the user's input.
@@ -8530,7 +8510,7 @@ a string that will delimit the next parameter.
{@+scanner_status=matching;unbalance=0;
long_state=eq_type(cur_cs);
if (long_state >= outer_call) long_state=long_state-2;
-do {link(temp_head)=null;
+@/do@+{link(temp_head)=null;
if ((info(r) > match_token+255)||(info(r) < match_token)) s=null;
else{@+match_chr=info(r)-match_token;s=link(r);r=s;
p=temp_head;m=0;
@@ -8538,7 +8518,7 @@ else{@+match_chr=info(r)-match_token;s=link(r);r=s;
@<Scan a parameter until its delimiter string has been found; or, if |s=null|,
simply scan the delimiter string@>;@/
/*now |info(r)| is a token whose command code is either |match| or |end_match|*/
-} while (!(info(r)==end_match_token));
+}@+ while (!(info(r)==end_match_token));
}
@ If |info(r)| is a |match| or |end_match| command, it cannot be equal to
@@ -8638,7 +8618,7 @@ the rules.
if (s!=r)
if (s==null) @<Report an improper use of the macro and abort@>@;
else{@+t=s;
- do {store_new_token(info(t));incr(m);u=link(t);v=s;
+ @/do@+{store_new_token(info(t));incr(m);u=link(t);v=s;
loop@+{@+if (u==r)
if (cur_tok!=info(v)) goto done;
else{@+r=link(v);goto resume;
@@ -8647,7 +8627,7 @@ if (s!=r)
u=link(u);v=link(v);
}
done: t=link(t);
- } while (!(t==r));
+ }@+ while (!(t==r));
r=s; /*at this point, no tokens are recently matched*/
}
@@ -8739,7 +8719,8 @@ if (cur_cmd!=left_brace)
}
@ @<Get the next non-blank non-relax non-call token@>=
-do get_x_token(); while (!((cur_cmd!=spacer)&&(cur_cmd!=relax)))
+@/do@+{get_x_token();
+}@+ while (!((cur_cmd!=spacer)&&(cur_cmd!=relax)))
@ The |scan_optional_equals| routine looks for an optional `\.=' sign preceded
by optional spaces; `\.{\\relax}' is not ignored here.
@@ -8750,7 +8731,8 @@ if (cur_tok!=other_token+'=') back_input();
}
@ @<Get the next non-blank non-call token@>=
-do get_x_token(); while (!(cur_cmd!=spacer))
+@/do@+{get_x_token();
+}@+ while (!(cur_cmd!=spacer))
@ In case you are getting bored, here is a slightly less trivial routine:
Given a string of lowercase letters, like `\.{pt}' or `\.{plus}' or
@@ -8809,9 +8791,9 @@ have to declare |scan_int| as a |forward| procedure. A few other
procedures are also declared at this point.
@p static void scan_int(void); /*scans an integer value*/
-@#@<Declare procedures that scan restricted classes of integers@>@;
-@<Declare \eTeX\ procedures for scanning@>@;
-@<Declare procedures that scan font-related stuff@>@;
+@t\4\4@>@<Declare procedures that scan restricted classes of integers@>@;
+@t\4\4@>@<Declare \eTeX\ procedures for scanning@>@;
+@t\4\4@>@<Declare procedures that scan font-related stuff@>@;
@ \TeX\ doesn't know exactly what to expect when |scan_something_internal|
begins. For example, an integer or dimension or glue value could occur
@@ -9009,9 +8991,9 @@ primitive("badness", last_item, badness_code);
@ @<Cases of |print_cmd_chr|...@>=
case set_aux: if (chr_code==vmode) print_esc("prevdepth");
@+else print_esc("spacefactor");@+break;
-case set_page_int: if (chr_code==0) print_esc("deadcycles");
-else @<Cases of |set_page_int| for |print_cmd_chr|@>@;
-else print_esc("insertpenalties");@+break;
+case set_page_int: if (chr_code==0) print_esc("deadcycles")
+@/@<Cases of |set_page_int| for |print_cmd_chr|@>;@/
+@+else print_esc("insertpenalties");@+break;
case set_box_dimen: if (chr_code==width_offset) print_esc("wd");
else if (chr_code==height_offset) print_esc("ht");
else print_esc("dp");@+break;
@@ -9020,7 +9002,7 @@ case last_item: switch (chr_code) {
case dimen_val: print_esc("lastkern");@+break;
case glue_val: print_esc("lastskip");@+break;
case input_line_no_code: print_esc("inputlineno");@+break;
- @/@t\4@>@<Cases of |last_item| for |print_cmd_chr|@>@;
+ @/@<Cases of |last_item| for |print_cmd_chr|@>@/
default:print_esc("badness");
} @+break;
@@ -9042,8 +9024,8 @@ else if (m==vmode)
else scanned_result(space_factor, int_val)
@ @<Fetch the |dead_cycles| or the |insert_penalties|@>=
-{@+if (m==0) cur_val=dead_cycles;
-else @<Cases for `Fetch the |dead_cycles| or the |insert_penalties|'@>@;
+{@+if (m==0) cur_val=dead_cycles
+@/@<Cases for `Fetch the |dead_cycles| or the |insert_penalties|'@>;@/
else cur_val=insert_penalties;
cur_val_level=int_val;
}
@@ -9094,14 +9076,14 @@ else if (m >= input_line_no_code)
if (m >= eTeX_glue) @<Process an expression and |return|@>@;
else if (m >= eTeX_dim)
{@+switch (m) {
- @/@t\4@>@<Cases for fetching a dimension value@>@;
+ @/@<Cases for fetching a dimension value@>@/
} /*there are no other cases*/
cur_val_level=dimen_val;
}
else{@+switch (m) {
case input_line_no_code: cur_val=line;@+break;
case badness_code: cur_val=last_badness;@+break;
- @/@t\4@>@<Cases for fetching an integer value@>@;
+ @/@<Cases for fetching an integer value@>@/
} /*there are no other cases*/
cur_val_level=int_val;
}
@@ -9326,11 +9308,11 @@ if (negative) negate(cur_val);
@ @<Get the next non-blank non-sign token...@>=
negative=false;
-do {@<Get the next non-blank non-call token@>;
+@/do@+{@<Get the next non-blank non-call token@>;
if (cur_tok==other_token+'-')
{@+negative=!negative;cur_tok=other_token+'+';
}
-} while (!(cur_tok!=other_token+'+'));
+}@+ while (!(cur_tok!=other_token+'+'))
@ A space is ignored after an alphabetic character constant, so that
such constants behave like numeric ones.
@@ -9451,7 +9433,7 @@ int @!f; /*numerator of a fraction whose denominator is $2^{16}$*/
f=0;arith_error=false;cur_order=normal;negative=false;
cur_hfactor=cur_vfactor=0;
if (!shortcut)
- {@+@<Get the next non-blank non-sign...@>@;
+ {@+@<Get the next non-blank non-sign...@>;
if ((cur_cmd >= min_internal)&&(cur_cmd <= max_internal))
@<Fetch an internal dimension and |goto attach_sign|, or fetch an internal
integer@>@;
@@ -9690,7 +9672,7 @@ most of the work has already been done.
bool negative; /*should the answer be negated?*/
pointer @!q; /*new glue specification*/
bool @!mu; /*does |level==mu_val|?*/
-mu=(level==mu_val);@<Get the next non-blank non-sign...@>@;
+mu=(level==mu_val);@<Get the next non-blank non-sign...@>;
if ((cur_cmd >= min_internal)&&(cur_cmd <= max_internal))
{@+scan_something_internal(level, negative);
if (cur_val_level >= glue_val)
@@ -9870,7 +9852,7 @@ case convert: switch (chr_code) {
case font_name_code: print_esc("fontname");@+break;
case job_name_code: print_esc("jobname");@+break;
case eTeX_revision_code: print_esc("eTeXrevision");@+break;
- @/@t\4@>@<Cases of |convert| for |print_cmd_chr|@>@;
+ @/@<Cases of |convert| for |print_cmd_chr|@>@/
} @+break;
@ The procedure |conv_toks| uses |str_toks| to insert the token list
@@ -9901,7 +9883,7 @@ case string_code: case meaning_code: {@+save_scanner_status=scanner_status;
case font_name_code: scan_font_ident();@+break;
case job_name_code: if (job_name==0) open_log_file();@+break;
case eTeX_revision_code: do_nothing;@+break;
-@/@t\4@>@<Cases of `Scan the argument for command |c|'@>@;
+@/@<Cases of `Scan the argument for command |c|'@>@/
} /*there are no other cases*/
@ @<Print the result of command |c|@>=
@@ -9919,7 +9901,7 @@ case font_name_code: {@+printn(font_name[cur_val]);
} @+break;
case eTeX_revision_code: print(eTeX_revision);@+break;
case job_name_code: printn(job_name);@+break;
-@/@t\4@>@<Cases of `Print the result of command |c|'@>@;
+@/@<Cases of `Print the result of command |c|'@>@/
} /*there are no other cases*/
@ Now we can't postpone the difficulties any longer; we must bravely tackle
@@ -10095,13 +10077,13 @@ p=def_ref; /*the reference count*/
store_new_token(end_match_token);
if ((n < 0)||(n > 15)) m=16;@+else m=n;
s=align_state;align_state=1000000; /*disable tab marks, etc.*/
-do @<Input and store tokens from the next line of the file@>@;
-while (!(align_state==1000000));
+@/do@+{@<Input and store tokens from the next line of the file@>;
+}@+ while (!(align_state==1000000));
cur_val=def_ref;scanner_status=normal;align_state=s;
}
@ @<Input and store tokens from the next line of the file@>=
-{begin_file_reading();name=m+1;
+begin_file_reading();name=m+1;
if (read_open[m]==closed) @<Input for \.{\\read} from the terminal@>;
else if (read_open[m]==just_open) @<Input the first line of |read_file[m]|@>@;
else@<Input the next line of |read_file[m]|@>;
@@ -10114,12 +10096,12 @@ loop@+{@+get_token();
if (cur_tok==0) goto done;
/*|cur_cmd==cur_chr==0| will occur at the end of the line*/
if (align_state < 1000000) /*unmatched `\.\}' aborts the line*/
- {@+do get_token(); while (!(cur_tok==0));
+ {@+@/do@+{get_token();}@+ while (!(cur_tok==0));
align_state=1000000;goto done;
}
store_new_token(cur_tok);
}
-done: end_file_reading();}
+done: end_file_reading()
@ Here we input on-line into the |buffer| array, prompting the user explicitly
if |n >= 0|. The value of |n| is set negative so that additional prompts
@@ -10237,7 +10219,7 @@ switch (chr_code%unless_code) {
case if_true_code: print_esc("iftrue");@+break;
case if_false_code: print_esc("iffalse");@+break;
case if_case_code: print_esc("ifcase");@+break;
- @/@t\4@>@<Cases of |if_test| for |print_cmd_chr|@>@;
+ @/@<Cases of |if_test| for |print_cmd_chr|@>@/
default:print_esc("if");
}
} @+break;
@@ -10418,7 +10400,7 @@ case if_eof_code: {@+scan_four_bit_int();b=(read_open[cur_val]==closed);
} @+break;
case if_true_code: b=true;@+break;
case if_false_code: b=false;@+break;
-@/@t\4@>@<Cases for |conditional|@>@;
+@/@<Cases for |conditional|@>@/
case if_case_code: @<Select the appropriate case and |return| or |goto common_ending|@>;
} /*there are no other cases*/
@@ -10795,7 +10777,6 @@ static str_number b_make_name_string(byte_file *f)
{@+return make_name_string();
}
#ifdef @!INIT
-@+@t}\6\4\4{@>
static str_number w_make_name_string(word_file *f)
{@+return make_name_string();
}
@@ -11549,8 +11530,8 @@ that do not do local optimization.
@ Font parameters are referred to as |slant(f)|, |space(f)|, etc.
-@d param_end(A) @[param_base[A]].sc@]
-@d param(A) @[font_info[A+param_end@]
+@d param_end(A) param_base[A]].sc
+@d param(A) font_info[A+param_end
@d slant param(slant_code) /*slant to the right, per unit distance upward*/
@d space param(space_code) /*normal space between words*/
@d space_stretch param(space_stretch_code) /*stretch between words*/
@@ -11954,11 +11935,11 @@ if (cur_val==fmem_ptr)
}
@ @<Increase the number of parameters...@>=
-{@+do {if (fmem_ptr==font_mem_size)
+{@+@/do@+{if (fmem_ptr==font_mem_size)
overflow("font memory", font_mem_size);
@:TeX capacity exceeded font memory}{\quad font memory@>
font_info[fmem_ptr].sc=0;incr(fmem_ptr);incr(font_params[f]);
-} while (!(n==font_params[f]));
+}@+ while (!(n==font_params[f]));
cur_val=fmem_ptr-1; /*this equals |param_base[f]+font_params[f]|*/
}
@@ -13002,7 +12983,7 @@ entering a new level of recursion. In effect, the values of |save_h| and
|save_v| on \TeX's run-time stack correspond to the values of |h| and |v|
that a \.{DVI}-reading program will push onto its coordinate stack.
-@p @<Declare procedures needed in |hlist_out|, |vlist_out|@>@t@>@;
+@p @t\4@>@<Declare procedures needed in |hlist_out|, |vlist_out|@>@t@>@/
static void hlist_out(void) /*output an |hlist_node| box*/
{@+
scaled base_line; /*the baseline coordinate for this box*/
@@ -13044,13 +13025,13 @@ reaching a non-|char_node|. The program uses the fact that |set_char_0==0|.
@<Output node |p| for |hlist_out|...@>=
reswitch: if (is_char_node(p))
{@+synch_h;synch_v;
- do {f=font(p);c=character(p);
+ @/do@+{f=font(p);c=character(p);
if (f!=dvi_f) @<Change font |dvi_f| to |f|@>;
if (c >= qi(128)) dvi_out(set1);
dvi_out(qo(c));@/
cur_h=cur_h+char_width(f, char_info(f, c));
p=link(p);
- } while (!(!is_char_node(p)));
+ }@+ while (!(!is_char_node(p)));
dvi_h=cur_h;
}
else@<Output the non-|char_node| |p| for |hlist_out| and move to the next
@@ -13355,7 +13336,7 @@ ready for the |ship_out| routine that gets them started in the first place.
@p static void ship_out(pointer @!p) /*output the box |p|*/
{@+
execute_output(p);
-@<Flush the box from memory...@>@;
+flush_node_list(p);
}
@ @<Flush the box from memory, showing statistics if requested@>=
@@ -13921,20 +13902,21 @@ if (last_badness > vbadness)
@ When a box is being appended to the current vertical list, the
baselineskip calculation is handled by the |append_to_vlist| routine.
-@p static void append_to_vlist(pointer @!b)
-{ @+bool height_known;
+@p static void append_to_vlist(pointer @!b)@t\2\2@>@/
+{ bool height_known;@t\1@>@/
height_known=(type(b)==hlist_node || type(b)==vlist_node ||@|
(type(b)==whatsit_node && subtype(b)==hset_node));@/
if (prev_depth > ignore_depth && height_known)@/
- {@+scaled d; /*deficiency of space between baselines*/
+ {@+scaled d;@t\1@> /*deficiency of space between baselines*/
pointer @!p; /*a new glue node*/
- d=width(baseline_skip)-prev_depth-height(b);
+ {@+d=width(baseline_skip)-prev_depth-height(b);
if (d < line_skip_limit) p=new_param_glue(line_skip_code);
else{@+p=new_skip_param(baseline_skip_code);
width(temp_ptr)=d; /*|temp_ptr==glue_ptr(p)|*/
}
link(tail)=p;tail=p;
-} @+ else @+if (prev_depth<=unknown_depth || prev_depth>ignore_depth ) @/
+ }@+
+} @+ else @+if (prev_depth<=unknown_depth || prev_depth>ignore_depth )@t\2@>@/
{@+pointer p;
p=new_baseline_node(baseline_skip, line_skip, line_skip_limit);
link(tail)= p;tail= p;
@@ -14576,12 +14558,12 @@ be probed in increasing order of height.
@<Look at the variants of |(z,x)|; set |f| and |c|...@>=
if ((z!=0)||(x!=min_quarterword))
{@+z=z+s+16;
- do {z=z-16;g=fam_fnt(z);
+ @/do@+{z=z-16;g=fam_fnt(z);
if (g!=null_font)
@<Look at the list of characters starting with |x| in font |g|; set |f|
and |c| whenever a better character is found; |goto found| as soon as a large
enough variant is encountered@>;
- } while (!(z < 16));
+ }@+ while (!(z < 16));
}
@ @<Look at the list of characters starting with |x|...@>=
@@ -14971,8 +14953,8 @@ case rel_noad: case close_noad: case punct_noad:
@<Convert \(a)a final |bin_noad| to an |ord_noad|@>;
if (type(q)==right_noad) goto done_with_noad;
} @+break;
-@/@t\4@>@<Cases for noads that can follow a |bin_noad|@>@;
-@/@t\4@>@<Cases for nodes that can appear in an mlist, after which we |goto
+@t\4@>@<Cases for noads that can follow a |bin_noad|@>@;
+@t\4@>@<Cases for nodes that can appear in an mlist, after which we |goto
done_with_node|@>@;
default:confusion("mlist1");
@:this can't happen mlist1}{\quad mlist1@>
@@ -15732,7 +15714,8 @@ is not a penalty node or a |rel_noad| or absent entirely.
@<Append any |new_hlist| entries for |q|, and any appropriate penalties@>=
if (new_hlist(q)!=null)
{@+link(p)=new_hlist(q);
- do p=link(p); while (!(link(p)==null));
+ @/do@+{p=link(p);
+ }@+ while (!(link(p)==null));
}
if (penalties) if (link(q)!=null) if (pen < inf_penalty)
{@+r_type=type(link(q));
@@ -15957,7 +15940,7 @@ good start. This mostly involves scanning the preamble and putting its
information into the preamble list.
@^preamble@>
-@p @<Declare the procedure called |get_preamble_token|@>@;
+@p @t\4@>@<Declare the procedure called |get_preamble_token|@>@t@>@/
static void align_peek(void);@/
static void normal_paragraph(void);@/
static void init_align(void)
@@ -16145,7 +16128,8 @@ started, or finishes off the alignment.
static void align_peek(void)
{@+
restart: align_state=1000000;
-do get_x_or_protected(); while (!(cur_cmd!=spacer));
+@/do@+{get_x_or_protected();
+}@+ while (!(cur_cmd!=spacer));
if (cur_cmd==no_align)
{@+scan_left_brace();new_save_level(no_align_group);
if (mode==-vmode) normal_paragraph();
@@ -16164,7 +16148,7 @@ from internal vertical mode to restricted horizontal mode or vice versa.
The |space_factor| and |prev_depth| are not used on this semantic level,
but we clear them to zero just to be tidy.
-@p @<Declare the procedure called |init_span|@>@;
+@p @t\4@>@<Declare the procedure called |init_span|@>@t@>@/
static void init_row(void)
{@+push_nest();mode=(-hmode-vmode)-mode;
if (mode==-hmode) space_factor=0;@+else prev_depth=0;
@@ -16257,7 +16241,8 @@ if (extra_info(cur_align)!=span_code)
init_span(p);
}
align_state=1000000;
-do get_x_or_protected(); while (!(cur_cmd!=spacer));
+@/do@+{get_x_or_protected();
+}@+ while (!(cur_cmd!=spacer));
cur_align=p;
init_col();return false;
}
@@ -16351,8 +16336,8 @@ link(end_span)=max_quarterword+1;info(end_span)=null;
@ @<Update width entry for spanned columns@>=
{@+q=cur_span;
-do {incr(n);q=link(link(q));
-} while (!(q==cur_align));
+@/do@+{incr(n);q=link(link(q));
+}@+ while (!(q==cur_align));
if (n > max_quarterword) confusion("256 spans"); /*this can happen, but won't*/
@^system dependencies@>
@:this can't happen 256 spans}{\quad 256 spans@>
@@ -16409,9 +16394,21 @@ unsave(); /*that |align_group| was for the whole alignment*/
if (nest[nest_ptr-1].mode_field==mmode) o=display_indent;
else o=0;
@<Go through the preamble list, determining the column widths and changing
-the alignrecords to dummy unset boxes@>@;
+the alignrecords to dummy unset boxes@>;
if (x)
-{ /*Handle an alignment that depends on |hsize| or |vsize|*/
+{ @<Handle an alignment that depends on |hsize| or |vsize|@>@;
+ pop_alignment();
+}
+else
+{ @<Package the preamble list, to determine the actual tabskip glue amounts,
+ and let |p| point to this prototype box@>;
+ @<Set the glue in all the unset boxes of the current list@>;
+ flush_node_list(p);pop_alignment();
+}
+@<Insert the \(c)current list into its environment@>;
+} @/
+@t\4@>@<Declare the procedure called |align_peek|@>@;
+@ @<Handle an alignment that depends on |hsize| or |vsize|@>=
pointer r=get_node(align_node_size);
save_ptr=save_ptr-2;pack_begin_line=-mode_line;
@@ -16423,18 +16420,6 @@ if (x)
align_v(r)= (mode!=-vmode);
link(head)=r; tail=r;
pack_begin_line=0;
- pop_alignment();
-}
-else
-{ @<Package the preamble list, to determine the actual tabskip glue amounts,
- and let |p| point to this prototype box@>;
- @<Set the glue in all the unset boxes of the current list@>;
- flush_node_list(p);pop_alignment();
-}
-@<Insert the \(c)current list into its environment@>;
-}
-@#
-@t\4@>@<Declare the procedure called |align_peek|@>@;
@ It's time now to dismantle the preamble list and to compute the column
widths. Let $w_{ij}$ be the maximum of the natural widths of all entries
@@ -16461,7 +16446,7 @@ value is changed to zero and so is the next tabskip.
@<Go through the preamble list,...@>=
q=link(preamble);
-do {flush_list(u_part(q));flush_list(v_part(q));
+@/do@+{flush_list(u_part(q));flush_list(v_part(q));
p=link(link(q));
if (width(q)==null_flag)
@<Nullify |width(q)| and the tabskip glue following this column@>;
@@ -16475,7 +16460,7 @@ glue_stretch(q)=0;glue_shrink(q)=0;
if (width(q)>max_dimen) x=true;
#endif
q=p;
-} while (!(q==null));
+}@+ while (!(q==null))
@ @<Nullify |width(q)| and the tabskip glue following this column@>=
{@+width(q)=0;r=link(q);s=glue_ptr(r);
@@ -16487,7 +16472,7 @@ if (s!=zero_glue)
@ Merging of two span-node lists is a typical exercise in the manipulation of
linearly linked data structures. The essential invariant in the following
-|do {| loop is that we want to dispense with node |r|, in |q|'s list,
+|@/do@+{| loop is that we want to dispense with node |r|, in |q|'s list,
and |u| is its successor; all nodes of |p|'s list up to and including |s|
have been processed, and the successor of |s| matches |r| or precedes |r|
or follows |r|, according as |link(r)==n| or |link(r) > n| or |link(r) < n|.
@@ -16495,7 +16480,7 @@ or follows |r|, according as |link(r)==n| or |link(r) > n| or |link(r) < n|.
@<Merge the widths...@>=
{@+t=width(q)+width(glue_ptr(link(q)));
r=info(q);s=end_span;info(s)=p;n=min_quarterword+1;
-do {width(r)=width(r)-t;u=info(r);
+@/do@+{width(r)=width(r)-t;u=info(r);
while (link(r) > n)
{@+s=info(s);n=link(info(s))+1;
}
@@ -16506,7 +16491,7 @@ else{@+if (width(r) > width(info(s))) width(info(s))=width(r);
free_node(r, span_node_size);
}
r=u;
-} while (!(r==end_span));
+}@+ while (!(r==end_span));
}
@ Now the preamble list has been converted to a list of alternating unset
@@ -16523,12 +16508,12 @@ if (mode==-vmode)
p=hpack(preamble, saved(1), saved_hfactor(1), saved_vfactor(1), saved(0));overfull_rule=rule_save;
}
else{@+q=link(preamble);
- do {height(q)=width(q);width(q)=0;q=link(link(q));
- } while (!(q==null));
+ @/do@+{height(q)=width(q);width(q)=0;q=link(link(q));
+ }@+ while (!(q==null));
p=vpack(preamble, saved(1), saved_hfactor(1), saved_vfactor(1), saved(0));
q=link(preamble);
- do {width(q)=height(q);height(q)=0;q=link(link(q));
- } while (!(q==null));
+ @/do@+{width(q)=height(q);height(q)=0;q=link(link(q));
+ }@+ while (!(q==null));
}
pack_begin_line=0
@@ -16566,9 +16551,9 @@ else{@+type(q)=vlist_node;height(q)=height(p);
glue_order(q)=glue_order(p);glue_sign(q)=glue_sign(p);
glue_set(q)=glue_set(p);shift_amount(q)=o;
r=link(list_ptr(q));s=link(list_ptr(p));
-do {@<Set the glue in node |r| and change it from an unset node@>;
+@/do@+{@<Set the glue in node |r| and change it from an unset node@>;
r=link(link(r));s=link(link(s));
-} while (!(r==null));
+}@+ while (!(r==null));
}
@ A box made from spanned columns will be followed by tabskip glue nodes and
@@ -16900,7 +16885,7 @@ k=1 to 6 do cur_active_width[k]=cur_active_width[k]+mem[q+k].sc|};$$ and we
want to do this without the overhead of |for| loops. The |do_all_six|
macro makes such six-tuples convenient.
-@d do_all_six(A) A(1);A(2);A(3);A(4);A(5);A(6)@;
+@d do_all_six(A) A(1);A(2);A(3);A(4);A(5);A(6)
@<Glob...@>=
static scaled @!active_width0[6], *const @!active_width = @!active_width0-1;
@@ -17058,7 +17043,7 @@ loop@+{@+resume: r=link(prev_r);
@<If node |r| is of type |delta_node|, update |cur_active_width|, set |prev_r|
and |prev_prev_r|, then |goto resume|@>;
@<If a line number class has ended, create new active nodes for the best
-feasible breaks in that class; then |return| if |r==last_active|, otherwise
+feasible breaks in that class; then |return| if |r=last_active|, otherwise
compute the new |line_width|@>;
@<Consider the demerits for a line from |r| to |cur_p|; deactivate node
|r| if it should no longer be active; then |goto resume| if a line from
@@ -17811,10 +17796,10 @@ character node.
@<Advance \(c)|cur_p| to the node following the present string...@>=
{@+prev_p=cur_p;
-do {f=font(cur_p);
+@/do@+{f=font(cur_p);
act_width=act_width+char_width(f, char_info(f, character(cur_p)));
cur_p=link(cur_p);
-} while (!(!is_char_node(cur_p)));
+}@+ while (!(!is_char_node(cur_p)));
}
@ When node |cur_p| is a glue node, we look at |prev_p| to see whether or not
@@ -17839,9 +17824,9 @@ only character nodes, kern nodes, box nodes, rule nodes, and ligature nodes.
@<Try to break after a discretionary fragment...@>=
{@+s=pre_break(cur_p);disc_width=0;
if (s==null) try_break(ex_hyphen_penalty, hyphenated);
-else{@+do {@<Add the width of node |s| to |disc_width|@>;
+else{@+@/do@+{@<Add the width of node |s| to |disc_width|@>;
s=link(s);
- } while (!(s==null));
+ }@+ while (!(s==null));
act_width=act_width+disc_width;
try_break(hyphen_penalty, hyphenated);
act_width=act_width-disc_width;
@@ -17919,11 +17904,11 @@ if (link(active)!=last_active)
@ @<Find an active node...@>=
r=link(active);fewest_demerits=awful_bad;
-do {if (type(r)!=delta_node) if (total_demerits(r) < fewest_demerits)
+@/do@+{if (type(r)!=delta_node) if (total_demerits(r) < fewest_demerits)
{@+fewest_demerits=total_demerits(r);best_bet=r;
}
r=link(r);
-} while (!(r==last_active));
+}@+ while (!(r==last_active));
best_line=line_number(best_bet)
@ The adjustment for a desired looseness is a slightly more complicated
@@ -17933,7 +17918,7 @@ looseness calculation, independently of the other segments.
@<Find the best active node...@>=
{@+r=link(active);actual_looseness=0;
-do {if (type(r)!=delta_node)
+@/do@+{if (type(r)!=delta_node)
{@+line_diff=line_number(r)-best_line;
if (((line_diff < actual_looseness)&&(looseness <= line_diff))||@|
((line_diff > actual_looseness)&&(looseness >= line_diff)))
@@ -17946,7 +17931,7 @@ do {if (type(r)!=delta_node)
}
}
r=link(r);
-} while (!(r==last_active));
+}@+ while (!(r==last_active));
best_line=line_number(best_bet);
}
@@ -17981,14 +17966,14 @@ quarterword @!t; /*used for replacement counts in discretionary nodes*/
int @!pen; /*use when calculating penalties between lines*/
halfword @!cur_line; /*the current line number being justified*/
@<Reverse the links of the relevant passive nodes, setting |cur_p| to the
-first breakpoint@>@;
+first breakpoint@>;
cur_line=prev_graf+1;
-do {@<Justify the line ending at breakpoint |cur_p|, and append it to the
+@/do@+{@<Justify the line ending at breakpoint |cur_p|, and append it to the
current vertical list, together with associated penalties and other insertions@>;
incr(cur_line);cur_p=next_break(cur_p);
if (cur_p!=null) if (!post_disc_break)
@<Prune unwanted nodes at the beginning of the next line@>;
-} while (!(cur_p==null));
+}@+ while (!(cur_p==null));
if ((cur_line!=best_line)||(link(temp_head)!=null))
confusion("line breaking");
@:this can't happen line breaking}{\quad line breaking@>
@@ -18003,8 +17988,8 @@ Node |r| is the passive node being moved from stack to stack.
@<Reverse the links of the relevant passive nodes...@>=
q=break_node(best_bet);cur_p=null;
-do {r=q;q=prev_break(q);next_break(r)=cur_p;cur_p=r;
-} while (!(q==null));
+@/do@+{r=q;q=prev_break(q);next_break(r)=cur_p;cur_p=r;
+}@+ while (!(q==null))
@ Glue and penalty and kern and math nodes are deleted at the beginning of
a line, except in the anomalous case that the node to be deleted is actually
@@ -18257,8 +18242,8 @@ int @!c; /*character being considered for hyphenation*/
second pass, and |cur_p| points to a glue node.
@p
-static void hyphenate_word(void)
-{ @+pointer @!q, @!s, @!prev_s; /*miscellaneous nodes of temporary interest*/
+static void hyphenate_word(void)@t\2\2@>@/
+{ pointer @!q, @!s, @!prev_s;@t\1@> /*miscellaneous nodes of temporary interest*/
small_number @!j; /*an index into |hc| or |hu|*/
uint8_t @!c; /*character being considered for hyphenation*/
@@ -18692,7 +18677,7 @@ pointer @!hyf_node; /*the hyphen, if it exists*/
@ When the following code is performed, |hyf[0]| and |hyf[hn]| will be zero.
@<Reconstitute nodes for the hyphenated word...@>=
-do {l=j;j=reconstitute(j, hn, bchar, qi(hyf_char))+1;
+@/do@+{l=j;j=reconstitute(j, hn, bchar, qi(hyf_char))+1;
if (hyphen_passed==0)
{@+link(s)=link(hold_head);
while (link(s) > null) s=link(s);
@@ -18702,8 +18687,8 @@ if (hyphen_passed==0)
}
if (hyphen_passed > 0)
@<Create and append a discretionary node as an alternative to the unhyphenated
-word, and continue to develop both branches until they become equivalent@>@;
-} while (!(j > hn));
+word, and continue to develop both branches until they become equivalent@>;
+}@+ while (!(j > hn));
link(s)=q
@ In this repeat loop we will insert another discretionary if |hyf[j-1]| is
@@ -18717,7 +18702,7 @@ point. (Consider the word `difficult', where the letter `c' is in position |j|.)
}
@<Create and append a discretionary node as an alternative...@>=
-do {r=get_node(small_node_size);
+@/do@+{r=get_node(small_node_size);
link(r)=link(hold_head);type(r)=disc_node;
major_tail=r;r_count=0;
while (link(major_tail) > null) advance_major_tail;
@@ -18728,7 +18713,7 @@ list and to |major_tail| until synchronization has been achieved@>;
@<Move pointer |s| to the end of the current list, and set |replace_count(r)|
appropriately@>;
hyphen_passed=j-1;link(hold_head)=null;
-} while (!(!odd(hyf[j-1])));
+}@+ while (!(!odd(hyf[j-1])))
@ The new hyphen might combine with the previous character via ligature
or kern. At this point we have |l-1 <= i < j| and |i < hn|.
@@ -18760,7 +18745,7 @@ if (bchar_label[hf]!=non_address) /*put left boundary at beginning of new line*
{@+decr(l);c=hu[l];c_loc=l;hu[l]=256;
}
while (l < j)
- {@+do {l=reconstitute(l, hn, bchar, non_char)+1;
+ {@+@/do@+{l=reconstitute(l, hn, bchar, non_char)+1;
if (c_loc > 0)
{@+hu[c_loc]=c;c_loc=0;
}
@@ -18770,7 +18755,7 @@ while (l < j)
minor_tail=link(hold_head);
while (link(minor_tail) > null) minor_tail=link(minor_tail);
}
- } while (!(l >= j));
+ }@+ while (!(l >= j));
while (l > j)
@<Append characters of |hu[j..]| to |major_tail|, advancing~|j|@>;
}
@@ -18888,10 +18873,10 @@ for (j=0; j<=r_hyf-1; j++) hyf[hn-j]=0
@ @<Store \(m)maximum values in the |hyf| table@>=
{@+v=trie_op(z);
-do {v=v+op_start[cur_lang];i=l-hyf_distance[v];
+@/do@+{v=v+op_start[cur_lang];i=l-hyf_distance[v];
if (hyf_num[v] > hyf[i]) hyf[i]=hyf_num[v];
v=hyf_next[v];
-} while (!(v==min_quarterword));
+}@+ while (!(v==min_quarterword));
}
@ The exception table that is built by \TeX's \.{\\hyphenation} primitive is
@@ -18956,10 +18941,10 @@ k=hyph_word[h];if (k==0) goto not_found;
if (length(k) < hn) goto not_found;
if (length(k)==hn)
{@+j=1;u=str_start[k];
- do {if (so(str_pool[u]) < hc[j]) goto not_found;
+ @/do@+{if (so(str_pool[u]) < hc[j]) goto not_found;
if (so(str_pool[u]) > hc[j]) goto done;
incr(j);incr(u);
- } while (!(j > hn));
+ }@+ while (!(j > hn));
@<Insert hyphens as specified in |hyph_list[h]|@>;
decr(hn);goto found;
}
@@ -19085,10 +19070,10 @@ k=hyph_word[h];
if (length(k) < length(s)) goto found;
if (length(k) > length(s)) goto not_found;
u=str_start[k];v=str_start[s];
-do {if (str_pool[u] < str_pool[v]) goto found;
+@/do@+{if (str_pool[u] < str_pool[v]) goto found;
if (str_pool[u] > str_pool[v]) goto not_found;
incr(u);incr(v);
-} while (!(u==str_start[k+1]));
+}@+ while (!(u==str_start[k+1]));
found: q=hyph_list[h];hyph_list[h]=p;p=q;@/
t=hyph_word[h];hyph_word[h]=s;s=t;
not_found:
@@ -19376,7 +19361,7 @@ loop@+{@+h=z-c;@/
otherwise |goto not_found|@>;
not_found: z=trie_link(z); /*move to the next hole*/
}
-found: @<Pack the family into |trie| relative to |h|@>@;
+found: @<Pack the family into |trie| relative to |h|@>;
}
@ By making sure that |trie_max| is at least |h+256|, we can be sure that
@@ -19387,9 +19372,9 @@ never be occupied in |trie|, and we will have |trie_max >= trie_link(z)|.
if (trie_max < h+256)
{@+if (trie_size <= h+256) overflow("pattern memory", trie_size);
@:TeX capacity exceeded pattern memory}{\quad pattern memory@>
- do {incr(trie_max);trie_taken[trie_max]=false;
+ @/do@+{incr(trie_max);trie_taken[trie_max]=false;
trie_link(trie_max)=trie_max+1;trie_back(trie_max)=trie_max-1;
- } while (!(trie_max==h+256));
+ }@+ while (!(trie_max==h+256));
}
@ @<If all characters of the family fit relative to |h|...@>=
@@ -19402,15 +19387,15 @@ goto found
@ @<Pack the family into |trie| relative to |h|@>=
trie_taken[h]=true;trie_ref[p]=h;q=p;
-do {z=h+so(trie_c[q]);l=trie_back(z);r=trie_link(z);
+@/do@+{z=h+so(trie_c[q]);l=trie_back(z);r=trie_link(z);
trie_back(r)=l;trie_link(l)=r;trie_link(z)=0;
if (l < 256)
{@+if (z < 256) ll=z;@+else ll=256;
- do {trie_min[l]=r;incr(l);
- } while (!(l==ll));
+ @/do@+{trie_min[l]=r;incr(l);
+ }@+ while (!(l==ll));
}
q=trie_r[q];
-} while (!(q==0));
+}@+ while (!(q==0))
@ To pack the entire linked trie, we use the following recursive procedure.
@^recursion@>
@@ -19418,12 +19403,12 @@ q=trie_r[q];
@<Declare procedures for preprocessing hyph...@>=
static void trie_pack(trie_pointer @!p) /*pack subtries of a family*/
{@+trie_pointer q; /*a local variable that need not be saved on recursive calls*/
-do {q=trie_l[p];
+@/do@+{q=trie_l[p];
if ((q > 0)&&(trie_ref[q]==0))
{@+first_fit(q);trie_pack(q);
}
p=trie_r[p];
-} while (!(p==0));
+}@+ while (!(p==0));
}
@ When the whole trie has been allocated into the sequential table, we
@@ -19441,8 +19426,8 @@ if (trie_max==0) /*no patterns were given*/
else{@+if (hyph_root > 0) trie_fix(hyph_root);
if (trie_root > 0) trie_fix(trie_root); /*this fixes the non-holes in |trie|*/
r=0; /*now we will zero out all the holes*/
- do {s=trie_link(r);trie[r]=h;r=s;
- } while (!(r > trie_max));
+ @/do@+{s=trie_link(r);trie[r]=h;r=s;
+ }@+ while (!(r > trie_max));
}
trie_char(0)=qi('?'); /*make |trie_char(c)!=c| for all |c|*/
@@ -19458,11 +19443,11 @@ static void trie_fix(trie_pointer @!p) /*moves |p| and its siblings into |trie|*
ASCII_code @!c; /*another one that need not be saved*/
trie_pointer @!z; /*|trie| reference; this local variable must be saved*/
z=trie_ref[p];
-do {q=trie_l[p];c=so(trie_c[p]);
+@/do@+{q=trie_l[p];c=so(trie_c[p]);
trie_link(z+c)=trie_ref[q];trie_char(z+c)=qi(c);trie_op(z+c)=trie_o[p];
if (q > 0) trie_fix(q);
p=trie_r[p];
-} while (!(p==0));
+}@+ while (!(p==0));
}
@ Now let's go back to the easier problem, of building the linked
@@ -20061,9 +20046,9 @@ if (page_head!=page_tail)
print_scaled(t);
if (type(r)==split_up)
{@+q=page_head;t=0;
- do {q=link(q);
+ @/do@+{q=link(q);
if ((type(q)==ins_node)&&(subtype(q)==subtype(r))) incr(t);
- } while (!(q==broken_ins(r)));
+ }@+ while (!(q==broken_ins(r)));
print(", #");print_int(t);print(" might split");
}
r=link(r);
@@ -20723,7 +20708,8 @@ pop_nest();build_page();
@.Unbalanced output routine@>
help2("Your sneaky output routine has problematic {'s and/or }'s.",@/
"I can't handle that very well; good luck.");error();
-do get_token(); while (!(loc==null));
+@/do@+{get_token();
+}@+ while (!(loc==null));
} /*loops forever if reading from a file, since |null==min_halfword <= 0|*/
@ @<Ensure that box 255 is empty after output@>=
@@ -20807,7 +20793,7 @@ case hmode+no_boundary: {@+get_x_token();
case hmode+spacer: if (space_factor==1000) goto append_normal_space;
else app_space();@+break;
case hmode+ex_space: case mmode+ex_space: goto append_normal_space;
-@/@t\4@>@<Cases of |main_control| that are not part of the inner loop@>@;
+@t\4@>@<Cases of |main_control| that are not part of the inner loop@>@;
} /*of the big |case| statement*/
goto big_switch;
main_loop: @<Append character |cur_chr| and the following characters (if~any)
@@ -21120,7 +21106,7 @@ if ((space_factor >= 2000)&&(xspace_skip!=zero_glue))
else{@+if (space_skip!=zero_glue) main_p=space_skip;
else@<Find the glue specification...@>;
main_p=new_spec(main_p);
- @<Modify the glue specification in |main_p| according to the space factor@>@;
+ @<Modify the glue specification in |main_p| according to the space factor@>;
q=new_glue(main_p);glue_ref_count(main_p)=null;
}
link(tail)=q;tail=q;
@@ -21129,7 +21115,7 @@ link(tail)=q;tail=q;
@ @<Modify the glue specification in |main_p| according to the space factor@>=
if (space_factor >= 2000) width(main_p)=width(main_p)+extra_space(cur_font);
stretch(main_p)=xn_over_d(stretch(main_p), space_factor, 1000);
-shrink(main_p)=xn_over_d(shrink(main_p), 1000, space_factor);
+shrink(main_p)=xn_over_d(shrink(main_p), 1000, space_factor)
@ Whew---that covers the main loop. We can now proceed at a leisurely
pace through the other combinations of possibilities.
@@ -21144,7 +21130,7 @@ any_mode(ignore_spaces): {@+@<Get the next non-blank non-call...@>;
goto reswitch;
}
case vmode+stop: if (its_all_over()) return;@+break; /*this is the only way out*/
-@<Forbidden cases detected in |main_control|@>@+@, any_mode(mac_param):
+@t\4@>@<Forbidden cases detected in |main_control|@>@+@, any_mode(mac_param):
report_illegal_case();@+break;
@<Math-only cases in non-math modes, or vice versa@>: insert_dollar_sign();@+break;
@t\4@>@<Cases of |main_control| that build boxes and lists@>@;
@@ -21497,7 +21483,7 @@ case bottom_level: {@+print_err("Too many }'s");
} @+break;
case semi_simple_group: case math_shift_group:
case math_left_group: extra_right_brace();@+break;
-@/@t\4@>@<Cases of |handle_right_brace| where a |right_brace| triggers a delayed
+@t\4@>@<Cases of |handle_right_brace| where a |right_brace| triggers a delayed
action@>@;
default:confusion("rightbrace");
@:this can't happen rightbrace}{\quad rightbrace@>
@@ -21693,7 +21679,7 @@ current vertical list.
}
else{@+cur_val=box_context-global_box_flag;a=4;
}
-if (cur_val < 256) @[g_define(box_base+cur_val, box_ref, cur_box)@];
+if (cur_val < 256) define(box_base+cur_val, box_ref, cur_box);
else sa_def_box;
}
@@ -21761,13 +21747,13 @@ else{@+if (!is_char_node(tail))
@ @<Remove the last box...@>=
{@+q=head;
-do {p=q;
+@/do@+{p=q;
if (!is_char_node(q)) if (type(q)==disc_node)
{@+for (m=1; m<=replace_count(q); m++) p=link(p);
if (p==tail) goto done;
}
q=link(p);
-} while (!(q==tail));
+}@+ while (!(q==tail));
cur_box=tail;shift_amount(cur_box)=0;
tail=p;link(p)=null;
done: ;}
@@ -22078,13 +22064,13 @@ if ((mode==vmode)&&(tail==head))
non-glue@>@;
else{@+if (!is_char_node(tail)) if (type(tail)==cur_chr)
{@+q=head;
- do {p=q;
+ @/do@+{p=q;
if (!is_char_node(q)) if (type(q)==disc_node)
{@+for (m=1; m<=replace_count(q); m++) p=link(p);
if (p==tail) return;
}
q=link(p);
- } while (!(q==tail));
+ }@+ while (!(q==tail));
link(p)=null;flush_node_list(tail);tail=p;
}
}
@@ -22125,8 +22111,8 @@ case remove_item: if (chr_code==glue_node) print_esc("unskip");
else print_esc("unpenalty");@+break;
case un_hbox: if (chr_code==copy_code) print_esc("unhcopy");
else print_esc("unhbox");@+break;
-case un_vbox: if (chr_code==copy_code) print_esc("unvcopy");
- else @<Cases of |un_vbox| for |print_cmd_chr|@>@;
+case un_vbox: if (chr_code==copy_code) print_esc("unvcopy")
+ @<Cases of |un_vbox| for |print_cmd_chr|@>;@/
else print_esc("unvbox");@+break;
@ The |un_hbox| and |un_vbox| commands unwrap one of the 256 current boxes.
@@ -23167,8 +23153,8 @@ primitive("right", left_right, right_noad);
text(frozen_right)=text(cur_val);eqtb[frozen_right]=eqtb[cur_val];
@ @<Cases of |print_cmd_chr|...@>=
-case left_right: if (chr_code==left_noad) print_esc("left");
-else @<Cases of |left_right| for |print_cmd_chr|@>@;
+case left_right: if (chr_code==left_noad) print_esc("left")
+@/@<Cases of |left_right| for |print_cmd_chr|@>;@/
else print_esc("right");@+break;
@ @<Cases of |main_control| that build...@>=
@@ -23505,8 +23491,8 @@ primitive("xdef", def, 3);
@ @<Cases of |print_cmd_chr|...@>=
case prefix: if (chr_code==1) print_esc("long");
- else if (chr_code==2) print_esc("outer");
- else @<Cases of |prefix| for |print_cmd_chr|@>@;
+ else if (chr_code==2) print_esc("outer")
+ @/@<Cases of |prefix| for |print_cmd_chr|@>;@/
else print_esc("global");@+break;
case def: if (chr_code==0) print_esc("def");
else if (chr_code==1) print_esc("gdef");
@@ -23616,7 +23602,7 @@ if ((cur_cmd!=def)&&((a%4!=0)||(j!=0)))
since the following routines test for the \.{\\global} prefix as follows.
@d global (a >= 4)
-@d g_define(A, B, C) if (global) geq_define(A, B, C);@+else eq_define(A, B, C)
+@d define(A, B, C) if (global) geq_define(A, B, C);@+else eq_define(A, B, C)
@d word_define(A, B) if (global) geq_word_define(A, B);@+else eq_word_define(A, B)
@<Adjust \(f)for the setting of \.{\\globaldefs}@>=
@@ -23634,7 +23620,8 @@ control sequence for a token that is not redefinable.
@<Declare subprocedures for |prefixed_command|@>=
static void get_r_token(void)
{@+
-restart: do get_token(); while (!(cur_tok!=space_token));
+restart: @/do@+{get_token();
+}@+ while (!(cur_tok!=space_token));
if ((cur_cs==0)||(cur_cs > frozen_control_sequence))
{@+print_err("Missing control sequence inserted");
@.Missing control...@>
@@ -23657,7 +23644,7 @@ text(frozen_protection)=s_no("inaccessible");
(Unfortunately, they aren't all as simple as this.)
@<Assignments@>=
-case set_font: g_define(cur_font_loc, data, cur_chr);@+break;
+case set_font: define(cur_font_loc, data, cur_chr);@+break;
@ When a |def| command has been scanned,
|cur_chr| is odd if the definition is supposed to be global, and
@@ -23671,7 +23658,7 @@ case def: {@+if (odd(cur_chr)&&!global&&(global_defs >= 0)) a=a+4;
{@+q=get_avail();info(q)=j;link(q)=link(def_ref);
link(def_ref)=q;
}
- g_define(p, call+(a%4), def_ref);
+ define(p, call+(a%4), def_ref);
} @+break;
@ Both \.{\\let} and \.{\\futurelet} share the command code |let|.
@@ -23689,7 +23676,8 @@ case let: if (chr_code!=normal) print_esc("futurelet");@+else print_esc("let");@
case let: {@+n=cur_chr;
get_r_token();p=cur_cs;
if (n==normal)
- {@+do get_token(); while (!(cur_cmd!=spacer));
+ {@+@/do@+{get_token();
+ }@+ while (!(cur_cmd!=spacer));
if (cur_tok==other_token+'=')
{@+get_token();
if (cur_cmd==spacer) get_token();
@@ -23702,7 +23690,7 @@ case let: {@+n=cur_chr;
else if ((cur_cmd==internal_register)||(cur_cmd==toks_register))
if ((cur_chr < mem_bot)||(cur_chr > lo_mem_stat_max))
add_sa_ref(cur_chr);
- g_define(p, cur_cmd, cur_chr);
+ define(p, cur_cmd, cur_chr);
} @+break;
@ A \.{\\chardef} creates a control sequence whose |cmd| is |char_given|;
@@ -23757,12 +23745,12 @@ producing an ``undefined control sequence'' error or expanding the
previous meaning. This allows, for instance, `\.{\\chardef\\foo=123\\foo}'.
@<Assignments@>=
-case shorthand_def: {@+n=cur_chr;get_r_token();p=cur_cs;g_define(p, relax, 256);
+case shorthand_def: {@+n=cur_chr;get_r_token();p=cur_cs;define(p, relax, 256);
scan_optional_equals();
switch (n) {
- case char_def_code: {@+scan_char_num();g_define(p, char_given, cur_val);
+ case char_def_code: {@+scan_char_num();define(p, char_given, cur_val);
} @+break;
- case math_char_def_code: {@+scan_fifteen_bit_int();g_define(p, math_given, cur_val);
+ case math_char_def_code: {@+scan_fifteen_bit_int();define(p, math_given, cur_val);
} @+break;
default:{@+scan_register_num();
if (cur_val > 255)
@@ -23770,15 +23758,15 @@ case shorthand_def: {@+n=cur_chr;get_r_token();p=cur_cs;g_define(p, relax, 256);
if (j > mu_val) j=tok_val; /*|int_val dotdot mu_val| or |tok_val|*/
find_sa_element(j, cur_val, true);add_sa_ref(cur_ptr);
if (j==tok_val) j=toks_register;@+else j=internal_register;
- g_define(p, j, cur_ptr);
+ define(p, j, cur_ptr);
}
else
switch (n) {
- case count_def_code: g_define(p, assign_int, count_base+cur_val);@+break;
- case dimen_def_code: g_define(p, assign_dimen, scaled_base+cur_val);@+break;
- case skip_def_code: g_define(p, assign_glue, skip_base+cur_val);@+break;
- case mu_skip_def_code: g_define(p, assign_mu_glue, mu_skip_base+cur_val);@+break;
- case toks_def_code: g_define(p, assign_toks, toks_base+cur_val);
+ case count_def_code: define(p, assign_int, count_base+cur_val);@+break;
+ case dimen_def_code: define(p, assign_dimen, scaled_base+cur_val);@+break;
+ case skip_def_code: define(p, assign_glue, skip_base+cur_val);@+break;
+ case mu_skip_def_code: define(p, assign_mu_glue, mu_skip_base+cur_val);@+break;
+ case toks_def_code: define(p, assign_toks, toks_base+cur_val);
} /*there are no other cases*/
}
}
@@ -23794,7 +23782,7 @@ case read_to_cs: {@+j=cur_chr;scan_int();n=cur_val;
"I'm going to look for the \\cs now.");error();
}
get_r_token();
- p=cur_cs;read_toks(n, p, j);g_define(p, call, cur_val);
+ p=cur_cs;read_toks(n, p, j);define(p, call, cur_val);
} @+break;
@ The token-list parameters, \.{\\output} and \.{\\everypar}, etc., receive
@@ -23864,7 +23852,7 @@ case assign_dimen: {@+p=cur_chr;scan_optional_equals();
case assign_glue: case assign_mu_glue: {@+p=cur_chr;n=cur_cmd;scan_optional_equals();
if (n==assign_mu_glue) scan_glue(mu_val);@+else scan_glue(glue_val);
trap_zero_glue();
- g_define(p, glue_ref, cur_val);
+ define(p, glue_ref, cur_val);
} @+break;
@ When a glue register or parameter becomes zero, it will always point to
@@ -23927,8 +23915,8 @@ case def_code: {@+@<Let |n| be the largest legal code value, based on |cur_chr|@
help1("I'm going to use 0 instead of that illegal code value.");@/
error();cur_val=0;
}
- if (p < math_code_base) g_define(p, data, cur_val);
- else if (p < del_code_base) g_define(p, data, hi(cur_val));
+ if (p < math_code_base) define(p, data, cur_val);
+ else if (p < del_code_base) define(p, data, hi(cur_val));
else word_define(p, cur_val);
} @+break;
@@ -23941,7 +23929,7 @@ else n=255
@ @<Assignments@>=
case def_family: {@+p=cur_chr;scan_four_bit_int();p=p+cur_val;
- scan_optional_equals();scan_font_ident();g_define(p, data, cur_val);
+ scan_optional_equals();scan_font_ident();define(p, data, cur_val);
} @+break;
@ Next we consider changes to \TeX's numeric registers.
@@ -24149,8 +24137,8 @@ static void alter_integer(void)
{@+small_number c;
/*0 for \.{\\deadcycles}, 1 for \.{\\insertpenalties}, etc.*/
c=cur_chr;scan_optional_equals();scan_int();
-if (c==0) dead_cycles=cur_val;
-else @<Cases for |alter_integer|@>@;
+if (c==0) dead_cycles=cur_val
+@/@<Cases for |alter_integer|@>@;@/
else insert_penalties=cur_val;
}
@@ -24188,7 +24176,7 @@ case set_shape: {@+q=cur_chr;scan_optional_equals();scan_int();n=cur_val;
}
cur_hfactor=fh; cur_vfactor=fv;
}
- g_define(q, shape_ref, p);
+ define(q, shape_ref, p);
} @+break;
@ Here's something that isn't quite so obvious. It guarantees that
@@ -24219,7 +24207,7 @@ new_patterns();goto done;@;
print_err("Patterns can be loaded only by INITEX");
@.Patterns can be...@>
help0;error();
- do get_token(); while (!(cur_cmd==right_brace)); /*flush the patterns*/
+ @/do@+{get_token();}@+ while (!(cur_cmd==right_brace)); /*flush the patterns*/
return;
}
else{@+new_hyph_exceptions();goto done;
@@ -24273,12 +24261,12 @@ else{@+old_setting=selector;selector=new_string;
@.FONTx@>
str_room(1);t=make_string();
}
-g_define(u, set_font, null_font);scan_optional_equals();scan_file_name();
+define(u, set_font, null_font);scan_optional_equals();scan_file_name();
@<Scan the font size specification@>;
@<If this font has already been loaded, set |f| to the internal font number
and |goto common_ending|@>;
f=read_font_info(u, cur_name, cur_area, s);
-common_ending: g_define(u, set_font, f);eqtb[font_id_base+f]=eqtb[u];font_id_text(f)=t;
+common_ending: define(u, set_font, f);eqtb[font_id_base+f]=eqtb[u];font_id_text(f)=t;
}
@ @<Scan the font size specification@>=
@@ -24663,7 +24651,7 @@ so that the inverse relation between them is clear.
The global variable |format_ident| is a string that is printed right
after the |banner| line when \TeX\ is ready to start. For \.{INITEX} this
string says simply `\.{ (INITEX)}'; for other versions of \TeX\ it says,
-for example, `\.{(preloaded format=plain 1982.11.19)}', showing the year,
+for example, `\.{ (preloaded format=plain 1982.11.19)}', showing the year,
month, and day that the format file was created. We have |format_ident==0|
before \TeX's tables are loaded.
@@ -24853,10 +24841,10 @@ sort_avail();var_used=0;
dump_int(lo_mem_max);dump_int(rover);
if (eTeX_ex) for (k=int_val; k<=tok_val; k++) dump_int(sa_root[k]);
p=mem_bot;q=rover;x=0;
-do {for (k=p; k<=q+1; k++) dump_wd(mem[k]);
+@/do@+{for (k=p; k<=q+1; k++) dump_wd(mem[k]);
x=x+q+2-p;var_used=var_used+q-p;
p=q+node_size(q);q=rlink(q);
-} while (!(q==rover));
+}@+ while (!(q==rover));
var_used=var_used+lo_mem_max-p;dyn_used=mem_end+1-hi_mem_min;@/
for (k=p; k<=lo_mem_max; k++) dump_wd(mem[k]);
x=x+lo_mem_max+1-p;
@@ -24878,11 +24866,11 @@ undump(lo_mem_stat_max+1, lo_mem_max, rover);
if (eTeX_ex) for (k=int_val; k<=tok_val; k++)
undump(null, lo_mem_max, sa_root[k]);
p=mem_bot;q=rover;
-do {for (k=p; k<=q+1; k++) undump_wd(mem[k]);
+@/do@+{for (k=p; k<=q+1; k++) undump_wd(mem[k]);
p=q+node_size(q);
if ((p > lo_mem_max)||((q >= rlink(q))&&(rlink(q)!=rover))) goto bad_fmt;
q=rlink(q);
-} while (!(q==rover));
+}@+ while (!(q==rover));
for (k=p; k<=lo_mem_max; k++) undump_wd(mem[k]);
if (mem_min < mem_bot-2) /*make more low memory available*/
{@+p=llink(rover);q=mem_min+1;
@@ -24897,14 +24885,14 @@ for (k=hi_mem_min; k<=mem_end; k++) undump_wd(mem[k]);
undump_int(var_used);undump_int(dyn_used)
@ @<Dump the table of equivalents@>=
-@<Dump regions 1 to 4 of |eqtb|@>@;
-@<Dump regions 5 and 6 of |eqtb|@>@;
+@<Dump regions 1 to 4 of |eqtb|@>;
+@<Dump regions 5 and 6 of |eqtb|@>;
dump_int(par_loc);dump_int(write_loc);@/
dump_int(input_loc);@/
@<Dump the hash table@>@;
@ @<Undump the table of equivalents@>=
-@<Undump regions 1 to 6 of |eqtb|@>@;
+@<Undump regions 1 to 6 of |eqtb|@>;
undump(hash_base, frozen_control_sequence, par_loc);
par_token=cs_token_flag+par_loc;@/
undump(hash_base, frozen_control_sequence, write_loc);@/
@@ -24919,7 +24907,7 @@ copies of $x_n$, namely $(x_1,\ldots,x_n,x_n,\ldots,x_n)$.
@<Dump regions 1 to 4 of |eqtb|@>=
k=active_base;
-do {j=k;
+@/do@+{j=k;
while (j < int_base-1)
{@+if ((equiv(j)==equiv(j+1))&&(eq_type(j)==eq_type(j+1))&&@|
(eq_level(j)==eq_level(j+1))) goto found1;
@@ -24937,10 +24925,10 @@ while (k < l)
{@+dump_wd(eqtb[k]);incr(k);
}
k=j+1;dump_int(k-l);
-} while (!(k==int_base));
+}@+ while (!(k==int_base))
@ @<Dump regions 5 and 6 of |eqtb|@>=
-do {j=k;
+@/do@+{j=k;
while (j < eqtb_size)
{@+if (eqtb[j].i==eqtb[j+1].i) goto found2;
incr(j);
@@ -24956,11 +24944,11 @@ while (k < l)
{@+dump_wd(eqtb[k]);incr(k);
}
k=j+1;dump_int(k-l);
-} while (!(k > eqtb_size));
+}@+ while (!(k > eqtb_size))
@ @<Undump regions 1 to 6 of |eqtb|@>=
k=active_base;
-do {undump_int(x);
+@/do@+{undump_int(x);
if ((x < 1)||(k+x > eqtb_size+1)) goto bad_fmt;
for (j=k; j<=k+x-1; j++) undump_wd(eqtb[j]);
k=k+x;
@@ -24968,7 +24956,7 @@ undump_int(x);
if ((x < 0)||(k+x > eqtb_size+1)) goto bad_fmt;
for (j=k; j<=k+x-1; j++) eqtb[j]=eqtb[k-1];
k=k+x;
-} while (!(k > eqtb_size));
+}@+ while (!(k > eqtb_size))
@ A different scheme is used to compress the hash table, since its lower
region is usually sparse. When |text(p)!=0| for |p <= hash_used|, we output
@@ -24986,8 +24974,8 @@ print_ln();print_int(cs_count);print(" multiletter control sequences")
@ @<Undump the hash table@>=
undump(hash_base, frozen_control_sequence, hash_used);p=hash_base-1;
-do {undump(p+1, hash_used, p);undump_hh(hash[p]);
-} while (!(p==hash_used));
+@/do@+{undump(p+1, hash_used, p);undump_hh(hash[p]);
+}@+ while (!(p==hash_used));
for (p=hash_used+1; p<=undefined_control_sequence-1; p++) undump_hh(hash[p]);
undump_int(cs_count)
@@ -25800,7 +25788,7 @@ case extension: switch (chr_code) {
case ignore_node: print("[HINT internal: ignore]");@+break;
case immediate_code: print_esc("immediate");@+break;
case set_language_code: print_esc("setlanguage");@+break;
- @/@t\4@>@<Cases of |extension| for |print_cmd_chr|@>@;
+ @/@<Cases of |extension| for |print_cmd_chr|@>@/
default:print("[unknown extension!]");
} @+break;
@@ -25991,7 +25979,7 @@ case xdimen_node:
case ignore_node: @+break;
case immediate_code: @<Implement \.{\\immediate}@>@;@+break;
case set_language_code: @<Implement \.{\\setlanguage}@>@;@+break;
-@/@t\4@>@<Cases for |do_extension|@>@;
+@/@<Cases for |do_extension|@>@/
default:confusion("ext1");
@:this can't happen ext1}{\quad ext1@>
}
@@ -26122,7 +26110,7 @@ case language_node: {@+print_esc("setlanguage");
print_int(what_lhm(p));print_char(',');
print_int(what_rhm(p));print_char(')');
} @+break;
-@/@t\4@>@<Cases for displaying the |whatsit| node@>@;
+@/@<Cases for displaying the |whatsit| node@>@/
case param_node: print_esc("parameter ");
print_int(param_type(p));print_char(',');print_int(param_no(p));
print_char(':');print_int(param_value(p).i);
@@ -26263,7 +26251,7 @@ case write_node: case special_node: case latespecial_node: {@+r=get_node(write_n
case close_node: case language_node: {@+r=get_node(small_node_size);
words=small_node_size;
} @+break;
-@/@t\4@>@<Cases for making a partial copy of the whatsit node@>@;
+@/@<Cases for making a partial copy of the whatsit node@>@/
case param_node:
{@+r=get_node(param_node_size);
if (param_type(p)==glue_type) add_glue_ref(param_value(p).i);
@@ -26446,7 +26434,7 @@ case stream_node:
free_node(p,stream_node_size); @+break;
case xdimen_node:
free_node(p,xdimen_node_size);
-@/@t\4@>@<Cases for wiping out the whatsit node@>@;
+@/@<Cases for wiping out the whatsit node@>@/
default:confusion("ext3");
@:this can't happen ext3}{\quad ext3@>
} @/
@@ -26459,7 +26447,7 @@ goto done;
@ @<Let |d| be the width of the whatsit |p|@>=d=0
-@ @d adv_past(A) if (subtype(A)==language_node)
+@ @d adv_past(A) @+if (subtype(A)==language_node)
{@+cur_lang=what_lang(A);l_hyf=what_lhm(A);r_hyf=what_rhm(A);
set_hyph_index;
}
@@ -26558,7 +26546,8 @@ end_token_list() /*conserve stack space*/
@.Unbalanced write...@>
help2("On this page there's a \\write with fewer real {'s than }'s.",@/
"I can't handle that very well; good luck.");error();
-do get_token(); while (!(cur_tok==end_write_token));
+@/do@+{get_token();
+}@+ while (!(cur_tok==end_write_token));
}
@ The |out_what| procedure takes care of outputting whatsit nodes for
@@ -27029,9 +27018,9 @@ a=1;
print_nl("");print_ln();
loop@+{@+print_nl("### ");print_group(true);
if (cur_group==bottom_level) goto done;
- do {m=nest[p].mode_field;
+ @/do@+{m=nest[p].mode_field;
if (p > 0) decr(p);else m=vmode;
- } while (!(m!=hmode));
+ }@+ while (!(m!=hmode));
print(" (");
switch (cur_group) {
case simple_group: {@+incr(p);goto found2;
@@ -27156,7 +27145,7 @@ scanner_status=s;warning_index=w;def_ref=d;
@ The \.{\\showtokens} command displays a token list.
-@d show_tokens 5 /* \.{\\showtokens}, must be odd! */
+@d show_tokens 5 /* \.{\\showtokens} , must be odd! */
@<Generate all \eTeX...@>=
primitive("showtokens", xray, show_tokens);
@@ -27179,9 +27168,9 @@ primitive("unexpanded", the, 1);@/
primitive("detokenize", the, show_tokens);@/
@!@:detokenize\_}{\.{\\detokenize} primitive@>
-@ @<Cases of |the| for |print_cmd_chr|@>=
-if (chr_code==1) print_esc("unexpanded");
-else print_esc("detokenize");
+@ @<Cases of |the| for |print_cmd_chr|@>=;
+else if (chr_code==1) print_esc("unexpanded");
+else print_esc("detokenize")
@ @<Handle \.{\\unexpanded} or \.{\\detokenize} and |return|@>=
if (odd(cur_chr))
@@ -27216,14 +27205,14 @@ case show_ifs: {@+begin_diagnostic();print_nl("");print_ln();
{@+print_nl("### ");print("no active conditionals");
}
else{@+p=cond_ptr;n=0;
- do {incr(n);p=link(p);@+} while (!(p==null));
+ @/do@+{incr(n);p=link(p);@+}@+ while (!(p==null));
p=cond_ptr;t=cur_if;l=if_line;m=if_limit;
- do {print_nl("### level ");print_int(n);print(": ");
+ @/do@+{print_nl("### level ");print_int(n);print(": ");
print_cmd_chr(if_test, t);
if (m==fi_code) print_esc("else");
print_if_line(l);
decr(n);t=subtype(p);l=if_line_field(p);m=type(p);p=link(p);
- } while (!(p==null));
+ }@+ while (!(p==null));
}
} @+break;
@@ -27234,17 +27223,17 @@ interaction mode.
primitive("interactionmode", set_page_int, 2);
@!@:interaction\_mode\_}{\.{\\interactionmode} primitive@>
-@ @<Cases of |set_page_int| for |print_cmd_chr|@>=
-if (chr_code==2) print_esc("interactionmode");
+@ @<Cases of |set_page_int| for |print_cmd_chr|@>=;
+else if (chr_code==2) print_esc("interactionmode")
-@ @<Cases for `Fetch the |dead_cycles| or the |insert_penalties|'@>=
-if (m==2) cur_val=interaction;
+@ @<Cases for `Fetch the |dead_cycles| or the |insert_penalties|'@>=;
+else if (m==2) cur_val=interaction
@ @<Declare \eTeX\ procedures for use...@>=
static void new_interaction(void);
-@ @<Cases for |alter_integer|@>=
-if (c==2)
+@ @<Cases for |alter_integer|@>=;
+else if (c==2)
{@+if ((cur_val < batch_mode)||(cur_val > error_stop_mode))
{@+print_err("Bad interaction mode");
@.Bad interaction mode@>
@@ -27263,8 +27252,8 @@ delimiters to appear between \.{\\left} and \.{\\right}.
primitive("middle", left_right, middle_noad);
@!@:middle\_}{\.{\\middle} primitive@>
-@ @<Cases of |left_right| for |print_cmd_chr|@>=
-if (chr_code==middle_noad) print_esc("middle");
+@ @<Cases of |left_right| for |print_cmd_chr|@>=;
+else if (chr_code==middle_noad) print_esc("middle")
@ The |scan_tokens| feature of \eTeX\ defines the \.{\\scantokens}
primitive.
@@ -27273,11 +27262,11 @@ primitive.
primitive("scantokens", input, 2);
@!@:scan\_tokens\_}{\.{\\scantokens} primitive@>
-@ @<Cases of |input| for |print_cmd_chr|@>=
-if (chr_code==2) print_esc("scantokens");
+@ @<Cases of |input| for |print_cmd_chr|@>=;
+else if (chr_code==2) print_esc("scantokens")
-@ @<Cases for |input|@>=
-if (cur_chr==2) pseudo_start();
+@ @<Cases for |input|@>=;
+else if (cur_chr==2) pseudo_start()
@ The global variable |pseudo_files| is used to maintain a stack of
pseudo files. The |info| field of each pseudo file points to a linked
@@ -27403,8 +27392,8 @@ while (pseudo_files!=null) pseudo_close(); /*flush pseudo files*/
primitive("readline", read_to_cs, 1);@/
@!@:read\_line\_}{\.{\\readline} primitive@>
-@ @<Cases of |read| for |print_cmd_chr|@>=
-print_esc("readline");
+@ @<Cases of |read| for |print_cmd_chr|@>=;
+else print_esc("readline")
@ @<Handle \.{\\readline} and |goto done|@>=
if (j==1)
@@ -27479,9 +27468,9 @@ preceding the mandatory \.{\\endcsname} have been expanded).
@<Cases for |conditional|@>=
case if_cs_code: {@+n=get_avail();p=n; /*head of the list of characters*/
- do {get_x_token();
+ @/do@+{get_x_token();
if (cur_cs==0) store_new_token(cur_tok);
- } while (!(cur_cs!=0));
+ }@+ while (!(cur_cs!=0));
if (cur_cmd!=end_cs_name) @<Complain about missing \.{\\endcsname}@>;
@<Look up the characters of list |n| in the hash table, and set |cur_cs|@>;
flush_list(n);
@@ -27523,8 +27512,8 @@ or during \.{\\write}.
primitive("protected", prefix, 8);
@!@:protected\_}{\.{\\protected} primitive@>
-@ @<Cases of |prefix| for |print_cmd_chr|@>=
-if (chr_code==8) print_esc("protected");
+@ @<Cases of |prefix| for |print_cmd_chr|@>=;
+else if (chr_code==8) print_esc("protected")
@ The |get_x_or_protected| procedure is like |get_x_token| except that
protected macros are not expanded.
@@ -27762,7 +27751,7 @@ resume: if (s==expr_none) o=l;@+else o=int_val;
found: @<Scan the next operator and set |o|@>;
arith_error=b;
@<Make sure that |f| is in the proper range@>;
-switch (s) {@t\4@>@<Cases for evaluation of the current term@>@;
+switch (s) {@<Cases for evaluation of the current term@>@;
} /*there are no other cases*/
if (o > expr_sub) s=o;@+else@<Evaluate the current expression@>;
b=arith_error;
@@ -28423,12 +28412,12 @@ else{@+if (sa_index(q) < mu_val_limit)
else if (sa_ptr(q)!=null) return;
s=pointer_node_size;
}
-do {i=hex_dig4(sa_index(q));p=q;q=link(p);free_node(p, s);
+@/do@+{i=hex_dig4(sa_index(q));p=q;q=link(p);free_node(p, s);
if (q==null) /*the whole tree has been freed*/
{@+sa_root[i]=null;return;
}
delete_sa_ptr;s=index_node_size; /*node |q| is an index node*/
-} while (!(sa_used(q) > 0));
+}@+ while (!(sa_used(q) > 0));
}
@ The |print_sa_num| procedure prints the register number corresponding
@@ -28531,7 +28520,7 @@ if (l < 4) /*|q| is an index node*/
}
else /*|q| is the node for a mark class*/
{@+switch (a) {
- @t\4@>@<Cases for |do_marks|@>@;
+ @<Cases for |do_marks|@>@;
} /*there are no other cases*/
if (sa_bot_mark(q)==null) if (sa_split_bot_mark(q)==null)
{@+free_node(q, mark_class_node_size);q=null;
@@ -28808,7 +28797,7 @@ at by |sa_chain|
@<Declare \eTeX\ procedures for tr...@>=
static void sa_restore(void)
{@+pointer p; /*sparse array element*/
-do {p=sa_loc(sa_chain);
+@/do@+{p=sa_loc(sa_chain);
if (sa_lev(p)==level_one)
{@+if (sa_index(p) >= dimen_val_limit) sa_destroy(sa_chain);
#ifdef @!STAT
@@ -28831,7 +28820,7 @@ delete_sa_ref(p);
p=sa_chain;sa_chain=link(p);
if (sa_index(p) < dimen_val_limit) free_node(p, word_node_size);
else free_node(p, pointer_node_size);
-} while (!(sa_chain==null));
+}@+ while (!(sa_chain==null));
}
@ When reading \.{\\patterns} while \.{\\savinghyphcodes} is positive
@@ -28852,8 +28841,8 @@ hyph_root=0;hyph_start=0;
@ @<Store hyphenation codes for current language@>=
{@+c=cur_lang;first_child=false;p=0;
-do {q=p;p=trie_r[q];
-} while (!((p==0)||(c <= so(trie_c[p]))));
+@/do@+{q=p;p=trie_r[q];
+}@+ while (!((p==0)||(c <= so(trie_c[p]))));
if ((p==0)||(c < so(trie_c[p])))
@<Insert a new trie node between |q| and |p|, and make |p| point to it@>;
q=p; /*now node |q| represents |cur_lang|*/
@@ -28931,9 +28920,9 @@ primitive("pagediscards", un_vbox, last_box_code);@/
primitive("splitdiscards", un_vbox, vsplit_code);@/
@!@:split\_discards\_}{\.{\\splitdiscards} primitive@>
-@ @<Cases of |un_vbox| for |print_cmd_chr|@>=
-if (chr_code==last_box_code) print_esc("pagediscards");
-else if (chr_code==vsplit_code) print_esc("splitdiscards");
+@ @<Cases of |un_vbox| for |print_cmd_chr|@>=;
+else if (chr_code==last_box_code) print_esc("pagediscards");
+else if (chr_code==vsplit_code) print_esc("splitdiscards")
@ @<Handle saved items and |goto done|@>=
{@+link(tail)=disc_ptr[cur_chr];disc_ptr[cur_chr]=null;
@@ -28993,7 +28982,7 @@ expand_depth_count=0;
@* The extended features of \Prote.
\Prote\ extends furthermore \eTeX\ i.e. \eTeX\ is thus required
before adding \Prote\ own extensions. But if \eTeX\ mode has not
-be enabled, the engine is still compatible with \TeX\ with no added
+be enabled, the engine is still compatible with \TeX with no added
primitive commands and with a modification of code---from
\eTeX\ exclusively for now---that is sufficiently minor so that
the engine still deserves the name \TeX.
@@ -29288,7 +29277,7 @@ hash since |no_new_control_sequence| is |true|), but since it has the
for a control sequence entered but never defined.
@ @<Cases for |conditional|@>=
-case if_primitive_code: {@+do get_token(); while (!(cur_tok!=space_token));
+case if_primitive_code: {@+@/do@+{get_token();}@+ while (!(cur_tok!=space_token));
if ((cur_cs!=0)&&(cur_cmd!=undefined_cs)&&(cur_cmd < call)) b=true;else b=false;
} @+break;
@@ -29650,7 +29639,7 @@ dependent. The information shall be set in |xchg_buffer|.
@^system dependencies@>
In this basic implementation, we set the string to the empty one by
-simply setting |xchg_buffer_length| to~$0$.
+simply setting |xchg_buffer_length| to $0$.
@d get_file_mtime xchg_buffer_length=0
@@ -29727,10 +29716,10 @@ case file_dump_code:
{ FILE *f=fopen((char*)name_of_file0,"rb");
if (f!=NULL) {@+
fseek(f,k,SEEK_SET);
- do {i=fgetc(f); if (i==EOF) break;
+ do@+{i=fgetc(f); if (i==EOF) break;
dig[0]=i%16;dig[1]=i/16;
print_the_digs(2);decr(l);
- } while (!(feof(f)||(l==0)));
+ }@+ while (!(feof(f)||(l==0)));
fclose(f);
}
} @+break;
@@ -29830,7 +29819,7 @@ communicate with the routines.
@ To obtain the MD5 hash signature of a file will need an external
implementation, since the algorithm requires bitwise operation that
-standard \PASCAL\ does not provide. So we do not bother to try. The
+standard \PASCAL does not provide. So we do not bother to try. The
present implementation returns nothing.
@^system dependencies@>
@@ -30015,7 +30004,7 @@ else{@+n=(n-1)*mpfract_one;
}
}
-@ The |do {| loop here preserves the following invariant relations
+@ The |@/do@+{| loop here preserves the following invariant relations
between |f|, |p|, and~|q|:
(i)~|0 <= p < q|; (ii)~$fq+p=2^k(q+p_0)$, where $k$ is an integer and
$p_0$ is the original value of~$p$.
@@ -30030,11 +30019,11 @@ in a register, not store it in memory.
@<Compute $f=\lfloor 2^{28}(1+p/q)+{1\over2}\rfloor$@>=
f=1;
-do {be_careful=p-q;p=be_careful+p;
+@/do@+{be_careful=p-q;p=be_careful+p;
if (p >= 0) f=f+f+1;
else{@+double(f);p=p+q;
}
-} while (!(f >= mpfract_one));
+}@+ while (!(f >= mpfract_one));
be_careful=p-q;
if (be_careful+p >= 0) incr(f)
@@ -30057,7 +30046,7 @@ else{@+n=f/mpfract_one;f=f%mpfract_one;
}
}
f=f+mpfract_one;
-@<Compute $p=\lfloor qf/2^{28}+{1\over2}\rfloor-q$@>@;
+@<Compute $p=\lfloor qf/2^{28}+{1\over2}\rfloor-q$@>;
be_careful=n-el_gordo;
if (be_careful+p > 0)
{@+arith_error=true;n=el_gordo-p;
@@ -30082,12 +30071,12 @@ $f_0$ is the original value of~$f$; (ii)~$2^k\L f<2^{k+1}$.
@<Compute $p=\lfloor qf/2^{28}+{1\over2}\rfloor-q$@>=
p=mpfract_half; /*that's $2^{27}$; the invariants hold now with $k=28$*/
if (q < mpfract_four)
- do {if (odd(f)) p=halfp(p+q);@+else p=halfp(p);
+ @/do@+{if (odd(f)) p=halfp(p+q);@+else p=halfp(p);
f=halfp(f);
- } while (!(f==1));
-else do {if (odd(f)) p=p+halfp(q-p);@+else p=halfp(p);
+ }@+ while (!(f==1));
+else@/do@+{if (odd(f)) p=p+halfp(q-p);@+else p=halfp(p);
f=halfp(f);
- } while (!(f==1));
+ }@+ while (!(f==1))
@ There's an auxiliary array |randoms| that contains 55 pseudo-random
fractions. Using the recurrence $x_n=(x_{n-55}-x_{n-31})\bmod 2^{28}$,
@@ -30275,15 +30264,15 @@ can readily be obtained with the ratio method (Algorithm 3.4.1R in
static scaled norm_rand(void)
{@+int @!x, @!u, @!l; /*what the book would call $2^{16}X$, $2^{28}U$,
and $-2^{24}\ln U$*/
-do {
- do {next_random;
+@/do@+{
+ @/do@+{next_random;
x=take_mpfract(112429, randoms[j_random]-mpfract_half);
/*$2^{16}\sqrt{8/e}\approx 112428.82793$*/
next_random;u=randoms[j_random];
- } while (!(abs(x) < u));
+ }@+ while (!(abs(x) < u));
x=make_mpfract(x, u);
l=139548960-m_log(u); /*$2^{24}\cdot12\ln2\approx139548959.6165$*/
-} while (!(ab_vs_cd(1024, l, x, x) >= 0));
+}@+ while (!(ab_vs_cd(1024, l, x, x) >= 0));
return x;
}
@@ -31171,8 +31160,8 @@ if (option_no_empty_page &&
((eject && penalty(p)>2*(eject_penalty)) ||
(page_contents==empty && !is_visible(p))))
{ pointer r, prev_r = p;
- loop@+{
- r =link(prev_r);
+ while (true)
+ { r =link(prev_r);
if (r==null) return;
else if (is_visible(r)) break;
else if (type(r)==penalty_node && penalty(r)<=eject_penalty)
@@ -32017,14 +32006,15 @@ static pointer new_setpage_node(uint8_t i, str_number n)
for (p=link(prev_p); p!=null; prev_p=p,p=link(p))
if (setpage_id(p)==i) break;
if (p==null)
- @<Allocate a new |setpage_node| |p|@>@;
+ @<allocate a new |setpage_node| |p|@>@;
else
link(prev_p)=link(p);
link(p)=link(setpage_head);
link(setpage_head)=p;
return p;
}
-@ @<Allocate a new |setpage_node| |p|@>=
+@
+@<allocate a new |setpage_node| |p|@>=
{ p=get_node(setpage_node_size);type(p)=whatsit_node;subtype(p)=setpage_node;
setpage_number(p)=max_ref[page_kind]=++max_page;
setpage_id(p)=i;
@@ -32111,7 +32101,7 @@ static void hint_open(void)
output_file_name=make_name_string();
DBG(DBGBASIC,"Output file %s opened\n",(char *)name_of_file+1);
}
-@#
+
#define HITEX_VERSION "1.1"
static void hput_definitions();
extern int option_global;
@@ -32326,7 +32316,7 @@ but we will need it repeatedly in the function |hdef_param_node| and the overhea
not warrant having the mapping in both directions.
@<Hi\TeX\ variables@>=
-static const int hmap_int[] ={@t\1\1@>@/
+static const int hmap_int[] ={@/
pretolerance_no, /* |pretolerance_code| 0 */
tolerance_no, /* |tolerance_code| 1 */
line_penalty_no, /* |line_penalty_code| 2 */
@@ -32369,7 +32359,7 @@ year_no, /* |year_code| 23 */
-1, /* |output_penalty_code| 39 */
-1, /* |max_dead_cycles_code| 40 */
hang_after_no, /* |hang_after_code| 41*/
-@t\2\2@> floating_penalty_no /* |floating_penalty_code| 42*/
+floating_penalty_no /* |floating_penalty_code| 42*/
};
@@ -32381,7 +32371,7 @@ hang_after_no, /* |hang_after_code| 41*/
if ( hmap_int[i]>=0) int_defined[hmap_int[i]]=int_par(i);
max_ref[int_kind]=MAX_INT_DEFAULT;
@ The function |hget_int_no| tries to allocate a predefined integer number;
-if not successful, it returns~$-1$.
+if not successful, it returns $-1$.
@<Hi\TeX\ auxiliary routines@>=
static int hget_int_no(int32_t n)
@@ -32424,7 +32414,7 @@ We proceed as we did for integers, starting with the array that holds the define
@<Hi\TeX\ variables@>=
static scaled dimen_defined[0x100]={0};
@ @<Hi\TeX\ variables@>=
-static const int hmap_dimen[] ={@t\1\1@>@/
+static const int hmap_dimen[] ={@/
-1, /* |par_indent_code| 0 */
-1, /* |math_surround_code| 1 */
line_skip_limit_no, /* |line_skip_limit_code| 2 */
@@ -32445,7 +32435,7 @@ static const int hmap_dimen[] ={@t\1\1@>@/
hang_indent_no, /* |hang_indent_code| 17 */
-1, /* |h_offset_code| 18 */
-1, /* |v_offset_code| 19 */
- @t\2\2@> emergency_stretch_no /* |emergency_stretch_code| 20 */
+ emergency_stretch_no /* |emergency_stretch_code| 20 */
};
@ @<Fix definitions for dimension parameters@>=
dimen_defined[zero_dimen_no]=0;
@@ -32551,7 +32541,8 @@ glue specifications and often a simple comparison of pointers might suffice.
@<Hi\TeX\ variables@>=
static pointer glue_defined[0x100];
@ @<Hi\TeX\ variables@>=
-static int hmap_glue[] ={@t\1\1@>@/
+static int hmap_glue[] ={
+
line_skip_no, /* |line_skip_code| 0 */
baseline_skip_no, /* |baseline_skip_code| 1 */
-1, /* |par_skip_code| 2 */
@@ -32566,7 +32557,7 @@ split_top_skip_no, /* |split_top_skip_code| 10 */
tab_skip_no, /* |tab_skip_code| 11 */
-1, /* |space_skip_code| 12 */
-1, /* |xspace_skip_code| 13 */
-@t\2\2@> par_fill_skip_no /* |par_fill_skip_code| 14 */
+par_fill_skip_no /* |par_fill_skip_code| 14 */
};
@ @<Fix definitions for glue parameters@>=
glue_defined[zero_skip_no]=zero_glue; incr(glue_ref_count(zero_glue));
@@ -32818,8 +32809,8 @@ static bool node_equal(pointer p, pointer q)
}
static bool list_equal(pointer p, pointer q)
-{@+loop@+{
- if (p==q) return true;
+{@+while (true)
+ { if (p==q) return true;
if (p==null || q==null) return false;
if (!node_equal(p,q)) return false;
p=link(p);q=link(q);
@@ -34397,7 +34388,7 @@ static const char *option_mfmode="ljfour", *option_dpi_str="600";
extern int option_compress;
extern unsigned int debugflags;
-static struct option long_options[] = {@t\1\1@>@/
+static struct option long_options[] = {@/
{ "help", 0, 0, 0 },@/
{ "version", 0, 0, 0 },@/
{ "interaction", 1, 0, 0 },@/
@@ -34428,8 +34419,8 @@ static struct option long_options[] = {@t\1\1@>@/
{ "hint-debug", 1, 0, 0 },@/
{ "hint-debug-help", 0, 0, 0 },@/
#endif
-@t\2\2@> { 0, 0, 0, 0 } @/
-};
+ { 0, 0, 0, 0 }@+}@+;
+
@ Parsing the command line options is accomplished with the
|parse_options| function which in turn uses the |getopt_long_only|
@@ -34438,7 +34429,7 @@ function from the \CEE/ library. This function returns 0 and sets the
the end of all options is reached.
@<\TeX\ Live functions@>=
static void parse_options (int argc, char *argv[])
-{@+ loop @+ {
+{@+ while (true) {
int option_index;
int g = getopt_long_only (argc, argv, "+", long_options, &option_index);
if (g==0)
@@ -34488,7 +34479,7 @@ the option structure.
#include <kpathsea/kpathsea.h>
static int argument_is(struct option *opt, char * s)
{@+ return STREQ(opt->name, s); @+}
-#define ARGUMENT_IS(S) @[argument_is(long_options+option_index,S)@]
+#define ARGUMENT_IS(S) argument_is(long_options+option_index,S)
@ Now we can handle the first two options:
@@ -35111,7 +35102,7 @@ last=first;
while (optind < argc) input_add_str(argv[optind++]);
loc=first;
return (loc < last);
-}
+@+ }
@ @<Forward declarations@>=
static int input_command_line(void);
@@ -35169,9 +35160,8 @@ static bool b_open_out(byte_file *f) /*open a binary file for output*/
{@+f->f=open_out((char *)name_of_file+1,"wb");
return f->f!=NULL && ferror(f->f)==0;@+
}
-@#
+
#ifdef @!INIT
-@+@t}\6\4\4{@>
static bool w_open_out(word_file *f) /*open a word file for output*/
{@+f->f=open_out((char *)name_of_file+1,"wb");
return f->f!=NULL && ferror(f->f)==0;@+
@@ -35359,8 +35349,6 @@ static int get_md5_sum(int s, int file);
@ The code that follows was taken from the \.{texmfmp.c} file of
the \TeX\ Live distribution and slightly modified.
-@s md5_byte_t int
-@s md5_state_t int
@<\TeX\ Live auxiliary functions@>=
@@ -35419,17 +35407,17 @@ make_time_str(time_t t, bool utc)
static void get_creation_date(void)
{ make_time_str(start_time,source_date_epoch!=NULL);
}
-@#
-#ifdef WIN32
+
/* static structure for file status set by |find_input_file| */
-@+@t}\6{@>
+#ifdef WIN32
static struct _stat file_stat;
-#define GET_FILE_STAT @[_stat(fname,&file_stat)@]
+#define GET_FILE_STAT _stat(fname,&file_stat)
#else
static struct stat file_stat;
-#define GET_FILE_STAT @[stat(fname,&file_stat)@]
+#define GET_FILE_STAT stat(fname,&file_stat)
#endif
+
static char* find_input_file(void)
{ char *fname;
int r;
diff --git a/Build/source/texk/web2c/luatexdir/ChangeLog b/Build/source/texk/web2c/luatexdir/ChangeLog
index 282484e2f84..6885c01f395 100644
--- a/Build/source/texk/web2c/luatexdir/ChangeLog
+++ b/Build/source/texk/web2c/luatexdir/ChangeLog
@@ -1,3 +1,8 @@
+2024-09-01 Luigi Scarso <luigi.scarso@gmail.com>
+ * Changed char utf8word[(4 * MAX_WORD_LEN) + 1] = { 0 };
+ in char utf8word[(4 * MAX_WORD_LEN) + 1] ;
+ in function void hnj_hyphenation(halfword head, halfword tail) (thanks to user202729@protonmail.com)
+
2024-05-11 Luigi Scarso <luigi.scarso@gmail.com>
* allow interactive deletion of \outer token, see https://tug.org/texmfbug/newbug.html#B142outer
diff --git a/Build/source/texk/web2c/luatexdir/lang/texlang.c b/Build/source/texk/web2c/luatexdir/lang/texlang.c
index bc912de0bb1..a7a741b55cb 100644
--- a/Build/source/texk/web2c/luatexdir/lang/texlang.c
+++ b/Build/source/texk/web2c/luatexdir/lang/texlang.c
@@ -943,7 +943,7 @@ void hnj_hyphenation(halfword head, halfword tail)
int lchar, i;
struct tex_language *lang;
lang_variables langdata;
- char utf8word[(4 * MAX_WORD_LEN) + 1] = { 0 };
+ char utf8word[(4 * MAX_WORD_LEN) + 1];
int wordlen = 0;
char *hy = utf8word;
char *replacement = NULL;
diff --git a/Build/source/texk/web2c/luatexdir/luatex_svnversion.h b/Build/source/texk/web2c/luatexdir/luatex_svnversion.h
index 5422bb4ad06..add4ee39419 100644
--- a/Build/source/texk/web2c/luatexdir/luatex_svnversion.h
+++ b/Build/source/texk/web2c/luatexdir/luatex_svnversion.h
@@ -1,4 +1,4 @@
#ifndef luatex_svn_revision_h
#define luatex_svn_revision_h
-#define luatex_svn_revision 7621
+#define luatex_svn_revision 7624
#endif
diff --git a/Build/source/texk/web2c/man/ChangeLog b/Build/source/texk/web2c/man/ChangeLog
index f82821f1847..b047bb6abf4 100644
--- a/Build/source/texk/web2c/man/ChangeLog
+++ b/Build/source/texk/web2c/man/ChangeLog
@@ -1,3 +1,7 @@
+2024-08-12 Andreas Scherer <https://ascherer.github.io>
+
+ * ctwill.man: CWEB 4.12 release.
+
2024-04-14 TANAKA Takuji <ttk@t-lab.opal.ne.jp>
* Makefile.am: Remove eptex.
diff --git a/Build/source/texk/web2c/man/ctwill.man b/Build/source/texk/web2c/man/ctwill.man
index 50437193073..a30dcd6a0be 100644
--- a/Build/source/texk/web2c/man/ctwill.man
+++ b/Build/source/texk/web2c/man/ctwill.man
@@ -1,6 +1,6 @@
.\" Automatically generated by Pandoc
.\"
-.TH "CTWILL" "1" "March 10, 2024" "Web2c @VERSION@" "General Commands Manual"
+.TH "CTWILL" "1" "August 12, 2024" "Web2c @VERSION@" "General Commands Manual"
.SH NAME
ctwill, ctwill-proofsort, ctwill-refsort, ctwill-twinx \- translate CWEB to TeX with
mini-indexes
@@ -122,7 +122,7 @@ The present incarnation of \f[B]ctwill\f[R] and its utilities tries hard
to be a drop-in replacement for the original package.
There are, however, a few differences worth noting:
.IP \[bu] 2
-This version is based on the most recent version of CWEB (4.11).
+This version is based on the most recent version of CWEB (4.12).
.IP \[bu] 2
In TeX\ Live the utility programs are prefixed with \f[B]ctwill\-\f[R]
and the macro files with \f[B]ct\f[R] for technical reasons.
diff --git a/Build/source/texk/web2c/mft.ch b/Build/source/texk/web2c/mft.ch
index 57f82579138..4bb78a0bcf1 100644
--- a/Build/source/texk/web2c/mft.ch
+++ b/Build/source/texk/web2c/mft.ch
@@ -74,7 +74,7 @@ procedure initialize;
var @<Local variables for initialization@>@/
begin @<Set initial values@>@/
@y
-@<Define |parse_arguments|@>
+@<Define \(|parse_arguments|@>
procedure initialize;
var @<Local variables for initialization@>@/
begin
@@ -989,7 +989,7 @@ used to provide basic formatting.
@d argument_is (#) == (strcmp (long_options[option_index].name, #) = 0)
-@<Define |parse_arguments|@> =
+@<Define \(|parse_arguments|@> =
procedure parse_arguments;
const n_options = 5; {Pascal won't count array lengths for us.}
var @!long_options: array[0..n_options] of getopt_struct;
diff --git a/Build/source/texk/web2c/patgen.ch b/Build/source/texk/web2c/patgen.ch
index e6accc88d89..c4c45d12a60 100644
--- a/Build/source/texk/web2c/patgen.ch
+++ b/Build/source/texk/web2c/patgen.ch
@@ -53,7 +53,7 @@ procedure initialize; {this procedure gets things started properly}
var @<Local variables for initialization@>@/
begin print_ln(banner);@/
@y
-@<Define |parse_arguments|@>
+@<Define \(|parse_arguments|@>
procedure initialize; {this procedure gets things started properly}
var @<Local variables for initialization@>@/
begin
@@ -97,11 +97,11 @@ error message about what caused the error.
% machines, from Johannes Hielscher, 10 Jul 2019 00:00:03 (tex-live list),
% and later from Mojca Miklavec, 23 Sep 2019 21:21:42.
% It seems hopeless, so went back to the original values (10000000/500000).
-%
+%
% The real solution is to provide a way to allocate the arrays
% dynamically, so that the large arrays can be used by those who need
% them but other are not affected.
-%
+%
% If the values here are still too big, you can probably get it to work
% by adding swap or zram; or write a patch to allocate the arrays dynamically.
@x [27]
@@ -243,7 +243,7 @@ Parse a Unix-style command line.
@d argument_is (#) == (strcmp (long_options[option_index].name, #) = 0)
-@<Define |parse_arguments|@> =
+@<Define \(|parse_arguments|@> =
procedure parse_arguments;
const n_options = 2; {Pascal won't count array lengths for us.}
var @!long_options: array[0..n_options] of getopt_struct;
diff --git a/Build/source/texk/web2c/patgen.web b/Build/source/texk/web2c/patgen.web
index a34597237ff..1d6a7f40e15 100644
--- a/Build/source/texk/web2c/patgen.web
+++ b/Build/source/texk/web2c/patgen.web
@@ -7,11 +7,14 @@
% Version 2.3 avoided division by zero - Karl Berry (October, 1996).
% Version 2.4 avoided use of uninitialized variables (September, 2009).
% A few typos fixed, no version change (April, 2020).
+% Sorting order fixed, no version change (September, 2024).
% Here is TeX material that gets inserted after \input webmac
\def\hang{\hangindent 3em\indent\ignorespaces}
\def\PASCAL{Pascal}
+\def\({} % this is used to make section names sort themselves better
+
\def\title{PATGEN}
\def\contentspagenumber{45} % should be odd
\def\topofcontents{\null
@@ -722,7 +725,7 @@ right of the occupied region (starting at |trie_max+1|).
@p function first_fit: trie_pointer;
label found, not_found;
var s, @!t: trie_pointer; @!q: q_index;
-begin @<Set |s| to the trie base location at which this state should be
+begin @<Set \(|s| to the trie base location at which this state should be
packed@>;
for q:=1 to qmax do {pack it}
begin t:=s+q_char(q);@/
@@ -743,7 +746,7 @@ one level of patterns is being generated, the threshold is set to 7 on
subsequent levels because the pattern trie will be sparser after bad
patterns are deleted (see |delete_bad_patterns|).
-@<Set |s| to the trie base location at which this state should be packed@>=
+@<Set \(|s| to the trie base location at which this state should be packed@>=
if qmax>qmax_thresh then t:=trie_back(trie_max+1) @+else t:=0;
loop begin t:=trie_link(t); s:=t-q_char(1); {get next unoccupied cell}
@<Ensure |trie| linked up to |s+num_ASCII_codes|@>;
@@ -914,7 +917,7 @@ end;
@p function firstc_fit: triec_pointer;
label found, not_found;
var a, @!b: triec_pointer; @!q: q_index;
-begin @<Set |b| to the count trie base location at which this state should
+begin @<Set \(|b| to the count trie base location at which this state should
be packed@>;
for q:=1 to qmax do {pack it}
begin a:=b+q_char(q);@/
@@ -932,7 +935,7 @@ end;
@ The threshold for attempting a first-fit packing is 3 transitions, which
is lower than for the pattern trie because speed is more important here.
-@<Set |b| to the count trie base location...@>=
+@<Set \(|b| to the count trie base location...@>=
if qmax>3 then a:=triec_back(triec_max+1) @+else a:=0;
loop begin a:=triec_link(a); b:=a-q_char(1);@/
@<Ensure |triec| linked up to |b+num_ASCII_codes|@>;
diff --git a/Build/source/texk/web2c/pdftexdir/ChangeLog b/Build/source/texk/web2c/pdftexdir/ChangeLog
index 9fcf5a277d2..ee5e17bb9f3 100644
--- a/Build/source/texk/web2c/pdftexdir/ChangeLog
+++ b/Build/source/texk/web2c/pdftexdir/ChangeLog
@@ -1,3 +1,11 @@
+2024-08-04 Thanh Han The <hanthethanh@gmail.com>
+
+ * pdftoepdf.cc (write_epdf): reversed sep and pdfkeyprefix.
+
+2024-07-26 Andreas Scherer <https://ascherer.github.io>
+
+ * pdftex.ch: Prepare for compilation with gcc-14.
+
2024-05-24 Thanh Han The <hanthethanh@gmail.com>
* pdftex.web (pdf_ptex_use_underscore_code),
@@ -5,7 +13,7 @@
(Put each ...): define it.
(print_param): print it.
(get_ptex_use_underscore): new fn to implement it.
- (pdf_print_info): adjust Fullbanner prefix character.
+ (pdf_print_info): adjust Fullbanner prefix character.
* pdftoepdf.cc (getptexuseunderscore): declare fn.
(write_epdf): use it for FileName, PageNumber, InfoDict.
diff --git a/Build/source/texk/web2c/pdftexdir/pdftex.ch b/Build/source/texk/web2c/pdftexdir/pdftex.ch
index 0607f7f2f1f..a30a74c3376 100644
--- a/Build/source/texk/web2c/pdftexdir/pdftex.ch
+++ b/Build/source/texk/web2c/pdftexdir/pdftex.ch
@@ -179,7 +179,7 @@ pdf_font_type:=xmalloc_array(eight_bits, font_max);
pdf_font_attr:=xmalloc_array(str_number, font_max);
pdf_font_blink:=xmalloc_array(internal_font_number, font_max);
pdf_font_elink:=xmalloc_array(internal_font_number, font_max);
-pdf_font_has_space_char:=xmalloc_array(internal_font_number, font_max);
+pdf_font_has_space_char:=xmalloc_array(boolean, font_max);
pdf_font_stretch:=xmalloc_array(integer, font_max);
pdf_font_shrink:=xmalloc_array(integer, font_max);
pdf_font_step:=xmalloc_array(integer, font_max);
@@ -298,7 +298,7 @@ pdf_font_type:=xmalloc_array(eight_bits,font_max);
pdf_font_attr:=xmalloc_array(str_number,font_max);
pdf_font_blink:=xmalloc_array(internal_font_number,font_max);
pdf_font_elink:=xmalloc_array(internal_font_number,font_max);
-pdf_font_has_space_char:=xmalloc_array(internal_font_number,font_max);
+pdf_font_has_space_char:=xmalloc_array(boolean,font_max);
pdf_font_stretch:=xmalloc_array(integer,font_max);
pdf_font_shrink:=xmalloc_array(integer,font_max);
pdf_font_step:=xmalloc_array(integer,font_max);
diff --git a/Build/source/texk/web2c/pdftexdir/pdftoepdf.cc b/Build/source/texk/web2c/pdftexdir/pdftoepdf.cc
index 238ed4dba5d..237bd6530c4 100644
--- a/Build/source/texk/web2c/pdftexdir/pdftoepdf.cc
+++ b/Build/source/texk/web2c/pdftexdir/pdftoepdf.cc
@@ -846,18 +846,18 @@ void write_epdf(void)
// write additional information
if ((suppress_ptex_info & MASK_SUPPRESS_PTEX_FILENAME) == 0) {
- pdf_printf("/%s%sFileName (%s)\n", sep, pdfkeyprefix,
+ pdf_printf("/%s%sFileName (%s)\n", pdfkeyprefix, sep,
convertStringToPDFString(pdf_doc->file_name,
strlen(pdf_doc->file_name)));
}
if ((suppress_ptex_info & MASK_SUPPRESS_PTEX_PAGENUMBER) == 0) {
- pdf_printf("/%s%sPageNumber %i\n", sep, pdfkeyprefix, (int) epdf_selected_page);
+ pdf_printf("/%s%sPageNumber %i\n", pdfkeyprefix, sep, (int) epdf_selected_page);
}
if ((suppress_ptex_info & MASK_SUPPRESS_PTEX_INFODICT) == 0) {
pdf_doc->doc->getDocInfoNF(&info);
if (info.isRef()) {
// the info dict must be indirect (PDF Ref p. 61)
- pdf_printf("/%s%sInfoDict ", sep, pdfkeyprefix);
+ pdf_printf("/%s%sInfoDict ", pdfkeyprefix, sep);
pdf_printf("%d 0 R\n", addOther(info.getRef()));
}
}
diff --git a/Build/source/texk/web2c/pktogf.ch b/Build/source/texk/web2c/pktogf.ch
index bd468f2a9de..7f6fc2d80a2 100644
--- a/Build/source/texk/web2c/pktogf.ch
+++ b/Build/source/texk/web2c/pktogf.ch
@@ -12,7 +12,7 @@
% gf and pk formats. PKtoGF runs silently unless it is given the
% -v switch in the command line.
-@x [0] l.21
+@x [0] l.22
\def\title{PKtoGF}
@y
\def\title{PK$\,$\lowercase{to}$\,$GF changes for C}
@@ -60,7 +60,7 @@ procedure initialize; {this procedure gets things started properly}
var i:integer; {loop index for initializations}
begin print_ln(banner);@/
@y
-@<Define |parse_arguments|@>
+@<Define \(|parse_arguments|@>
procedure initialize; {this procedure gets things started properly}
var i:integer; {loop index for initializations}
begin
@@ -79,7 +79,7 @@ begin
@<Labels...@>=final_end;
@y
@ This module is deleted, because it is only useful for
-a non-local goto, which we don't use in C.
+a non-local |goto|\unskip, which we don't use in C.
@z
% [6] Remove terminal_line_length, since there is no dialog, and
@@ -459,7 +459,7 @@ Parse a Unix-style command line.
@d argument_is (#) == (strcmp (long_options[option_index].name, #) = 0)
-@<Define |parse_arguments|@> =
+@<Define \(|parse_arguments|@> =
procedure parse_arguments;
const n_options = 3; {Pascal won't count array lengths for us.}
var @!long_options: array[0..n_options] of getopt_struct;
diff --git a/Build/source/texk/web2c/pktogf.web b/Build/source/texk/web2c/pktogf.web
index eadd46c093b..abe9a23fd8b 100644
--- a/Build/source/texk/web2c/pktogf.web
+++ b/Build/source/texk/web2c/pktogf.web
@@ -8,6 +8,7 @@
% Cleaned up description (bitweight errors) no version change: July 1990
% Fixed bug with empty character setting min_n to 1 (1.1): 19 October 1990
% A few typos, no version change: 22 April 2020, 27 October 2020
+% Sorting order fixed, no version change: 02 September 2024
\def\versiondate{22 April 2020}
%
\font\ninerm=cmr9
@@ -16,7 +17,7 @@
\def\MF{{\logo META}\-{\logo FONT}}
\def\PASCAL{{\mc Pascal}}
\def\tamu{Texas A\char38 M}
-\def\(#1){} % this is used to make section names sort themselves better
+\def\({} % this is used to make section names sort themselves better
\def\9#1{} % this is used for sort keys in the index
\def\title{PKtoGF}
\def\contentspagenumber{1}
@@ -1197,7 +1198,7 @@ else
@<Read short character preamble@> ;
@<Calculate and check |min_m|, |max_m|, |min_n|, and |max_n|@> ;
@<Save character locator@> ;
-@<Write character preamble@> ;
+@<Write \(character preamble@> ;
@<Read and translate raster description@> ;
gf_byte(eoc) ;
last_eoc := gf_loc ;
@@ -1360,7 +1361,7 @@ for i := 0 to 255 do
@ This module takes care of the simple job of writing the character
preamble, after picking one to fit.
-@<Write character preamble@>=
+@<Write \(character preamble@>=
begin
if (char_pointer[car mod 256] = -1) and
(car >= 0) and (car < 256) and
@@ -1408,7 +1409,7 @@ end
@ And another module to write out those character locators we have so
carefully saved up the information for.
-@<Write character locators@>=
+@<Write \(character locators@>=
for i := 0 to 255 do
if char_pointer[i] <> -1 then begin
if (s_ver_esc[i] = 0) and (s_hor_esc[i] >= 0) and
@@ -1615,7 +1616,7 @@ gf_quad(mmin_m) ;
gf_quad(mmax_m) ;
gf_quad(mmin_n) ;
gf_quad(mmax_n) ;
-@<Write character locators@> ;
+@<Write \(character locators@> ;
gf_byte(post_post) ;
gf_quad(j) ;
gf_byte(gf_id_byte) ;
diff --git a/Build/source/texk/web2c/pktype.ch b/Build/source/texk/web2c/pktype.ch
index b5e64566529..58316843997 100644
--- a/Build/source/texk/web2c/pktype.ch
+++ b/Build/source/texk/web2c/pktype.ch
@@ -8,6 +8,12 @@
% There is no terminal input to this program.
% Output is to stdout, and may, of course, be redirected.
+@x [0] l.25
+\def\(#1){} % this is used to make section names sort themselves better
+@y
+\def\({} % this is used to make section names sort themselves better
+@z
+
@x [0] l.27
\def\title{PKtype}
@y
@@ -46,7 +52,7 @@ procedure initialize; {this procedure gets things started properly}
@y
type @<Types in the outer block@>@/
var @<Globals in the outer block@>@/
-@<Define |parse_arguments|@>
+@<Define \(|parse_arguments|@>
procedure initialize; {this procedure gets things started properly}
var i:integer; {loop index for initializations}
begin
@@ -65,7 +71,7 @@ begin
@<Labels...@>=final_end;
@y
@ This module is deleted, because it is only useful for
-a non-local |goto|, which we don't use in C.
+a non-local |goto|\unskip, which we don't use in C.
@z
% [6] No need for |name_length| or |terminal_line_length|. Since these
@@ -89,7 +95,7 @@ to the very end of the program with an error message.
begin goto final_end;
end;
@y
-@ We use a call to the external C exit to avoid a non-local |goto|.
+@ We use a call to the external C exit to avoid a non-local |goto|\unskip.
@d abort(#)==begin print_ln(#); uexit(1) end
@@ -335,7 +341,7 @@ Parse a Unix-style command line.
@d argument_is (#) == (strcmp (long_options[option_index].name, #) = 0)
-@<Define |parse_arguments|@> =
+@<Define \(|parse_arguments|@> =
procedure parse_arguments;
const n_options = 2; {Pascal won't count array lengths for us.}
var @!long_options: array[0..n_options] of getopt_struct;
diff --git a/Build/source/texk/web2c/pltotf.ch b/Build/source/texk/web2c/pltotf.ch
index fcc08fbc5cb..c4a9a535d24 100644
--- a/Build/source/texk/web2c/pltotf.ch
+++ b/Build/source/texk/web2c/pltotf.ch
@@ -11,6 +11,12 @@
% 01/16/90 (SR) Version 3.2.
% (more recent changes in the ChangeLog)
+@x [0] l.31
+\def\(#1){} % this is used to make section names sort themselves better
+@y
+\def\({} % this is used to make section names sort themselves better
+@z
+
@x [0] l.34
\def\title{PL\lowercase{to}TF}
@y
@@ -45,7 +51,7 @@ procedure initialize; {this procedure gets things started properly}
var @<Local variables for initialization@>@/
begin print_ln(banner);@/
@y
-@<Define |parse_arguments|@>
+@<Define \(|parse_arguments|@>
procedure initialize; {this procedure gets things started properly}
var @<Local variables for initialization@>@/
begin kpse_set_program_name (argv[0], my_name);
@@ -263,7 +269,7 @@ Parse a Unix-style command line.
@d argument_is (#) == (strcmp (long_options[option_index].name, #) = 0)
-@<Define |parse_arguments|@> =
+@<Define \(|parse_arguments|@> =
procedure parse_arguments;
const n_options = 3; {Pascal won't count array lengths for us.}
var @!long_options: array[0..n_options] of getopt_struct;
diff --git a/Build/source/texk/web2c/pooltype.ch b/Build/source/texk/web2c/pooltype.ch
index 74b4d9a2354..8aa11064223 100644
--- a/Build/source/texk/web2c/pooltype.ch
+++ b/Build/source/texk/web2c/pooltype.ch
@@ -4,6 +4,12 @@
% 11/29/89 (KB) Version released with 8-bit TeX.
% (more recent changes in the ChangeLog)
+@x [0] l.13
+\def\(#1){} % this is used to make section names sort themselves better
+@y
+\def\({} % this is used to make section names sort themselves better
+@z
+
@x [0] l.16
\def\title{POOL\lowercase{type}}
@y
@@ -34,7 +40,7 @@ procedure initialize; {this procedure gets things started properly}
var @<Local variables for initialization@>@;
begin @<Set initial values of key variables@>@/
@y
-@<Define |parse_arguments|@>
+@<Define \(|parse_arguments|@>
procedure initialize; {this procedure gets things started properly}
var @<Local variables for initialization@>@;
begin
@@ -112,7 +118,7 @@ Parse a Unix-style command line.
@d argument_is (#) == (strcmp (long_options[option_index].name, #) = 0)
-@<Define |parse_arguments|@> =
+@<Define \(|parse_arguments|@> =
procedure parse_arguments;
const n_options = 2; {Pascal won't count array lengths for us.}
var @!long_options: array[0..n_options] of getopt_struct;
diff --git a/Build/source/texk/web2c/ptexdir/ChangeLog b/Build/source/texk/web2c/ptexdir/ChangeLog
index 2fe03aef772..92cce7d5f48 100644
--- a/Build/source/texk/web2c/ptexdir/ChangeLog
+++ b/Build/source/texk/web2c/ptexdir/ChangeLog
@@ -1,3 +1,29 @@
+2024-09-22 TANAKA Takuji <ttk@t-lab.opal.ne.jp>
+
+ * ptex-base.ch, ptex_version.h: Version p4.1.2.
+
+2024-09-21 TANAKA Takuji <ttk@t-lab.opal.ne.jp>
+
+ * ptex-base.ch:
+ Fix a bug in initialization of inhibit_xsp/kinsoku tables
+ (Bug introduced on commit 2022-12-12 r65248).
+ * ptex-ctrlsym.test, tests/{ptexlineendmode,ucs}.tex,
+ (chartoken,ucs-{e,s}}.log, p-lineenda.typ:
+ Add more regression tests.
+ * tests/{free_{ixsp,pena},inhibitglue,kinsoku_{search,table},
+ postpena}.tex: Move to ../euptexdir/tests .
+ * am/ptex.am: Adjust.
+
+2024-09-16 TANAKA Takuji <ttk@t-lab.opal.ne.jp>
+
+ * ptex-base.ch: Use constants enc_jis and enc_ucs.
+ * ptex-{ctrlsym,prntknj,ucs}.test,
+ tests/{control-symbol,ctrlsym,endlinechar,printkanji,ucs}.tex,
+ tests/{ctrlsym,endline,jctrlsym,pknj-{e,s},ucs-{e,s}).log,
+ tests/p-{endline,jctrlsym,pknj-{e,s}}a.typ:
+ Add regression tests by using Kitagawa-san's sample files.
+ * am/ptex.am: Adjust.
+
2024-05-18 Hironori Kitagawa <h_kitagawa2001@yahoo.co.jp>
* ptex-base.ch: Enable to get character codes
diff --git a/Build/source/texk/web2c/ptexdir/am/ptex.am b/Build/source/texk/web2c/ptexdir/am/ptex.am
index ee8117c6800..caf30c2f986 100644
--- a/Build/source/texk/web2c/ptexdir/am/ptex.am
+++ b/Build/source/texk/web2c/ptexdir/am/ptex.am
@@ -107,9 +107,14 @@ EXTRA_DIST += \
ptex_tests = \
ptexdir/ptriptest.test \
ptexdir/pver.test \
- ptexdir/wcfname.test
+ ptexdir/wcfname.test \
+ ptexdir/ptex-ctrlsym.test \
+ ptexdir/ptex-prntknj.test \
+ ptexdir/ptex-ucs.test
ptexdir/ptriptest.log: ptex$(EXEEXT) dvitype$(EXEEXT) pltotf$(EXEEXT) tftopl$(EXEEXT)
-ptexdir/pver.log ptexdir/wcfname.log: ptex$(EXEEXT)
+ptexdir/pver.log ptexdir/wcfname.log \
+ ptexdir/ptex-ctrlsym.log ptexdir/ptex-prntknj.log \
+ ptexdir/ptex-ucs.log: ptex$(EXEEXT)
EXTRA_DIST += $(ptex_tests) $(pweb_tests)
DISTCLEANFILES += $(pweb_programs:=.c) $(pweb_programs:=.h) \
@@ -166,12 +171,31 @@ EXTRA_DIST += ptexdir/tests/skipjfmp.pl ptexdir/tests/skipjfmp.tfm
DISTCLEANFILES += ptests/xskipjfmp.*
## ptexdir/ptriptest
EXTRA_DIST += ptexdir/ptrip/ptrip.diffs ptexdir/ptrip/texmf.cnf
+DISTCLEANFILES += ptrip.diffs
+
+## ptex-ctrlsym.test
+EXTRA_DIST += ptexdir/tests/ctrlsym.tex ptexdir/tests/ctrlsym.log \
+ ptexdir/tests/endlinechar.tex ptexdir/tests/endline.log \
+ ptexdir/tests/control-symbol.tex ptexdir/tests/jctrlsym.log \
+ ptexdir/tests/p-endlinea.typ ptexdir/tests/p-jctrlsyma.typ \
+ ptexdir/tests/let_char_token.tex ptexdir/tests/chartoken.log \
+ ptexdir/tests/ptexlineendmode.tex ptexdir/tests/p-lineenda.typ
+DISTCLEANFILES += p-ctrlsym.log p-ctrlsym.out p-ctrlsym.tex \
+ p-endline*.* p-jctrlsym*.* p-chartoken.* p-lineend.*
+## ptex-prntknj.test
+EXTRA_DIST += ptexdir/tests/printkanji.tex \
+ ptexdir/tests/pknj-s.log ptexdir/tests/pknj-e.log \
+ ptexdir/tests/p-pknj-sa.typ ptexdir/tests/p-pknj-ea.typ
+DISTCLEANFILES += p-pknj-*.*
+## ptex-ucs.test
+EXTRA_DIST += ptexdir/tests/ucs.tex ptexdir/tests/ucs-s.log \
+ ptexdir/tests/ucs-e.log
+DISTCLEANFILES += p-ucs*.*
+
ptrip.diffs: ptex$(EXEEXT) dvitype$(EXEEXT) pltotf$(EXEEXT) tftopl$(EXEEXT)
$(triptrap_diffs) $@
.PHONY: ptrip-clean
ptrip-clean:
rm -rf ptripdir
-DISTCLEANFILES += ptrip.diffs
-
# (end of ptex.am)
diff --git a/Build/source/texk/web2c/ptexdir/ptex-base.ch b/Build/source/texk/web2c/ptexdir/ptex-base.ch
index 6b77387d58e..36496b2e4b2 100644
--- a/Build/source/texk/web2c/ptexdir/ptex-base.ch
+++ b/Build/source/texk/web2c/ptexdir/ptex-base.ch
@@ -74,6 +74,8 @@
% (2022-10-24) HY pTeX p4.1.0 Add new syntax \font [in jis/ucs].
% New primitives: \tojis, \ptextracingfonts and \ptexfontname.
% (2023-09-17) HY pTeX p4.1.1 Support more than 256 different glue/kern.
+% (2024-09-22) HK pTeX p4.1.2 Enable to get character codes
+% from control symbols by using backquote.
@x
% Here is TeX material that gets inserted after \input webmac
@@ -88,8 +90,8 @@
@y
@d pTeX_version=4
@d pTeX_minor_version=1
-@d pTeX_revision==".1"
-@d pTeX_version_string=='-p4.1.1' {current \pTeX\ version}
+@d pTeX_revision==".2"
+@d pTeX_version_string=='-p4.1.2' {current \pTeX\ version}
@#
@d pTeX_banner=='This is pTeX, Version 3.141592653',pTeX_version_string
@d pTeX_banner_k==pTeX_banner
@@ -1305,10 +1307,11 @@ for k:=0 to 255 do
begin cat_code(k):=other_char; kcat_code(k):=other_kchar;
math_code(k):=hi(k); sf_code(k):=1000;
auto_xsp_code(k):=0;
+ inhibit_xsp_type(k):=0; kinsoku_type(k):=0;
end;
for k:=0 to 1023 do
- begin inhibit_xsp_code(k):=0; inhibit_xsp_type(k):=0;
- kinsoku_code(k):=0; kinsoku_type(k):=0;
+ begin inhibit_xsp_code(k):=0;
+ kinsoku_code(k):=0;
end;
@z
@x [17.232] l.4971 - pTeX: initialize cat_code, cur_jfont, cur_tfont
@@ -1523,6 +1526,9 @@ primitive("ptextracingfonts",assign_int,int_base+ptex_tracing_fonts_code);@/
@d v_offset==dimen_par(v_offset_code)
@d t_baseline_shift==dimen_par(t_baseline_shift_code)
@d y_baseline_shift==dimen_par(y_baseline_shift_code)
+@#
+@d enc_jis=1
+@d enc_ucs=2
@z
@x l.5542 - pTeX:
@@ -1660,8 +1666,8 @@ procedure print_font_dir_and_enc(f:internal_font_number);
begin
if font_dir[f]=dir_tate then print("/TATE")
else if font_dir[f]=dir_yoko then print("/YOKO");
- if font_enc[f]=2 then print("+Unicode")
- else if font_enc[f]=1 then print("+JIS");
+ if font_enc[f]=enc_ucs then print("+Unicode")
+ else if font_enc[f]=enc_jis then print("+JIS");
end;
@z
@@ -3324,9 +3330,9 @@ if jfm_flag<>dir_default then
for k:=ctype_base[f] to ctype_base[f]+nt-1 do
begin
fget; read_twentyfourx(cx);
- if jfm_enc=2 then {Unicode TFM}
+ if jfm_enc=enc_ucs then {Unicode TFM}
font_info[k].hh.rh:=toDVI(fromUCS(cx))
- else if jfm_enc=1 then {JIS-encoded TFM}
+ else if jfm_enc=enc_jis then {JIS-encoded TFM}
font_info[k].hh.rh:=toDVI(fromJIS(cx))
else
font_info[k].hh.rh:=tokanji(cx); {|kchar_code|}
@@ -3584,9 +3590,9 @@ continue:
end;
p:=link(p);
jc:=KANJI(info(p));
- if font_enc[f]=2 then {Unicode TFM}
+ if font_enc[f]=enc_ucs then {Unicode TFM}
jc:=toUCS(jc)
- else if font_enc[f]=1 then {JIS-encoded TFM}
+ else if font_enc[f]=enc_jis then {JIS-encoded TFM}
begin if toJIS(jc)=0 then char_warning_jis(f,jc);
jc:=toJIS(jc); end
else
@@ -7075,8 +7081,8 @@ exit:end;
@ @<Scan the font encoding specification@>=
begin jfm_enc:=0;
if scan_keyword_noexpand("in") then
- if scan_keyword_noexpand("jis") then jfm_enc:=1
- else if scan_keyword_noexpand("ucs") then jfm_enc:=2
+ if scan_keyword_noexpand("jis") then jfm_enc:=enc_jis
+ else if scan_keyword_noexpand("ucs") then jfm_enc:=enc_ucs
else begin
print_err("Unknown TFM encoding");
@.Unknown TFM encoding@>
diff --git a/Build/source/texk/web2c/ptexdir/ptex-ctrlsym.test b/Build/source/texk/web2c/ptexdir/ptex-ctrlsym.test
new file mode 100755
index 00000000000..ee0c5b88e8a
--- /dev/null
+++ b/Build/source/texk/web2c/ptexdir/ptex-ctrlsym.test
@@ -0,0 +1,77 @@
+#! /bin/sh -vx
+# Copyright 2024 Japanese TeX Development Community <issue@texjp.org>
+# You may freely use, modify and/or distribute this file.
+
+BinDir=${BinDir:-.}
+ExeExt=${ExeExt:-}
+_ptex=$BinDir/ptex$ExeExt
+_updvitype=$BinDir/updvitype$ExeExt
+
+LC_ALL=C; export LC_ALL; LANGUAGE=C; export LANGUAGE
+
+TEXMFCNF=$srcdir/../kpathsea;export TEXMFCNF
+TEXINPUTS=".;$srcdir/tests"; export TEXINPUTS
+TEXFONTS=".;$srcdir//tests"; export TEXFONTS
+TEXFORMATS=.; export TEXFORMATS
+
+rm -f p-ctrlsym.tex p-endline*.* p-jctrlsym*.* p-chartoken.* p-lineend.*
+
+if test ! -f ./p-basic.tex; then
+ cp $srcdir/tests/dump-basic.tex ./p-basic.tex
+fi
+if test ! -f ./p-basic-euc.fmt; then
+ $_ptex -ini -kanji-internal=euc -jobname=p-basic-euc p-basic || exit 2
+fi
+
+# ctrlsym.tex
+$LN_S $srcdir/ptexdir/tests/ctrlsym.tex ./p-ctrlsym.tex
+$_ptex -fmt=p-basic-euc p-ctrlsym || exit 2
+
+sed 1d p-ctrlsym.log >p-ctrlsym.out
+
+diff $srcdir/ptexdir/tests/ctrlsym.log p-ctrlsym.out || exit 3
+
+
+# endlinchar.tex
+$LN_S $srcdir/ptexdir/tests/endlinechar.tex ./p-endline.tex
+$_ptex -fmt=p-basic-euc -interaction=batchmode p-endline.tex || :
+
+sed 1d p-endline.log >p-endline.out
+
+diff $srcdir/ptexdir/tests/endline.log p-endline.out || exit 4
+
+$_updvitype ./p-endline.dvi >ptests/xp-endline.typ
+sed "1d;s/TeX output 20[23].\...\...:.*'/TeX output 20XX.XX.XX'/" ptests/xp-endline.typ >ptests/xp-endlinea.typ
+diff $srcdir/ptexdir/tests/p-endlinea.typ ptests/xp-endlinea.typ || exit 5
+
+
+# control-symbol.tex
+$LN_S $srcdir/ptexdir/tests/control-symbol.tex ./p-jctrlsym.tex
+$_ptex -fmt=p-basic-euc -interaction=batchmode p-jctrlsym.tex || :
+
+sed 1d p-jctrlsym.log >p-jctrlsym.out
+
+diff $srcdir/ptexdir/tests/jctrlsym.log p-jctrlsym.out || exit 6
+
+$_updvitype ./p-jctrlsym.dvi >ptests/xp-jctrlsym.typ
+sed "1d;s/TeX output 20[23].\...\...:.*'/TeX output 20XX.XX.XX'/" ptests/xp-jctrlsym.typ >ptests/xp-jctrlsyma.typ
+diff $srcdir/ptexdir/tests/p-jctrlsyma.typ ptests/xp-jctrlsyma.typ || exit 7
+
+
+# let_char_token.tex
+$LN_S $srcdir/ptexdir/tests/let_char_token.tex ./p-chartoken.tex
+$_ptex -fmt=p-basic-euc -interaction=batchmode p-chartoken.tex || :
+
+sed 1d p-chartoken.log >p-chartoken.out
+
+diff $srcdir/ptexdir/tests/chartoken.log p-chartoken.out || exit 8
+
+
+# ptexlineendmode.tex
+$LN_S $srcdir/ptexdir/tests/ptexlineendmode.tex ./p-lineend.tex
+$_ptex -fmt=p-basic-euc -kanji=utf8 -interaction=batchmode p-lineend.tex || :
+
+$_updvitype -kanji=utf8 ./p-lineend.dvi >ptests/xp-lineend.typ
+sed "1d;s/TeX output 20[23].\...\...:.*'/TeX output 20XX.XX.XX'/" ptests/xp-lineend.typ >ptests/xp-lineenda.typ
+diff $srcdir/ptexdir/tests/p-lineenda.typ ptests/xp-lineenda.typ || exit 9
+
diff --git a/Build/source/texk/web2c/ptexdir/ptex-prntknj.test b/Build/source/texk/web2c/ptexdir/ptex-prntknj.test
new file mode 100755
index 00000000000..0f2c50f96fd
--- /dev/null
+++ b/Build/source/texk/web2c/ptexdir/ptex-prntknj.test
@@ -0,0 +1,48 @@
+#! /bin/sh -vx
+# Copyright 2024 Japanese TeX Development Community <issue@texjp.org>
+# You may freely use, modify and/or distribute this file.
+
+BinDir=${BinDir:-.}
+ExeExt=${ExeExt:-}
+_ptex=$BinDir/ptex$ExeExt
+_updvitype=$BinDir/updvitype$ExeExt
+
+LC_ALL=C; export LC_ALL; LANGUAGE=C; export LANGUAGE
+
+TEXMFCNF=$srcdir/../kpathsea;export TEXMFCNF
+OFMFONTS=".;$srcdir/tests"; export OFMFONTS
+TFMFONTS=".;$srcdir//tests"; export TFMFONTS
+TEXINPUTS=".;$srcdir/tests"; export TEXINPUTS
+TEXFORMATS=.; export TEXFORMATS
+
+rm -f p-base.* p-ucs*.*
+$LN_S $srcdir/ptexdir/tests/printkanji.tex ./printkanji.tex
+
+if test ! -f ./p-basic.tex; then
+ cp $srcdir/tests/dump-basic.tex ./p-basic.tex
+fi
+if test ! -f ./p-basic-sjis.fmt; then
+ $_ptex -ini -kanji-internal=sjis -jobname=p-basic-sjis p-basic || exit 1
+fi
+if test ! -f ./p-basic-euc.fmt; then
+ $_ptex -ini -kanji-internal=euc -jobname=p-basic-euc p-basic || exit 2
+fi
+
+$_ptex -fmt=p-basic-sjis -kanji-internal=sjis -jobname=p-pknj-s -interaction=batchmode printkanji || :
+$_ptex -fmt=p-basic-euc -kanji-internal=euc -jobname=p-pknj-e -interaction=batchmode printkanji || :
+
+sed 1d p-pknj-s.log >p-pknj-s.out
+sed 1d p-pknj-e.log >p-pknj-e.out
+
+diff $srcdir/ptexdir/tests/pknj-s.log p-pknj-s.out || exit 10
+diff $srcdir/ptexdir/tests/pknj-e.log p-pknj-e.out || exit 11
+
+$_updvitype ./p-pknj-s.dvi >ptests/xp-pknj-s.typ
+$_updvitype ./p-pknj-e.dvi >ptests/xp-pknj-e.typ
+
+sed "1d;s/TeX output 20[23].\...\...:.*'/TeX output 20XX.XX.XX'/" ptests/xp-pknj-s.typ >ptests/xp-pknj-sa.typ
+sed "1d;s/TeX output 20[23].\...\...:.*'/TeX output 20XX.XX.XX'/" ptests/xp-pknj-e.typ >ptests/xp-pknj-ea.typ
+
+diff $srcdir/ptexdir/tests/p-pknj-sa.typ ptests/xp-pknj-sa.typ || exit 20
+diff $srcdir/ptexdir/tests/p-pknj-ea.typ ptests/xp-pknj-ea.typ || exit 21
+
diff --git a/Build/source/texk/web2c/ptexdir/ptex-ucs.test b/Build/source/texk/web2c/ptexdir/ptex-ucs.test
new file mode 100755
index 00000000000..bef8ba44910
--- /dev/null
+++ b/Build/source/texk/web2c/ptexdir/ptex-ucs.test
@@ -0,0 +1,37 @@
+#! /bin/sh -vx
+# Copyright 2024 Japanese TeX Development Community <issue@texjp.org>
+# You may freely use, modify and/or distribute this file.
+
+BinDir=${BinDir:-.}
+ExeExt=${ExeExt:-}
+_ptex=$BinDir/ptex$ExeExt
+
+LC_ALL=C; export LC_ALL; LANGUAGE=C; export LANGUAGE
+
+TEXMFCNF=$srcdir/../kpathsea;export TEXMFCNF
+OFMFONTS=".;$srcdir/tests"; export OFMFONTS
+TEXINPUTS=".;$srcdir/tests"; export TEXINPUTS
+TEXFORMATS=.; export TEXFORMATS
+
+rm -f p-ucs*.*
+$LN_S $srcdir/ptexdir/tests/ucs.tex ./p-ucs.tex
+
+if test ! -f ./p-basic.tex; then
+ cp $srcdir/tests/dump-basic.tex ./p-basic.tex
+fi
+if test ! -f ./p-basic-sjis.fmt; then
+ $_ptex -ini -kanji-internal=sjis -jobname=p-basic-sjis p-basic || exit 1
+fi
+if test ! -f ./p-basic-euc.fmt; then
+ $_ptex -ini -kanji-internal=euc -jobname=p-basic-euc p-basic || exit 2
+fi
+
+$_ptex -fmt=p-basic-sjis -kanji-internal=sjis -jobname=p-ucs-s -interaction=batchmode p-ucs || exit 4
+$_ptex -fmt=p-basic-euc -kanji-internal=euc -jobname=p-ucs-e -interaction=batchmode p-ucs || exit 5
+
+sed 1d p-ucs-s.log >p-ucs-s.out
+sed 1d p-ucs-e.log >p-ucs-e.out
+
+diff $srcdir/ptexdir/tests/ucs-s.log p-ucs-s.out || exit 10
+diff $srcdir/ptexdir/tests/ucs-e.log p-ucs-e.out || exit 11
+
diff --git a/Build/source/texk/web2c/ptexdir/ptex_version.h b/Build/source/texk/web2c/ptexdir/ptex_version.h
index 13e0f1467b3..247aabb113d 100644
--- a/Build/source/texk/web2c/ptexdir/ptex_version.h
+++ b/Build/source/texk/web2c/ptexdir/ptex_version.h
@@ -1 +1 @@
-#define PTEX_VERSION "p4.1.1"
+#define PTEX_VERSION "p4.1.2"
diff --git a/Build/source/texk/web2c/ptexdir/tests/chartoken.log b/Build/source/texk/web2c/ptexdir/tests/chartoken.log
new file mode 100644
index 00000000000..e834c9cf8a3
--- /dev/null
+++ b/Build/source/texk/web2c/ptexdir/tests/chartoken.log
@@ -0,0 +1,41 @@
+ restricted \write18 enabled.
+ %&-line parsing enabled.
+**p-chartoken.tex
+(./p-chartoken.tex
+\x=\count26
+
+! Improper alphabetic or KANJI constant.
+<to be read again>
+ \aaa
+\T #1->\x =`#1
+ \message {\the \x }
+l.6 \def\aaa{西}\T{\aaa}
+ % err
+A one-character control sequence belongs after a ` mark.
+So I'm essentially inserting \0 here.
+
+48
+! Improper alphabetic or KANJI constant.
+<to be read again>
+ \AAA
+\T #1->\x =`#1
+ \message {\the \x }
+l.7 \def\AAA{s}\T{\AAA}
+ % err
+A one-character control sequence belongs after a ` mark.
+So I'm essentially inserting \0 here.
+
+Missing character: There is no s in font nullfont!
+48
+! Improper alphabetic or KANJI constant.
+<to be read again>
+ \ccc
+\T #1->\x =`#1
+ \message {\the \x }
+l.8 \let\ccc=西\relax\T{\ccc}
+ % err
+A one-character control sequence belongs after a ` mark.
+So I'm essentially inserting \0 here.
+
+48 126 97 46273 46273 [1] )
+Output written on p-chartoken.dvi (1 page, 152 bytes).
diff --git a/Build/source/texk/web2c/ptexdir/tests/control-symbol.tex b/Build/source/texk/web2c/ptexdir/tests/control-symbol.tex
index f8127546885..be05906602d 100644
--- a/Build/source/texk/web2c/ptexdir/tests/control-symbol.tex
+++ b/Build/source/texk/web2c/ptexdir/tests/control-symbol.tex
@@ -1,5 +1,14 @@
\scrollmode
% plain TeX ソース
+\font\cmr=cmr10\cmr
+\ifx\kanjiskip\undefined\else
+ \ifnum\jis"2121="3000
+ \jfont\jpy=umin10 at 10pt\jpy
+ \else
+ \jfont\jpy=min10 at 10pt\jpy
+ \fi
+\fi
+
\def\+{A}
\def\+{A}
\def\X{\+}
@@ -95,7 +104,5 @@
4. \】 ◇, \@ ◇
-
\end
-
diff --git a/Build/source/texk/web2c/ptexdir/tests/ctrlsym.log b/Build/source/texk/web2c/ptexdir/tests/ctrlsym.log
new file mode 100644
index 00000000000..a74fd6faac3
--- /dev/null
+++ b/Build/source/texk/web2c/ptexdir/tests/ctrlsym.log
@@ -0,0 +1,11 @@
+ restricted \write18 enabled.
+ %&-line parsing enabled.
+**p-ctrlsym
+(./p-ctrlsym.tex
+A: 49379
+
+\雪 ->snow
+B: 49379. 雪\雪snow
+macro:->snow.
+ )
+No pages of output.
diff --git a/Build/source/texk/web2c/ptexdir/tests/ctrlsym.tex b/Build/source/texk/web2c/ptexdir/tests/ctrlsym.tex
new file mode 100644
index 00000000000..d84a541707b
--- /dev/null
+++ b/Build/source/texk/web2c/ptexdir/tests/ctrlsym.tex
@@ -0,0 +1,36 @@
+% Copyright 2024 Japanese TeX Development Community <issue@texjp.org>
+% You may freely use, modify and/or distribute this file.
+%
+\ifx\fmtname\undefined
+ \input basic
+ \def\fmtname{basic}
+ \expandafter\dump
+\fi
+%==================
+\begingroup % Just there to minimize tracing output
+ \tracingmacros=2
+ \tracingonline=1
+%--
+ \newlinechar=10
+ \count255=0
+ \count255=`雪 \relax
+ \message{^^JA: \the\count255^^J}
+ \def\雪{snow}
+ \count255=`\雪 \relax
+ \message{B: \the\count255. 雪\string\雪 \雪^^J}
+ \message{\expandafter\meaning\csname 雪\endcsname.^^J}
+ \ifx\Uchar\undefined\else\message{\expandafter\meaning\csname \Uchar"96EA\endcsname.^^J}\fi%
+ \ifnum\jis"2121="3000
+ \count4095=0
+ \count4095=`🍌 \relax
+ \message{^^JC: \the\count4095^^J}
+ \def\🍌{banana}
+ \count4095=`\🍌 \relax
+ \message{D: \the\count4095. 🍌\string\🍌 \🍌^^J}
+ \message{\expandafter\meaning\csname 🍌\endcsname.^^J}
+ \ifx\Uchar\undefined\else\message{\expandafter\meaning\csname \Uchar"1F34C\endcsname.^^J}\fi%
+ \fi
+%--
+ \setbox0=\vbox{} % no dvi/xdv/pdf output
+\endgroup
+\end
diff --git a/Build/source/texk/web2c/ptexdir/tests/endline.log b/Build/source/texk/web2c/ptexdir/tests/endline.log
new file mode 100644
index 00000000000..517be160e49
--- /dev/null
+++ b/Build/source/texk/web2c/ptexdir/tests/endline.log
@@ -0,0 +1,24 @@
+ restricted \write18 enabled.
+ %&-line parsing enabled.
+**p-endline.tex
+(./p-endline.tex
+! Undefined control sequence.
+l.29 7a\^^e3
+ ^^81^^82b\^^e3^^81^^82
+The control sequence at the end of the top line
+of your error message was never \def'ed. If you have
+misspelled it (e.g., `\hobx'), type `I' and the correct
+spelling (e.g., `I\hbox'). Otherwise just continue,
+and I'll forget about whatever was undefined.
+
+! Undefined control sequence.
+l.29 7a\^^e3^^81^^82b\^^e3
+ ^^81^^82
+The control sequence at the end of the top line
+of your error message was never \def'ed. If you have
+misspelled it (e.g., `\hobx'), type `I' and the correct
+spelling (e.g., `I\hbox'). Otherwise just continue,
+and I'll forget about whatever was undefined.
+
+[1] )
+Output written on p-endline.dvi (1 page, 232 bytes).
diff --git a/Build/source/texk/web2c/ptexdir/tests/endlinechar.tex b/Build/source/texk/web2c/ptexdir/tests/endlinechar.tex
index ef59b1fc886..5dc8de812e7 100644
--- a/Build/source/texk/web2c/ptexdir/tests/endlinechar.tex
+++ b/Build/source/texk/web2c/ptexdir/tests/endlinechar.tex
@@ -1,23 +1,23 @@
-\font\x=ecrm1000 \x
+\font\x=ec-lmr10 \x
\parindent0pt\noautoxspacing
\output{\shipout\box255}
-\def\あ{ア}
-\def\,{イ}
+\def\あ{PPP}
+\def\,{QQQ}
1a\,
-% --> 1aイ
+% --> 1aQQQ
2a\あ
-% --> 2aア
+% --> 2aPPP
\endlinechar=-1\relax
3a\,
-% --> 3aイ
+% --> 3aQQQ
% previous pTeX, upTeX: Undefined cs.
4a\あ
-% --> 4aア
+% --> 4aPPP
% prev. pTeX: Undefined cs.
5a,
@@ -36,18 +36,22 @@
\catcode"81=11\relax
\catcode"82=11\relax
8a\^^e3^^81^^82
- % --> 8aア
+ % (p3.10.0-u1.27) --> 8aPPP
+ % (p4.0.0-u1.27) --> エラー 8a
\kcatcode`あ=15\relax
9a\あ
- % == 9a\^^e3^^81^^82. --> 9aア
- \def\^^e3^^81^^82{ウ}
+ % == 9a\^^e3^^81^^82.
+ % (p3.10.0-u1.27) --> 9aPPP
+ % (p4.0.0-u1.27) --> エラー 9a
+ \def\^^e3^^81^^82{RRR}
10a\あ b\あ
- % == 10a\^^e3^^81^^82 b\^^e3^^81^^82. --> 10aウbウ
+ % == 10a\^^e3^^81^^82 b\^^e3^^81^^82. --> 10aRRRbRRR
\kcatcode12354=16\relax
11a\あ b\あ
- % == 10a\^^e3^^81^^82 b\^^e3^^81^^82. --> 11aウbウ
+ % == 11a\^^e3^^81^^82 b\^^e3^^81^^82.
+ % (p3.10.0-u1.27) --> 11aRRRbRRR
+ % (p4.0.0-u1.27) --> 11aPPPbPPP
\fi
\bye
-
diff --git a/Build/source/texk/web2c/ptexdir/tests/jctrlsym.log b/Build/source/texk/web2c/ptexdir/tests/jctrlsym.log
new file mode 100644
index 00000000000..a1e190a80df
--- /dev/null
+++ b/Build/source/texk/web2c/ptexdir/tests/jctrlsym.log
@@ -0,0 +1,42 @@
+ restricted \write18 enabled.
+ %&-line parsing enabled.
+**p-jctrlsym.tex
+(./p-jctrlsym.tex
+■control symbol
+> \X=macro:
+->\+.
+l.27 \show\X
+ \relax\message{(\meaning\X)}
+
+(macro:->\+)
+> \Y=macro:
+->\+.
+l.28 \show\Y
+ \relax\message{(\meaning\Y)}
+
+(macro:->\+) ■control word
+> \X=macro:
+->\+ .
+l.39 \show\X
+ \relax\message{(\meaning\X)}
+
+(macro:->\+ )
+> \Y=macro:
+->\+ .
+l.40 \show\Y
+ \relax\message{(\meaning\Y)}
+
+(macro:->\+ ) ■control word
+> \X=macro:
+->\ABC .
+l.48 \show\X
+ \relax\message{(\meaning\X)}
+
+(macro:->\ABC )
+> \Y=macro:
+->\あいう .
+l.49 \show\Y
+ \relax\message{(\meaning\Y)}
+
+(macro:->\あいう ) ■行末の和文コントロールシンボル [1] )
+Output written on p-jctrlsym.dvi (1 page, 376 bytes).
diff --git a/Build/source/texk/web2c/ptexdir/tests/p-endlinea.typ b/Build/source/texk/web2c/ptexdir/tests/p-endlinea.typ
new file mode 100644
index 00000000000..31b9af3a8c3
--- /dev/null
+++ b/Build/source/texk/web2c/ptexdir/tests/p-endlinea.typ
@@ -0,0 +1,68 @@
+Options selected:
+ Starting page = *
+ Maximum number of pages = 1000000
+ Output level = 4 (the works)
+ Resolution = 300.00000000 pixels per inch
+numerator/denominator=25400000/473628672
+magnification=1000; 0.00006334 pixels per DVI unit
+' TeX output 20XX.XX.XX'
+Postamble starts at byte 167.
+maxv=42152922, maxh=30785863, maxstackdepth=1, totalpages=1
+Font 0: ec-lmr10---loaded at size 655360 DVI units
+
+42: beginning of page 1
+87: down3 655360 v:=0+655360=655360, vv:=42
+91: push
+level 0:(h=0,v=655360,w=0,x=0,y=0,z=0,hh=0,vv=42)
+92: fntdef1 0: ec-lmr10
+116: fntnum0 current font is ec-lmr10
+117: setchar49 ("31) h:=0+327680=327680, hh:=21
+118: setchar97 ("61) h:=327680+327680=655360, hh:=42
+119: setchar81 ("51) h:=655360+509738=1165098, hh:=74
+120: setchar81 ("51) h:=1165098+509738=1674836, hh:=106
+121: setchar81 ("51) h:=1674836+509738=2184574, hh:=138
+[1aQQQ]
+122: pop
+level 0:(h=0,v=655360,w=0,x=0,y=0,z=0,hh=0,vv=42)
+123: y3 786432 v:=655360+786432=1441792, vv:=91
+127: push
+level 0:(h=0,v=1441792,w=0,x=0,y=786432,z=0,hh=0,vv=91)
+128: setchar50 ("32) h:=0+327680=327680, hh:=21
+129: setchar97 ("61) h:=327680+327680=655360, hh:=42
+130: setchar80 ("50) h:=655360+446005=1101365, hh:=70
+131: setchar80 ("50) h:=1101365+446005=1547370, hh:=98
+132: setchar80 ("50) h:=1547370+446005=1993375, hh:=126
+[2aPPP]
+133: pop
+level 0:(h=0,v=1441792,w=0,x=0,y=786432,z=0,hh=0,vv=91)
+134: y0 786432 v:=1441792+786432=2228224, vv:=141
+135: push
+level 0:(h=0,v=2228224,w=0,x=0,y=786432,z=0,hh=0,vv=141)
+136: setchar51 ("33) h:=0+327680=327680, hh:=21
+137: setchar97 ("61) h:=327680+327680=655360, hh:=42
+138: setchar81 ("51) h:=655360+509738=1165098, hh:=74
+139: setchar81 ("51) h:=1165098+509738=1674836, hh:=106
+140: setchar81 ("51) h:=1674836+509738=2184574, hh:=138
+141: setchar52 ("34) h:=2184574+327680=2512254, hh:=159
+142: setchar97 ("61) h:=2512254+327680=2839934, hh:=180
+143: setchar80 ("50) h:=2839934+446005=3285939, hh:=208
+144: setchar80 ("50) h:=3285939+446005=3731944, hh:=236
+145: setchar80 ("50) h:=3731944+446005=4177949, hh:=264
+146: setchar53 ("35) h:=4177949+327680=4505629, hh:=285
+147: setchar97 ("61) h:=4505629+327680=4833309, hh:=306
+148: setchar54 ("36) h:=4833309+327680=5160989, hh:=327
+149: setchar97 ("61) h:=5160989+327680=5488669, hh:=348
+150: setchar55 ("37) h:=5488669+327680=5816349, hh:=369
+151: setchar97 ("61) h:=5816349+327680=6144029, hh:=390
+[3aQQQ4aPPP5a6a7a]
+152: set1 129("81) h:=6144029+491520=6635549, hh:=421
+154: w2 -18205 h:=6635549-18205=6617344, hh:=420
+157: set1 130("82) h:=6617344+473301=7090645, hh:=450
+159: setchar98 ("62) h:=7090645+364085=7454730, hh:=473
+[b]
+160: set1 129("81) h:=7454730+491520=7946250, hh:=504
+162: w0 -18205 h:=7946250-18205=7928045, hh:=503
+163: set1 130("82) h:=7928045+473301=8401346, hh:=533
+165: pop
+level 0:(h=0,v=2228224,w=0,x=0,y=786432,z=0,hh=0,vv=141)
+166: eop
diff --git a/Build/source/texk/web2c/ptexdir/tests/p-jctrlsyma.typ b/Build/source/texk/web2c/ptexdir/tests/p-jctrlsyma.typ
new file mode 100644
index 00000000000..7f8a5eb1ca6
--- /dev/null
+++ b/Build/source/texk/web2c/ptexdir/tests/p-jctrlsyma.typ
@@ -0,0 +1,123 @@
+Options selected:
+ Starting page = *
+ Maximum number of pages = 1000000
+ Output level = 4 (the works)
+ Resolution = 300.00000000 pixels per inch
+numerator/denominator=25400000/473628672
+magnification=1000; 0.00006334 pixels per DVI unit
+' TeX output 20XX.XX.XX'
+Postamble starts at byte 293.
+maxv=43725786, maxh=30785863, maxstackdepth=2, totalpages=1
+Font 1: min10 (JFM)---loaded at size 655360 DVI units
+Font 0: cmr10---loaded at size 655360 DVI units
+
+42: beginning of page 1
+87: push
+level 0:(h=0,v=0,w=0,x=0,y=0,z=0,hh=0,vv=0)
+88: down3 -917504 v:=0-917504=-917504, vv:=-58
+92: pop
+level 0:(h=0,v=0,w=0,x=0,y=0,z=0,hh=0,vv=0)
+93: down4 42152922 v:=0+42152922=42152922, vv:=2670
+98: push
+level 0:(h=0,v=42152922,w=0,x=0,y=0,z=0,hh=0,vv=2670)
+99: down4 -41497562 v:=42152922-41497562=655360, vv:=42
+104: push
+level 1:(h=0,v=655360,w=0,x=0,y=0,z=0,hh=0,vv=42)
+105: right3 1310720 h:=0+1310720=1310720, hh:=83
+[ ]
+109: fntdef1 0: cmr10
+130: fntnum0 current font is cmr10
+131: setchar49 ("31) h:=1310720+327681=1638401, hh:=104
+132: setchar46 ("2E) h:=1638401+182045=1820446, hh:=116
+133: right3 291271 h:=1820446+291271=2111717, hh:=134
+[1. ]
+137: fntdef1 1: min10
+158: fntnum1 current font is min10
+159: set2 8737("2221) type=0 h:=2111717+630598=2742315, hh:=174
+162: set2 8574("217E) type=0 h:=2742315+630598=3372913, hh:=214
+165: set2 8484("2124) type=8 h:=3372913+231778=3604691, hh:=229
+168: right3 398820 h:=3604691+398820=4003511, hh:=254
+172: set2 8737("2221) type=0 h:=4003511+630598=4634109, hh:=294
+175: set2 8574("217E) type=0 h:=4634109+630598=5264707, hh:=334
+[∡ⅾℤ ∡ⅾ]
+178: pop
+level 1:(h=0,v=655360,w=0,x=0,y=0,z=0,hh=0,vv=42)
+179: y3 786432 v:=655360+786432=1441792, vv:=91
+183: push
+level 1:(h=0,v=1441792,w=0,x=0,y=786432,z=0,hh=0,vv=91)
+184: right3 1310720 h:=0+1310720=1310720, hh:=83
+[ ]
+188: fntnum0 current font is cmr10
+189: setchar50 ("32) h:=1310720+327681=1638401, hh:=104
+190: setchar46 ("2E) h:=1638401+182045=1820446, hh:=116
+191: right3 291271 h:=1820446+291271=2111717, hh:=134
+[2. ]
+195: fntnum1 current font is min10
+196: set2 8737("2221) type=0 h:=2111717+630598=2742315, hh:=174
+199: set2 8574("217E) type=0 h:=2742315+630598=3372913, hh:=214
+[∡ⅾ]
+202: fntnum0 current font is cmr10
+203: setchar44 ("2C) h:=3372913+182045=3554958, hh:=226
+204: right3 218453 h:=3554958+218453=3773411, hh:=239
+[, ]
+208: fntnum1 current font is min10
+209: set2 8737("2221) type=0 h:=3773411+630598=4404009, hh:=279
+212: set2 8574("217E) type=0 h:=4404009+630598=5034607, hh:=319
+[∡ⅾ]
+215: pop
+level 1:(h=0,v=1441792,w=0,x=0,y=786432,z=0,hh=0,vv=91)
+216: y0 786432 v:=1441792+786432=2228224, vv:=141
+217: push
+level 1:(h=0,v=2228224,w=0,x=0,y=786432,z=0,hh=0,vv=141)
+218: right3 1310720 h:=0+1310720=1310720, hh:=83
+[ ]
+222: fntnum0 current font is cmr10
+223: setchar51 ("33) h:=1310720+327681=1638401, hh:=104
+224: setchar46 ("2E) h:=1638401+182045=1820446, hh:=116
+225: right3 291271 h:=1820446+291271=2111717, hh:=134
+[3. ]
+229: fntnum1 current font is min10
+230: set2 8737("2221) type=0 h:=2111717+630598=2742315, hh:=174
+233: set2 8574("217E) type=0 h:=2742315+630598=3372913, hh:=214
+[∡ⅾ]
+236: fntnum0 current font is cmr10
+237: setchar44 ("2C) h:=3372913+182045=3554958, hh:=226
+238: w3 218453 h:=3554958+218453=3773411, hh:=239
+[, ]
+242: fntnum1 current font is min10
+243: set2 8737("2221) type=0 h:=3773411+630598=4404009, hh:=279
+246: w0 218453 h:=4404009+218453=4622462, hh:=293
+247: set2 8574("217E) type=0 h:=4622462+630598=5253060, hh:=333
+[∡ ⅾ]
+250: pop
+level 1:(h=0,v=2228224,w=0,x=0,y=786432,z=0,hh=0,vv=141)
+251: y0 786432 v:=2228224+786432=3014656, vv:=191
+252: push
+level 1:(h=0,v=3014656,w=0,x=0,y=786432,z=0,hh=0,vv=191)
+253: right3 1310720 h:=0+1310720=1310720, hh:=83
+[ ]
+257: fntnum0 current font is cmr10
+258: setchar52 ("34) h:=1310720+327681=1638401, hh:=104
+259: setchar46 ("2E) h:=1638401+182045=1820446, hh:=116
+260: right3 291271 h:=1820446+291271=2111717, hh:=134
+[4. ]
+264: fntnum1 current font is min10
+265: set2 8737("2221) type=0 h:=2111717+630598=2742315, hh:=174
+268: w3 218453 h:=2742315+218453=2960768, hh:=188
+272: set2 8574("217E) type=0 h:=2960768+630598=3591366, hh:=228
+[∡ ⅾ]
+275: fntnum0 current font is cmr10
+276: setchar44 ("2C) h:=3591366+182045=3773411, hh:=240
+277: w0 218453 h:=3773411+218453=3991864, hh:=253
+[, ]
+278: fntnum1 current font is min10
+279: set2 8737("2221) type=0 h:=3991864+630598=4622462, hh:=293
+282: w0 218453 h:=4622462+218453=4840915, hh:=307
+283: set2 8574("217E) type=0 h:=4840915+630598=5471513, hh:=347
+[∡ ⅾ]
+286: pop
+level 1:(h=0,v=3014656,w=0,x=0,y=786432,z=0,hh=0,vv=191)
+287: pop
+level 0:(h=0,v=42152922,w=0,x=0,y=0,z=0,hh=0,vv=2670)
+288: down3 1572864 v:=42152922+1572864=43725786, vv:=2770
+292: eop
diff --git a/Build/source/texk/web2c/ptexdir/tests/p-lineenda.typ b/Build/source/texk/web2c/ptexdir/tests/p-lineenda.typ
new file mode 100644
index 00000000000..368a57272ae
--- /dev/null
+++ b/Build/source/texk/web2c/ptexdir/tests/p-lineenda.typ
@@ -0,0 +1,295 @@
+Options selected:
+ Starting page = *
+ Maximum number of pages = 1000000
+ Output level = 4 (the works)
+ Resolution = 300.00000000 pixels per inch
+numerator/denominator=25400000/473628672
+magnification=1000; 0.00006334 pixels per DVI unit
+' TeX output 20XX.XX.XX'
+Postamble starts at byte 635.
+maxv=43725786, maxh=30785863, maxstackdepth=2, totalpages=1
+Font 1: min10 (JFM)---loaded at size 655360 DVI units
+Font 0: ec-lmr10---loaded at size 655360 DVI units
+
+42: beginning of page 1
+87: push
+level 0:(h=0,v=0,w=0,x=0,y=0,z=0,hh=0,vv=0)
+88: down3 -917504 v:=0-917504=-917504, vv:=-58
+92: pop
+level 0:(h=0,v=0,w=0,x=0,y=0,z=0,hh=0,vv=0)
+93: down4 42152922 v:=0+42152922=42152922, vv:=2670
+98: push
+level 0:(h=0,v=42152922,w=0,x=0,y=0,z=0,hh=0,vv=2670)
+99: down4 -41497562 v:=42152922-41497562=655360, vv:=42
+104: push
+level 1:(h=0,v=655360,w=0,x=0,y=0,z=0,hh=0,vv=42)
+105: right3 1310720 h:=0+1310720=1310720, hh:=83
+[ ]
+109: fntdef1 0: ec-lmr10
+133: fntnum0 current font is ec-lmr10
+134: setchar48 ("30) h:=1310720+327680=1638400, hh:=104
+[0]
+135: fntdef1 1: min10
+156: fntnum1 current font is min10
+157: set2 8737("2221) type=0 h:=1638400+630598=2268998, hh:=144
+160: set2 8737("2221) type=0 h:=2268998+630598=2899596, hh:=184
+163: set2 8574("217E) type=0 h:=2899596+630598=3530194, hh:=224
+166: set2 9250("2422) type=0 h:=3530194+630598=4160792, hh:=264
+169: set2 8574("217E) type=0 h:=4160792+630598=4791390, hh:=304
+172: set2 9252("2424) type=0 h:=4791390+630598=5421988, hh:=344
+175: set2 8737("2221) type=0 h:=5421988+630598=6052586, hh:=384
+178: set2 8574("217E) type=0 h:=6052586+630598=6683184, hh:=424
+181: set2 8737("2221) type=0 h:=6683184+630598=7313782, hh:=464
+184: set2 8574("217E) type=0 h:=7313782+630598=7944380, hh:=504
+187: set2 13377("3441) type=0 h:=7944380+630598=8574978, hh:=544
+190: set2 15226("3B7A) type=0 h:=8574978+630598=9205576, hh:=584
+[◆◆◇あ◇い◆◇◆◇漢字]
+193: fntnum0 current font is ec-lmr10
+194: setchar83 ("53) h:=9205576+364085=9569661, hh:=607
+195: setchar97 ("61) h:=9569661+327680=9897341, hh:=628
+196: right3 655360 h:=9897341+655360=10552701, hh:=668
+200: setchar112 ("70) h:=10552701+364085=10916786, hh:=691
+201: setchar52 ("34) h:=10916786+327680=11244466, hh:=712
+202: setchar46 ("2E) h:=11244466+182043=11426509, hh:=724
+203: setchar48 ("30) h:=11426509+327680=11754189, hh:=745
+204: setchar46 ("2E) h:=11754189+182043=11936232, hh:=757
+205: setchar48 ("30) h:=11936232+327680=12263912, hh:=778
+[Sa p4.0.0]
+206: fntnum1 current font is min10
+207: set2 12362("304A) type=0 h:=12263912+630598=12894510, hh:=818
+210: set2 14687("395F) type=0 h:=12894510+630598=13525108, hh:=858
+[以降]
+213: pop
+level 1:(h=0,v=655360,w=0,x=0,y=0,z=0,hh=0,vv=42)
+214: y3 786432 v:=655360+786432=1441792, vv:=91
+218: push
+level 1:(h=0,v=1441792,w=0,x=0,y=786432,z=0,hh=0,vv=91)
+219: right3 1310720 h:=0+1310720=1310720, hh:=83
+[ ]
+223: fntnum0 current font is ec-lmr10
+224: setchar49 ("31) h:=1310720+327680=1638400, hh:=104
+[1]
+225: fntnum1 current font is min10
+226: set2 8737("2221) type=0 h:=1638400+630598=2268998, hh:=144
+229: set2 8737("2221) type=0 h:=2268998+630598=2899596, hh:=184
+232: set2 8574("217E) type=0 h:=2899596+630598=3530194, hh:=224
+235: set2 9250("2422) type=0 h:=3530194+630598=4160792, hh:=264
+238: set2 8574("217E) type=0 h:=4160792+630598=4791390, hh:=304
+241: set2 9252("2424) type=0 h:=4791390+630598=5421988, hh:=344
+244: set2 8737("2221) type=0 h:=5421988+630598=6052586, hh:=384
+247: set2 8574("217E) type=0 h:=6052586+630598=6683184, hh:=424
+250: right3 218453 h:=6683184+218453=6901637, hh:=437
+254: set2 8737("2221) type=0 h:=6901637+630598=7532235, hh:=477
+257: set2 8574("217E) type=0 h:=7532235+630598=8162833, hh:=517
+260: set2 13377("3441) type=0 h:=8162833+630598=8793431, hh:=557
+263: set2 15226("3B7A) type=0 h:=8793431+630598=9424029, hh:=597
+[◆◆◇あ◇い◆◇ ◆◇漢字]
+266: fntnum0 current font is ec-lmr10
+267: setchar83 ("53) h:=9424029+364085=9788114, hh:=620
+268: setchar97 ("61) h:=9788114+327680=10115794, hh:=641
+269: right3 655360 h:=10115794+655360=10771154, hh:=682
+273: setchar112 ("70) h:=10771154+364085=11135239, hh:=705
+274: setchar51 ("33) h:=11135239+327680=11462919, hh:=726
+275: setchar46 ("2E) h:=11462919+182043=11644962, hh:=738
+276: setchar56 ("38) h:=11644962+327680=11972642, hh:=759
+277: setchar46 ("2E) h:=11972642+182043=12154685, hh:=771
+278: setchar50 ("32) h:=12154685+327680=12482365, hh:=792
+[Sa p3.8.2]
+279: fntnum1 current font is min10
+280: set2 12362("304A) type=0 h:=12482365+630598=13112963, hh:=832
+283: set2 14687("395F) type=0 h:=13112963+630598=13743561, hh:=872
+[以降]
+286: pop
+level 1:(h=0,v=1441792,w=0,x=0,y=786432,z=0,hh=0,vv=91)
+287: y0 786432 v:=1441792+786432=2228224, vv:=141
+288: push
+level 1:(h=0,v=2228224,w=0,x=0,y=786432,z=0,hh=0,vv=141)
+289: right3 1310720 h:=0+1310720=1310720, hh:=83
+[ ]
+293: fntnum0 current font is ec-lmr10
+294: setchar50 ("32) h:=1310720+327680=1638400, hh:=104
+[2]
+295: fntnum1 current font is min10
+296: set2 8737("2221) type=0 h:=1638400+630598=2268998, hh:=144
+299: set2 8737("2221) type=0 h:=2268998+630598=2899596, hh:=184
+302: set2 8574("217E) type=0 h:=2899596+630598=3530194, hh:=224
+305: w3 218453 h:=3530194+218453=3748647, hh:=237
+309: set2 9250("2422) type=0 h:=3748647+630598=4379245, hh:=277
+312: set2 8574("217E) type=0 h:=4379245+630598=5009843, hh:=317
+315: w0 218453 h:=5009843+218453=5228296, hh:=331
+316: set2 9252("2424) type=0 h:=5228296+630598=5858894, hh:=371
+319: set2 8737("2221) type=0 h:=5858894+630598=6489492, hh:=411
+322: set2 8574("217E) type=0 h:=6489492+630598=7120090, hh:=451
+325: set2 8737("2221) type=0 h:=7120090+630598=7750688, hh:=491
+328: set2 8574("217E) type=0 h:=7750688+630598=8381286, hh:=531
+331: set2 13377("3441) type=0 h:=8381286+630598=9011884, hh:=571
+334: set2 15226("3B7A) type=0 h:=9011884+630598=9642482, hh:=611
+[◆◆◇ あ◇ い◆◇◆◇漢字]
+337: fntnum0 current font is ec-lmr10
+338: setchar83 ("53) h:=9642482+364085=10006567, hh:=634
+339: setchar97 ("61) h:=10006567+327680=10334247, hh:=655
+[Sa]
+340: pop
+level 1:(h=0,v=2228224,w=0,x=0,y=786432,z=0,hh=0,vv=141)
+341: y0 786432 v:=2228224+786432=3014656, vv:=191
+342: push
+level 1:(h=0,v=3014656,w=0,x=0,y=786432,z=0,hh=0,vv=191)
+343: right3 1310720 h:=0+1310720=1310720, hh:=83
+347: setchar51 ("33) h:=1310720+327680=1638400, hh:=104
+[ 3]
+348: fntnum1 current font is min10
+349: set2 8737("2221) type=0 h:=1638400+630598=2268998, hh:=144
+352: set2 8737("2221) type=0 h:=2268998+630598=2899596, hh:=184
+355: set2 8574("217E) type=0 h:=2899596+630598=3530194, hh:=224
+358: w3 218453 h:=3530194+218453=3748647, hh:=237
+362: set2 9250("2422) type=0 h:=3748647+630598=4379245, hh:=277
+365: set2 8574("217E) type=0 h:=4379245+630598=5009843, hh:=317
+368: w0 218453 h:=5009843+218453=5228296, hh:=331
+369: set2 9252("2424) type=0 h:=5228296+630598=5858894, hh:=371
+372: set2 8737("2221) type=0 h:=5858894+630598=6489492, hh:=411
+375: set2 8574("217E) type=0 h:=6489492+630598=7120090, hh:=451
+378: w0 218453 h:=7120090+218453=7338543, hh:=465
+379: set2 8737("2221) type=0 h:=7338543+630598=7969141, hh:=505
+382: set2 8574("217E) type=0 h:=7969141+630598=8599739, hh:=545
+385: set2 13377("3441) type=0 h:=8599739+630598=9230337, hh:=585
+388: set2 15226("3B7A) type=0 h:=9230337+630598=9860935, hh:=625
+[◆◆◇ あ◇ い◆◇ ◆◇漢字]
+391: fntnum0 current font is ec-lmr10
+392: setchar83 ("53) h:=9860935+364085=10225020, hh:=648
+393: setchar97 ("61) h:=10225020+327680=10552700, hh:=669
+394: right3 655360 h:=10552700+655360=11208060, hh:=710
+398: setchar112 ("70) h:=11208060+364085=11572145, hh:=733
+399: setchar51 ("33) h:=11572145+327680=11899825, hh:=754
+400: setchar46 ("2E) h:=11899825+182043=12081868, hh:=766
+401: setchar56 ("38) h:=12081868+327680=12409548, hh:=787
+402: setchar46 ("2E) h:=12409548+182043=12591591, hh:=799
+403: setchar49 ("31) h:=12591591+327680=12919271, hh:=820
+[Sa p3.8.1]
+404: fntnum1 current font is min10
+405: set2 12362("304A) type=0 h:=12919271+630598=13549869, hh:=860
+408: set2 16688("4130) type=0 h:=13549869+630598=14180467, hh:=900
+[以前]
+411: pop
+level 1:(h=0,v=3014656,w=0,x=0,y=786432,z=0,hh=0,vv=191)
+412: y0 786432 v:=3014656+786432=3801088, vv:=241
+413: push
+level 1:(h=0,v=3801088,w=0,x=0,y=786432,z=0,hh=0,vv=241)
+414: right3 1310720 h:=0+1310720=1310720, hh:=83
+[ ]
+418: fntnum0 current font is ec-lmr10
+419: setchar52 ("34) h:=1310720+327680=1638400, hh:=104
+[4]
+420: fntnum1 current font is min10
+421: set2 8737("2221) type=0 h:=1638400+630598=2268998, hh:=144
+424: w3 218453 h:=2268998+218453=2487451, hh:=158
+428: set2 8737("2221) type=0 h:=2487451+630598=3118049, hh:=198
+431: set2 8574("217E) type=0 h:=3118049+630598=3748647, hh:=238
+434: set2 9250("2422) type=0 h:=3748647+630598=4379245, hh:=278
+437: set2 8574("217E) type=0 h:=4379245+630598=5009843, hh:=318
+440: w0 218453 h:=5009843+218453=5228296, hh:=331
+441: set2 9252("2424) type=0 h:=5228296+630598=5858894, hh:=371
+444: set2 8737("2221) type=0 h:=5858894+630598=6489492, hh:=411
+447: set2 8574("217E) type=0 h:=6489492+630598=7120090, hh:=451
+450: set2 8737("2221) type=0 h:=7120090+630598=7750688, hh:=491
+453: set2 8574("217E) type=0 h:=7750688+630598=8381286, hh:=531
+456: set2 13377("3441) type=0 h:=8381286+630598=9011884, hh:=571
+459: set2 15226("3B7A) type=0 h:=9011884+630598=9642482, hh:=611
+[◆ ◆◇あ◇ い◆◇◆◇漢字]
+462: fntnum0 current font is ec-lmr10
+463: setchar83 ("53) h:=9642482+364085=10006567, hh:=634
+464: setchar97 ("61) h:=10006567+327680=10334247, hh:=655
+[Sa]
+465: pop
+level 1:(h=0,v=3801088,w=0,x=0,y=786432,z=0,hh=0,vv=241)
+466: y0 786432 v:=3801088+786432=4587520, vv:=291
+467: push
+level 1:(h=0,v=4587520,w=0,x=0,y=786432,z=0,hh=0,vv=291)
+468: right3 1310720 h:=0+1310720=1310720, hh:=83
+472: setchar53 ("35) h:=1310720+327680=1638400, hh:=104
+[ 5]
+473: fntnum1 current font is min10
+474: set2 8737("2221) type=0 h:=1638400+630598=2268998, hh:=144
+477: w3 218453 h:=2268998+218453=2487451, hh:=158
+481: set2 8737("2221) type=0 h:=2487451+630598=3118049, hh:=198
+484: set2 8574("217E) type=0 h:=3118049+630598=3748647, hh:=238
+487: set2 9250("2422) type=0 h:=3748647+630598=4379245, hh:=278
+490: set2 8574("217E) type=0 h:=4379245+630598=5009843, hh:=318
+493: w0 218453 h:=5009843+218453=5228296, hh:=331
+494: set2 9252("2424) type=0 h:=5228296+630598=5858894, hh:=371
+497: set2 8737("2221) type=0 h:=5858894+630598=6489492, hh:=411
+500: set2 8574("217E) type=0 h:=6489492+630598=7120090, hh:=451
+503: w0 218453 h:=7120090+218453=7338543, hh:=465
+504: set2 8737("2221) type=0 h:=7338543+630598=7969141, hh:=505
+507: set2 8574("217E) type=0 h:=7969141+630598=8599739, hh:=545
+510: set2 13377("3441) type=0 h:=8599739+630598=9230337, hh:=585
+513: set2 15226("3B7A) type=0 h:=9230337+630598=9860935, hh:=625
+[◆ ◆◇あ◇ い◆◇ ◆◇漢字]
+516: fntnum0 current font is ec-lmr10
+517: setchar83 ("53) h:=9860935+364085=10225020, hh:=648
+518: setchar97 ("61) h:=10225020+327680=10552700, hh:=669
+[Sa]
+519: pop
+level 1:(h=0,v=4587520,w=0,x=0,y=786432,z=0,hh=0,vv=291)
+520: y0 786432 v:=4587520+786432=5373952, vv:=340
+521: push
+level 1:(h=0,v=5373952,w=0,x=0,y=786432,z=0,hh=0,vv=340)
+522: right3 1310720 h:=0+1310720=1310720, hh:=83
+526: setchar54 ("36) h:=1310720+327680=1638400, hh:=104
+[ 6]
+527: fntnum1 current font is min10
+528: set2 8737("2221) type=0 h:=1638400+630598=2268998, hh:=144
+531: w3 218453 h:=2268998+218453=2487451, hh:=158
+535: set2 8737("2221) type=0 h:=2487451+630598=3118049, hh:=198
+538: set2 8574("217E) type=0 h:=3118049+630598=3748647, hh:=238
+541: w0 218453 h:=3748647+218453=3967100, hh:=251
+542: set2 9250("2422) type=0 h:=3967100+630598=4597698, hh:=291
+545: set2 8574("217E) type=0 h:=4597698+630598=5228296, hh:=331
+548: w0 218453 h:=5228296+218453=5446749, hh:=345
+549: set2 9252("2424) type=0 h:=5446749+630598=6077347, hh:=385
+552: set2 8737("2221) type=0 h:=6077347+630598=6707945, hh:=425
+555: set2 8574("217E) type=0 h:=6707945+630598=7338543, hh:=465
+558: set2 8737("2221) type=0 h:=7338543+630598=7969141, hh:=505
+561: set2 8574("217E) type=0 h:=7969141+630598=8599739, hh:=545
+564: set2 13377("3441) type=0 h:=8599739+630598=9230337, hh:=585
+567: set2 15226("3B7A) type=0 h:=9230337+630598=9860935, hh:=625
+[◆ ◆◇ あ◇ い◆◇◆◇漢字]
+570: fntnum0 current font is ec-lmr10
+571: setchar83 ("53) h:=9860935+364085=10225020, hh:=648
+572: setchar97 ("61) h:=10225020+327680=10552700, hh:=669
+[Sa]
+573: pop
+level 1:(h=0,v=5373952,w=0,x=0,y=786432,z=0,hh=0,vv=340)
+574: y0 786432 v:=5373952+786432=6160384, vv:=390
+575: push
+level 1:(h=0,v=6160384,w=0,x=0,y=786432,z=0,hh=0,vv=390)
+576: right3 1310720 h:=0+1310720=1310720, hh:=83
+580: setchar55 ("37) h:=1310720+327680=1638400, hh:=104
+[ 7]
+581: fntnum1 current font is min10
+582: set2 8737("2221) type=0 h:=1638400+630598=2268998, hh:=144
+585: w3 218453 h:=2268998+218453=2487451, hh:=158
+589: set2 8737("2221) type=0 h:=2487451+630598=3118049, hh:=198
+592: set2 8574("217E) type=0 h:=3118049+630598=3748647, hh:=238
+595: w0 218453 h:=3748647+218453=3967100, hh:=251
+596: set2 9250("2422) type=0 h:=3967100+630598=4597698, hh:=291
+599: set2 8574("217E) type=0 h:=4597698+630598=5228296, hh:=331
+602: w0 218453 h:=5228296+218453=5446749, hh:=345
+603: set2 9252("2424) type=0 h:=5446749+630598=6077347, hh:=385
+606: set2 8737("2221) type=0 h:=6077347+630598=6707945, hh:=425
+609: set2 8574("217E) type=0 h:=6707945+630598=7338543, hh:=465
+612: w0 218453 h:=7338543+218453=7556996, hh:=479
+613: set2 8737("2221) type=0 h:=7556996+630598=8187594, hh:=519
+616: set2 8574("217E) type=0 h:=8187594+630598=8818192, hh:=559
+619: set2 13377("3441) type=0 h:=8818192+630598=9448790, hh:=599
+622: set2 15226("3B7A) type=0 h:=9448790+630598=10079388, hh:=639
+[◆ ◆◇ あ◇ い◆◇ ◆◇漢字]
+625: fntnum0 current font is ec-lmr10
+626: setchar83 ("53) h:=10079388+364085=10443473, hh:=662
+627: setchar97 ("61) h:=10443473+327680=10771153, hh:=683
+[Sa]
+628: pop
+level 1:(h=0,v=6160384,w=0,x=0,y=786432,z=0,hh=0,vv=390)
+629: pop
+level 0:(h=0,v=42152922,w=0,x=0,y=0,z=0,hh=0,vv=2670)
+630: down3 1572864 v:=42152922+1572864=43725786, vv:=2770
+634: eop
diff --git a/Build/source/texk/web2c/ptexdir/tests/p-pknj-ea.typ b/Build/source/texk/web2c/ptexdir/tests/p-pknj-ea.typ
new file mode 100644
index 00000000000..cf8d08f7715
--- /dev/null
+++ b/Build/source/texk/web2c/ptexdir/tests/p-pknj-ea.typ
@@ -0,0 +1,825 @@
+Options selected:
+ Starting page = *
+ Maximum number of pages = 1000000
+ Output level = 4 (the works)
+ Resolution = 300.00000000 pixels per inch
+numerator/denominator=25400000/473628672
+magnification=1000; 0.00006334 pixels per DVI unit
+' TeX output 20XX.XX.XX'
+Postamble starts at byte 1055.
+maxv=43725786, maxh=30785863, maxstackdepth=3, totalpages=1
+Font 1: min10 (JFM)---loaded at size 655360 DVI units
+Font 0: ec-lmr10---loaded at size 655360 DVI units
+
+42: beginning of page 50623
+87: push
+level 0:(h=0,v=0,w=0,x=0,y=0,z=0,hh=0,vv=0)
+88: down3 -917504 v:=0-917504=-917504, vv:=-58
+92: pop
+level 0:(h=0,v=0,w=0,x=0,y=0,z=0,hh=0,vv=0)
+93: down4 42152922 v:=0+42152922=42152922, vv:=2670
+98: push
+level 0:(h=0,v=42152922,w=0,x=0,y=0,z=0,hh=0,vv=2670)
+99: down4 -41497562 v:=42152922-41497562=655360, vv:=42
+104: push
+level 1:(h=0,v=655360,w=0,x=0,y=0,z=0,hh=0,vv=42)
+105: right3 1310720 h:=0+1310720=1310720, hh:=83
+[ ]
+109: fntdef1 0: ec-lmr10
+133: fntnum0 current font is ec-lmr10
+134: setchar65 ("41) h:=1310720+491520=1802240, hh:=114
+135: setchar58 ("3A) h:=1802240+182043=1984283, hh:=126
+136: w3 218453 h:=1984283+218453=2202736, hh:=140
+[A: ]
+140: set1 197("C5) h:=2202736+491520=2694256, hh:=171
+142: set1 191("BF) h:=2694256+491520=3185776, hh:=202
+144: w0 218453 h:=3185776+218453=3404229, hh:=216
+[ ]
+145: set1 197("C5) h:=3404229+491520=3895749, hh:=247
+147: set1 191("BF) h:=3895749+491520=4387269, hh:=278
+149: w0 218453 h:=4387269+218453=4605722, hh:=292
+[ ]
+150: fntdef1 1: min10
+171: fntnum1 current font is min10
+172: set2 17727("453F) type=0 h:=4605722+630598=5236320, hh:=332
+[䔿]
+175: pop
+level 1:(h=0,v=655360,w=0,x=0,y=0,z=0,hh=0,vv=42)
+176: y3 786432 v:=655360+786432=1441792, vv:=91
+180: push
+level 1:(h=0,v=1441792,w=0,x=0,y=786432,z=0,hh=0,vv=91)
+181: push
+level 2:(h=0,v=1441792,w=0,x=0,y=786432,z=0,hh=0,vv=91)
+182: set2 8739("2223) type=0 h:=0+630598=630598, hh:=40
+[∣]
+185: pop
+level 2:(h=0,v=1441792,w=0,x=0,y=786432,z=0,hh=0,vv=91)
+186: right3 630598 h:=0+630598=630598, hh:=40
+[ ]
+190: fntnum0 current font is ec-lmr10
+191: setchar92 ("5C) h:=630598+327680=958278, hh:=61
+192: setchar109 ("6D) h:=958278+546111=1504389, hh:=96
+193: setchar101 ("65) h:=1504389+291275=1795664, hh:=114
+194: setchar97 ("61) h:=1795664+327680=2123344, hh:=135
+195: setchar110 ("6E) h:=2123344+364085=2487429, hh:=158
+196: setchar105 ("69) h:=2487429+182043=2669472, hh:=170
+197: setchar110 ("6E) h:=2669472+364085=3033557, hh:=193
+198: setchar103 ("67) h:=3033557+327680=3361237, hh:=214
+199: setchar46 ("2E) h:=3361237+182043=3543280, hh:=226
+[\meaning.]
+200: pop
+level 1:(h=0,v=1441792,w=0,x=0,y=786432,z=0,hh=0,vv=91)
+201: y0 786432 v:=1441792+786432=2228224, vv:=141
+202: push
+level 1:(h=0,v=2228224,w=0,x=0,y=786432,z=0,hh=0,vv=141)
+203: right3 1310720 h:=0+1310720=1310720, hh:=83
+207: setchar109 ("6D) h:=1310720+546111=1856831, hh:=118
+208: setchar97 ("61) h:=1856831+327680=2184511, hh:=139
+209: setchar99 ("63) h:=2184511+291275=2475786, hh:=157
+210: setchar114 ("72) h:=2475786+256683=2732469, hh:=173
+211: setchar111 ("6F) h:=2732469+327680=3060149, hh:=194
+212: setchar58 ("3A) h:=3060149+182043=3242192, hh:=206
+213: setchar45 ("2D) h:=3242192+218453=3460645, hh:=220
+214: setchar62 ("3E) h:=3460645+509738=3970383, hh:=252
+215: setchar109 ("6D) h:=3970383+546111=4516494, hh:=287
+216: setchar97 ("61) h:=4516494+327680=4844174, hh:=308
+217: setchar99 ("63) h:=4844174+291275=5135449, hh:=326
+218: setchar114 ("72) h:=5135449+256683=5392132, hh:=342
+219: setchar111 ("6F) h:=5392132+327680=5719812, hh:=363
+220: setchar58 ("3A) h:=5719812+182043=5901855, hh:=375
+221: setchar45 ("2D) h:=5901855+218453=6120308, hh:=389
+222: setchar62 ("3E) h:=6120308+509738=6630046, hh:=421
+[ macro:->macro:->]
+223: set1 197("C5) h:=6630046+491520=7121566, hh:=452
+225: set1 191("BF) h:=7121566+491520=7613086, hh:=483
+227: w3 218453 h:=7613086+218453=7831539, hh:=496
+[ ]
+231: set1 197("C5) h:=7831539+491520=8323059, hh:=527
+233: set1 191("BF) h:=8323059+491520=8814579, hh:=558
+235: w0 218453 h:=8814579+218453=9033032, hh:=572
+[ ]
+236: fntnum1 current font is min10
+237: set2 17727("453F) type=0 h:=9033032+630598=9663630, hh:=612
+[䔿]
+240: pop
+level 1:(h=0,v=2228224,w=0,x=0,y=786432,z=0,hh=0,vv=141)
+241: y0 786432 v:=2228224+786432=3014656, vv:=191
+242: push
+level 1:(h=0,v=3014656,w=0,x=0,y=786432,z=0,hh=0,vv=191)
+243: right3 1310720 h:=0+1310720=1310720, hh:=83
+[ ]
+247: fntnum0 current font is ec-lmr10
+248: setchar109 ("6D) h:=1310720+546111=1856831, hh:=118
+249: setchar97 ("61) h:=1856831+327680=2184511, hh:=139
+250: setchar99 ("63) h:=2184511+291275=2475786, hh:=157
+251: setchar114 ("72) h:=2475786+256683=2732469, hh:=173
+252: setchar111 ("6F) h:=2732469+327680=3060149, hh:=194
+253: setchar58 ("3A) h:=3060149+182043=3242192, hh:=206
+254: setchar45 ("2D) h:=3242192+218453=3460645, hh:=220
+255: setchar62 ("3E) h:=3460645+509738=3970383, hh:=252
+[macro:->]
+256: set1 197("C5) h:=3970383+491520=4461903, hh:=283
+258: set1 191("BF) h:=4461903+491520=4953423, hh:=314
+260: w3 218453 h:=4953423+218453=5171876, hh:=328
+[ ]
+264: set1 197("C5) h:=5171876+491520=5663396, hh:=359
+266: set1 191("BF) h:=5663396+491520=6154916, hh:=390
+268: w0 218453 h:=6154916+218453=6373369, hh:=404
+[ ]
+269: fntnum1 current font is min10
+270: set2 17727("453F) type=0 h:=6373369+630598=7003967, hh:=444
+[䔿]
+273: pop
+level 1:(h=0,v=3014656,w=0,x=0,y=786432,z=0,hh=0,vv=191)
+274: y0 786432 v:=3014656+786432=3801088, vv:=241
+275: push
+level 1:(h=0,v=3801088,w=0,x=0,y=786432,z=0,hh=0,vv=241)
+276: push
+level 2:(h=0,v=3801088,w=0,x=0,y=786432,z=0,hh=0,vv=241)
+277: set2 8739("2223) type=0 h:=0+630598=630598, hh:=40
+[∣]
+280: pop
+level 2:(h=0,v=3801088,w=0,x=0,y=786432,z=0,hh=0,vv=241)
+281: right3 630598 h:=0+630598=630598, hh:=40
+[ ]
+285: fntnum0 current font is ec-lmr10
+286: setchar92 ("5C) h:=630598+327680=958278, hh:=61
+287: setchar106 ("6A) h:=958278+200245=1158523, hh:=74
+288: setchar111 ("6F) h:=1158523+327680=1486203, hh:=95
+289: setchar98 ("62) h:=1486203+364085=1850288, hh:=118
+290: setchar110 ("6E) h:=1850288+364085=2214373, hh:=141
+291: setchar97 ("61) h:=2214373+327680=2542053, hh:=162
+292: setchar109 ("6D) h:=2542053+546111=3088164, hh:=197
+293: setchar101 ("65) h:=3088164+291275=3379439, hh:=215
+294: setchar46 ("2E) h:=3379439+182043=3561482, hh:=227
+[\jobname.]
+295: pop
+level 1:(h=0,v=3801088,w=0,x=0,y=786432,z=0,hh=0,vv=241)
+296: y0 786432 v:=3801088+786432=4587520, vv:=291
+297: push
+level 1:(h=0,v=4587520,w=0,x=0,y=786432,z=0,hh=0,vv=291)
+298: push
+level 2:(h=0,v=4587520,w=0,x=0,y=786432,z=0,hh=0,vv=291)
+299: fntnum1 current font is min10
+300: set2 8739("2223) type=0 h:=0+630598=630598, hh:=40
+[∣]
+303: pop
+level 2:(h=0,v=4587520,w=0,x=0,y=786432,z=0,hh=0,vv=291)
+304: right3 630598 h:=0+630598=630598, hh:=40
+[ ]
+308: fntnum0 current font is ec-lmr10
+309: setchar111 ("6F) h:=630598+327680=958278, hh:=61
+310: setchar110 ("6E) h:=958278+364085=1322363, hh:=84
+311: setchar101 ("65) h:=1322363+291275=1613638, hh:=102
+312: setchar108 ("6C) h:=1613638+182043=1795681, hh:=114
+313: setchar101 ("65) h:=1795681+291275=2086956, hh:=132
+314: setchar116 ("74) h:=2086956+254870=2341826, hh:=148
+315: setchar116 ("74) h:=2341826+254870=2596696, hh:=164
+316: setchar101 ("65) h:=2596696+291275=2887971, hh:=182
+317: setchar114 ("72) h:=2887971+256683=3144654, hh:=198
+318: setchar46 ("2E) h:=3144654+182043=3326697, hh:=210
+[oneletter.]
+319: pop
+level 1:(h=0,v=4587520,w=0,x=0,y=786432,z=0,hh=0,vv=291)
+320: y0 786432 v:=4587520+786432=5373952, vv:=340
+321: push
+level 1:(h=0,v=5373952,w=0,x=0,y=786432,z=0,hh=0,vv=340)
+322: right3 1310720 h:=0+1310720=1310720, hh:=83
+326: setchar92 ("5C) h:=1310720+327680=1638400, hh:=104
+[ \]
+327: set1 196("C4) h:=1638400+491520=2129920, hh:=135
+329: setchar46 ("2E) h:=2129920+182043=2311963, hh:=147
+330: setchar92 ("5C) h:=2311963+327680=2639643, hh:=168
+[.\]
+331: set1 197("C5) h:=2639643+491520=3131163, hh:=199
+333: setchar46 ("2E) h:=3131163+182043=3313206, hh:=211
+334: setchar92 ("5C) h:=3313206+327680=3640886, hh:=232
+[.\]
+335: set1 255("FF) h:=3640886+327680=3968566, hh:=253
+337: setchar46 ("2E) h:=3968566+182043=4150609, hh:=265
+338: right3 291271 h:=4150609+291271=4441880, hh:=281
+[. ]
+342: set1 191("BF) h:=4441880+491520=4933400, hh:=312
+344: pop
+level 1:(h=0,v=5373952,w=0,x=0,y=786432,z=0,hh=0,vv=340)
+345: y0 786432 v:=5373952+786432=6160384, vv:=390
+346: push
+level 1:(h=0,v=6160384,w=0,x=0,y=786432,z=0,hh=0,vv=390)
+347: push
+level 2:(h=0,v=6160384,w=0,x=0,y=786432,z=0,hh=0,vv=390)
+348: fntnum1 current font is min10
+349: set2 8739("2223) type=0 h:=0+630598=630598, hh:=40
+[∣]
+352: pop
+level 2:(h=0,v=6160384,w=0,x=0,y=786432,z=0,hh=0,vv=390)
+353: right3 630598 h:=0+630598=630598, hh:=40
+[ ]
+357: fntnum0 current font is ec-lmr10
+358: setchar99 ("63) h:=630598+291275=921873, hh:=58
+359: setchar115 ("73) h:=921873+258506=1180379, hh:=74
+360: setchar110 ("6E) h:=1180379+364085=1544464, hh:=97
+361: setchar97 ("61) h:=1544464+327680=1872144, hh:=118
+362: setchar109 ("6D) h:=1872144+546111=2418255, hh:=153
+363: setchar101 ("65) h:=2418255+291275=2709530, hh:=171
+364: setchar49 ("31) h:=2709530+327680=3037210, hh:=192
+365: setchar46 ("2E) h:=3037210+182043=3219253, hh:=204
+[csname1.]
+366: pop
+level 1:(h=0,v=6160384,w=0,x=0,y=786432,z=0,hh=0,vv=390)
+367: y0 786432 v:=6160384+786432=6946816, vv:=440
+368: push
+level 1:(h=0,v=6946816,w=0,x=0,y=786432,z=0,hh=0,vv=440)
+369: right3 1310720 h:=0+1310720=1310720, hh:=83
+373: setchar92 ("5C) h:=1310720+327680=1638400, hh:=104
+[ \]
+374: set1 197("C5) h:=1638400+491520=2129920, hh:=135
+376: set1 191("BF) h:=2129920+491520=2621440, hh:=166
+378: setchar92 ("5C) h:=2621440+327680=2949120, hh:=187
+[\]
+379: set1 197("C5) h:=2949120+491520=3440640, hh:=218
+381: set1 191("BF) h:=3440640+491520=3932160, hh:=249
+383: setchar44 ("2C) h:=3932160+182043=4114203, hh:=261
+384: w3 218453 h:=4114203+218453=4332656, hh:=274
+388: setchar92 ("5C) h:=4332656+327680=4660336, hh:=295
+[, \]
+389: set1 197("C5) h:=4660336+491520=5151856, hh:=326
+391: set1 191("BF) h:=5151856+491520=5643376, hh:=357
+393: setchar44 ("2C) h:=5643376+182043=5825419, hh:=369
+394: w0 218453 h:=5825419+218453=6043872, hh:=383
+395: setchar92 ("5C) h:=6043872+327680=6371552, hh:=404
+[, \]
+396: fntnum1 current font is min10
+397: set2 17727("453F) type=0 h:=6371552+630598=7002150, hh:=444
+[䔿]
+400: pop
+level 1:(h=0,v=6946816,w=0,x=0,y=786432,z=0,hh=0,vv=440)
+401: y0 786432 v:=6946816+786432=7733248, vv:=490
+402: push
+level 1:(h=0,v=7733248,w=0,x=0,y=786432,z=0,hh=0,vv=490)
+403: right3 1310720 h:=0+1310720=1310720, hh:=83
+[ ]
+407: fntnum0 current font is ec-lmr10
+408: setchar92 ("5C) h:=1310720+327680=1638400, hh:=104
+[\]
+409: fntnum1 current font is min10
+410: set2 9250("2422) type=0 h:=1638400+630598=2268998, hh:=144
+[␢]
+413: fntnum0 current font is ec-lmr10
+414: setchar61 ("3D) h:=2268998+509738=2778736, hh:=176
+415: setchar62 ("3E) h:=2778736+509738=3288474, hh:=208
+416: right3 218453 h:=3288474+218453=3506927, hh:=222
+420: setchar104 ("68) h:=3506927+364085=3871012, hh:=245
+421: setchar111 ("6F) h:=3871012+327680=4198692, hh:=266
+422: setchar103 ("67) h:=4198692+327680=4526372, hh:=287
+423: setchar101 ("65) h:=4526372+291275=4817647, hh:=305
+424: setchar44 ("2C) h:=4817647+182043=4999690, hh:=317
+[=> hoge,]
+425: pop
+level 1:(h=0,v=7733248,w=0,x=0,y=786432,z=0,hh=0,vv=490)
+426: y0 786432 v:=7733248+786432=8519680, vv:=540
+427: push
+level 1:(h=0,v=8519680,w=0,x=0,y=786432,z=0,hh=0,vv=540)
+428: right3 1310720 h:=0+1310720=1310720, hh:=83
+432: setchar92 ("5C) h:=1310720+327680=1638400, hh:=104
+[ \]
+433: set1 164("A4) h:=1638400+364085=2002485, hh:=127
+435: set1 162("A2) h:=2002485+291275=2293760, hh:=145
+437: setchar61 ("3D) h:=2293760+509738=2803498, hh:=177
+438: setchar62 ("3E) h:=2803498+509738=3313236, hh:=209
+439: right3 218453 h:=3313236+218453=3531689, hh:=224
+443: setchar112 ("70) h:=3531689+364085=3895774, hh:=247
+444: setchar105 ("69) h:=3895774+182043=4077817, hh:=259
+445: setchar121 ("79) h:=4077817+345898=4423715, hh:=281
+446: right2 -18205 h:=4423715-18205=4405510, hh:=280
+449: setchar111 ("6F) h:=4405510+327680=4733190, hh:=301
+450: setchar44 ("2C) h:=4733190+182043=4915233, hh:=313
+[=> piyo,]
+451: pop
+level 1:(h=0,v=8519680,w=0,x=0,y=786432,z=0,hh=0,vv=540)
+452: y0 786432 v:=8519680+786432=9306112, vv:=589
+453: push
+level 1:(h=0,v=9306112,w=0,x=0,y=786432,z=0,hh=0,vv=589)
+454: right3 1310720 h:=0+1310720=1310720, hh:=83
+458: setchar92 ("5C) h:=1310720+327680=1638400, hh:=104
+[ \]
+459: set1 164("A4) h:=1638400+364085=2002485, hh:=127
+461: set1 162("A2) h:=2002485+291275=2293760, hh:=145
+463: setchar61 ("3D) h:=2293760+509738=2803498, hh:=177
+464: setchar62 ("3E) h:=2803498+509738=3313236, hh:=209
+465: right3 218453 h:=3313236+218453=3531689, hh:=224
+469: setchar112 ("70) h:=3531689+364085=3895774, hh:=247
+470: setchar105 ("69) h:=3895774+182043=4077817, hh:=259
+471: setchar121 ("79) h:=4077817+345898=4423715, hh:=281
+472: right2 -18205 h:=4423715-18205=4405510, hh:=280
+475: setchar111 ("6F) h:=4405510+327680=4733190, hh:=301
+476: setchar46 ("2E) h:=4733190+182043=4915233, hh:=313
+[=> piyo.]
+477: pop
+level 1:(h=0,v=9306112,w=0,x=0,y=786432,z=0,hh=0,vv=589)
+478: y0 786432 v:=9306112+786432=10092544, vv:=639
+479: push
+level 1:(h=0,v=10092544,w=0,x=0,y=786432,z=0,hh=0,vv=639)
+480: push
+level 2:(h=0,v=10092544,w=0,x=0,y=786432,z=0,hh=0,vv=639)
+481: fntnum1 current font is min10
+482: set2 8739("2223) type=0 h:=0+630598=630598, hh:=40
+[∣]
+485: pop
+level 2:(h=0,v=10092544,w=0,x=0,y=786432,z=0,hh=0,vv=639)
+486: right3 630598 h:=0+630598=630598, hh:=40
+[ ]
+490: fntnum0 current font is ec-lmr10
+491: setchar48 ("30) h:=630598+327680=958278, hh:=61
+492: setchar120 ("78) h:=958278+345898=1304176, hh:=83
+493: setchar70 ("46) h:=1304176+427818=1731994, hh:=110
+494: setchar70 ("46) h:=1731994+427818=2159812, hh:=137
+495: setchar46 ("2E) h:=2159812+182043=2341855, hh:=149
+[0xFF.]
+496: pop
+level 1:(h=0,v=10092544,w=0,x=0,y=786432,z=0,hh=0,vv=639)
+497: y0 786432 v:=10092544+786432=10878976, vv:=689
+498: push
+level 1:(h=0,v=10878976,w=0,x=0,y=786432,z=0,hh=0,vv=689)
+499: right3 1310720 h:=0+1310720=1310720, hh:=83
+[ ]
+503: fntnum1 current font is min10
+504: set2 9250("2422) type=0 h:=1310720+630598=1941318, hh:=123
+[␢]
+507: fntnum0 current font is ec-lmr10
+508: setchar44 ("2C) h:=1941318+182043=2123361, hh:=135
+509: w3 218453 h:=2123361+218453=2341814, hh:=148
+513: setchar40 ("28) h:=2341814+254870=2596684, hh:=164
+514: setchar92 ("5C) h:=2596684+327680=2924364, hh:=185
+[, (\]
+515: set1 255("FF) h:=2924364+327680=3252044, hh:=206
+517: set1 197("C5) h:=3252044+491520=3743564, hh:=237
+519: set1 255("FF) h:=3743564+327680=4071244, hh:=258
+521: set1 191("BF) h:=4071244+491520=4562764, hh:=289
+523: setchar58 ("3A) h:=4562764+182043=4744807, hh:=301
+524: setchar109 ("6D) h:=4744807+546111=5290918, hh:=336
+525: setchar97 ("61) h:=5290918+327680=5618598, hh:=357
+526: setchar99 ("63) h:=5618598+291275=5909873, hh:=375
+527: setchar114 ("72) h:=5909873+256683=6166556, hh:=391
+528: setchar111 ("6F) h:=6166556+327680=6494236, hh:=412
+529: setchar58 ("3A) h:=6494236+182043=6676279, hh:=424
+530: setchar45 ("2D) h:=6676279+218453=6894732, hh:=438
+531: setchar62 ("3E) h:=6894732+509738=7404470, hh:=470
+[:macro:->]
+532: fntnum1 current font is min10
+533: set2 9250("2422) type=0 h:=7404470+630598=8035068, hh:=510
+[␢]
+536: fntnum0 current font is ec-lmr10
+537: setchar41 ("29) h:=8035068+254870=8289938, hh:=526
+538: setchar44 ("2C) h:=8289938+182043=8471981, hh:=538
+539: w0 218453 h:=8471981+218453=8690434, hh:=550
+540: setchar92 ("5C) h:=8690434+327680=9018114, hh:=571
+[), \]
+541: set1 255("FF) h:=9018114+327680=9345794, hh:=592
+543: set1 197("C5) h:=9345794+491520=9837314, hh:=623
+545: set1 255("FF) h:=9837314+327680=10164994, hh:=644
+547: set1 191("BF) h:=10164994+491520=10656514, hh:=675
+549: setchar44 ("2C) h:=10656514+182043=10838557, hh:=687
+550: w0 218453 h:=10838557+218453=11057010, hh:=700
+551: setchar109 ("6D) h:=11057010+546111=11603121, hh:=735
+552: setchar97 ("61) h:=11603121+327680=11930801, hh:=756
+553: setchar99 ("63) h:=11930801+291275=12222076, hh:=774
+554: setchar114 ("72) h:=12222076+256683=12478759, hh:=790
+555: setchar111 ("6F) h:=12478759+327680=12806439, hh:=811
+556: setchar58 ("3A) h:=12806439+182043=12988482, hh:=823
+557: setchar45 ("2D) h:=12988482+218453=13206935, hh:=837
+558: setchar62 ("3E) h:=13206935+509738=13716673, hh:=869
+559: setchar40 ("28) h:=13716673+254870=13971543, hh:=885
+560: setchar92 ("5C) h:=13971543+327680=14299223, hh:=906
+[, macro:->(\]
+561: set1 255("FF) h:=14299223+327680=14626903, hh:=927
+563: set1 197("C5) h:=14626903+491520=15118423, hh:=958
+565: set1 255("FF) h:=15118423+327680=15446103, hh:=979
+567: set1 191("BF) h:=15446103+491520=15937623, hh:=1010
+569: setchar58 ("3A) h:=15937623+182043=16119666, hh:=1022
+570: setchar109 ("6D) h:=16119666+546111=16665777, hh:=1057
+571: setchar97 ("61) h:=16665777+327680=16993457, hh:=1078
+572: setchar99 ("63) h:=16993457+291275=17284732, hh:=1096
+573: setchar114 ("72) h:=17284732+256683=17541415, hh:=1112
+574: setchar111 ("6F) h:=17541415+327680=17869095, hh:=1133
+575: setchar58 ("3A) h:=17869095+182043=18051138, hh:=1145
+576: setchar45 ("2D) h:=18051138+218453=18269591, hh:=1159
+577: setchar62 ("3E) h:=18269591+509738=18779329, hh:=1191
+[:macro:->]
+578: fntnum1 current font is min10
+579: set2 9250("2422) type=0 h:=18779329+630598=19409927, hh:=1231
+[␢]
+582: fntnum0 current font is ec-lmr10
+583: setchar41 ("29) h:=19409927+254870=19664797, hh:=1247
+584: setchar46 ("2E) h:=19664797+182043=19846840, hh:=1259
+[).]
+585: pop
+level 1:(h=0,v=10878976,w=0,x=0,y=786432,z=0,hh=0,vv=689)
+586: y0 786432 v:=10878976+786432=11665408, vv:=739
+587: push
+level 1:(h=0,v=11665408,w=0,x=0,y=786432,z=0,hh=0,vv=739)
+588: right3 1310720 h:=0+1310720=1310720, hh:=83
+[ ]
+592: fntnum1 current font is min10
+593: set2 9252("2424) type=0 h:=1310720+630598=1941318, hh:=123
+[␤]
+596: fntnum0 current font is ec-lmr10
+597: setchar44 ("2C) h:=1941318+182043=2123361, hh:=135
+598: w3 218453 h:=2123361+218453=2341814, hh:=148
+602: setchar40 ("28) h:=2341814+254870=2596684, hh:=164
+603: setchar92 ("5C) h:=2596684+327680=2924364, hh:=185
+[, (\]
+604: set1 255("FF) h:=2924364+327680=3252044, hh:=206
+606: setchar58 ("3A) h:=3252044+182043=3434087, hh:=218
+607: setchar109 ("6D) h:=3434087+546111=3980198, hh:=253
+608: setchar97 ("61) h:=3980198+327680=4307878, hh:=274
+609: setchar99 ("63) h:=4307878+291275=4599153, hh:=292
+610: setchar114 ("72) h:=4599153+256683=4855836, hh:=308
+611: setchar111 ("6F) h:=4855836+327680=5183516, hh:=329
+612: setchar58 ("3A) h:=5183516+182043=5365559, hh:=341
+613: setchar45 ("2D) h:=5365559+218453=5584012, hh:=355
+614: setchar62 ("3E) h:=5584012+509738=6093750, hh:=387
+[:macro:->]
+615: fntnum1 current font is min10
+616: set2 9252("2424) type=0 h:=6093750+630598=6724348, hh:=427
+[␤]
+619: fntnum0 current font is ec-lmr10
+620: setchar41 ("29) h:=6724348+254870=6979218, hh:=443
+621: setchar44 ("2C) h:=6979218+182043=7161261, hh:=455
+622: w0 218453 h:=7161261+218453=7379714, hh:=467
+623: setchar92 ("5C) h:=7379714+327680=7707394, hh:=488
+[), \]
+624: set1 255("FF) h:=7707394+327680=8035074, hh:=509
+626: setchar44 ("2C) h:=8035074+182043=8217117, hh:=521
+627: w0 218453 h:=8217117+218453=8435570, hh:=534
+628: setchar109 ("6D) h:=8435570+546111=8981681, hh:=569
+629: setchar97 ("61) h:=8981681+327680=9309361, hh:=590
+630: setchar99 ("63) h:=9309361+291275=9600636, hh:=608
+631: setchar114 ("72) h:=9600636+256683=9857319, hh:=624
+632: setchar111 ("6F) h:=9857319+327680=10184999, hh:=645
+633: setchar58 ("3A) h:=10184999+182043=10367042, hh:=657
+634: setchar45 ("2D) h:=10367042+218453=10585495, hh:=671
+635: setchar62 ("3E) h:=10585495+509738=11095233, hh:=703
+636: setchar40 ("28) h:=11095233+254870=11350103, hh:=719
+637: setchar92 ("5C) h:=11350103+327680=11677783, hh:=740
+[, macro:->(\]
+638: set1 255("FF) h:=11677783+327680=12005463, hh:=761
+640: setchar58 ("3A) h:=12005463+182043=12187506, hh:=773
+641: setchar109 ("6D) h:=12187506+546111=12733617, hh:=808
+642: setchar97 ("61) h:=12733617+327680=13061297, hh:=829
+643: setchar99 ("63) h:=13061297+291275=13352572, hh:=847
+644: setchar114 ("72) h:=13352572+256683=13609255, hh:=863
+645: setchar111 ("6F) h:=13609255+327680=13936935, hh:=884
+646: setchar58 ("3A) h:=13936935+182043=14118978, hh:=896
+647: setchar45 ("2D) h:=14118978+218453=14337431, hh:=910
+648: setchar62 ("3E) h:=14337431+509738=14847169, hh:=942
+[:macro:->]
+649: fntnum1 current font is min10
+650: set2 9252("2424) type=0 h:=14847169+630598=15477767, hh:=982
+[␤]
+653: fntnum0 current font is ec-lmr10
+654: setchar41 ("29) h:=15477767+254870=15732637, hh:=998
+655: setchar46 ("2E) h:=15732637+182043=15914680, hh:=1010
+[).]
+656: pop
+level 1:(h=0,v=11665408,w=0,x=0,y=786432,z=0,hh=0,vv=739)
+657: y0 786432 v:=11665408+786432=12451840, vv:=789
+658: push
+level 1:(h=0,v=12451840,w=0,x=0,y=786432,z=0,hh=0,vv=789)
+659: push
+level 2:(h=0,v=12451840,w=0,x=0,y=786432,z=0,hh=0,vv=789)
+660: fntnum1 current font is min10
+661: set2 8739("2223) type=0 h:=0+630598=630598, hh:=40
+[∣]
+664: pop
+level 2:(h=0,v=12451840,w=0,x=0,y=786432,z=0,hh=0,vv=789)
+665: right3 630598 h:=0+630598=630598, hh:=40
+[ ]
+669: fntnum0 current font is ec-lmr10
+670: setchar99 ("63) h:=630598+291275=921873, hh:=58
+671: setchar111 ("6F) h:=921873+327680=1249553, hh:=79
+672: setchar110 ("6E) h:=1249553+364085=1613638, hh:=102
+673: right2 -18205 h:=1613638-18205=1595433, hh:=101
+676: setchar116 ("74) h:=1595433+254870=1850303, hh:=117
+677: setchar101 ("65) h:=1850303+291275=2141578, hh:=135
+678: setchar120 ("78) h:=2141578+345898=2487476, hh:=157
+679: setchar116 ("74) h:=2487476+254870=2742346, hh:=173
+680: setchar115 ("73) h:=2742346+258506=3000852, hh:=189
+681: setchar46 ("2E) h:=3000852+182043=3182895, hh:=201
+[contexts.]
+682: pop
+level 1:(h=0,v=12451840,w=0,x=0,y=786432,z=0,hh=0,vv=789)
+683: y0 786432 v:=12451840+786432=13238272, vv:=839
+684: push
+level 1:(h=0,v=13238272,w=0,x=0,y=786432,z=0,hh=0,vv=839)
+685: right3 1310720 h:=0+1310720=1310720, hh:=83
+[ ]
+689: fntnum1 current font is min10
+690: set2 9250("2422) type=0 h:=1310720+630598=1941318, hh:=123
+[␢]
+693: fntnum0 current font is ec-lmr10
+694: set1 197("C5) h:=1941318+491520=2432838, hh:=154
+696: set1 191("BF) h:=2432838+491520=2924358, hh:=185
+698: w3 218453 h:=2924358+218453=3142811, hh:=199
+702: setchar97 ("61) h:=3142811+327680=3470491, hh:=220
+[ a]
+703: set1 197("C5) h:=3470491+491520=3962011, hh:=251
+705: set1 191("BF) h:=3962011+491520=4453531, hh:=282
+707: w0 218453 h:=4453531+218453=4671984, hh:=296
+[ ]
+708: set1 197("C5) h:=4671984+491520=5163504, hh:=327
+710: set1 191("BF) h:=5163504+491520=5655024, hh:=358
+712: w0 218453 h:=5655024+218453=5873477, hh:=372
+[ ]
+713: fntnum1 current font is min10
+714: set2 17727("453F) type=0 h:=5873477+630598=6504075, hh:=412
+717: w0 218453 h:=6504075+218453=6722528, hh:=426
+[䔿 ]
+718: fntnum0 current font is ec-lmr10
+719: set1 197("C5) h:=6722528+491520=7214048, hh:=457
+721: set1 191("BF) h:=7214048+491520=7705568, hh:=488
+723: w0 218453 h:=7705568+218453=7924021, hh:=502
+[ ]
+724: set1 197("C5) h:=7924021+491520=8415541, hh:=533
+726: set1 191("BF) h:=8415541+491520=8907061, hh:=564
+728: w0 218453 h:=8907061+218453=9125514, hh:=578
+[ ]
+729: fntnum1 current font is min10
+730: set2 17727("453F) type=0 h:=9125514+630598=9756112, hh:=618
+733: x3 436906 h:=9756112+436906=10193018, hh:=646
+[䔿 ]
+737: fntnum0 current font is ec-lmr10
+738: set1 197("C5) h:=10193018+491520=10684538, hh:=677
+740: set1 191("BF) h:=10684538+491520=11176058, hh:=708
+742: w0 218453 h:=11176058+218453=11394511, hh:=722
+[ ]
+743: fntnum1 current font is min10
+744: set2 17727("453F) type=0 h:=11394511+630598=12025109, hh:=762
+747: x0 436906 h:=12025109+436906=12462015, hh:=789
+748: set2 9252("2424) type=0 h:=12462015+630598=13092613, hh:=829
+[䔿 ␤]
+751: fntnum0 current font is ec-lmr10
+752: setchar109 ("6D) h:=13092613+546111=13638724, hh:=864
+753: setchar97 ("61) h:=13638724+327680=13966404, hh:=885
+754: setchar99 ("63) h:=13966404+291275=14257679, hh:=903
+755: setchar114 ("72) h:=14257679+256683=14514362, hh:=919
+756: setchar111 ("6F) h:=14514362+327680=14842042, hh:=940
+757: setchar58 ("3A) h:=14842042+182043=15024085, hh:=952
+758: setchar45 ("2D) h:=15024085+218453=15242538, hh:=966
+759: setchar62 ("3E) h:=15242538+509738=15752276, hh:=998
+[macro:->]
+760: set1 197("C5) h:=15752276+491520=16243796, hh:=1029
+762: set1 191("BF) h:=16243796+491520=16735316, hh:=1060
+764: w0 218453 h:=16735316+218453=16953769, hh:=1074
+[ ]
+765: set1 197("C5) h:=16953769+491520=17445289, hh:=1105
+767: set1 191("BF) h:=17445289+491520=17936809, hh:=1136
+769: w0 218453 h:=17936809+218453=18155262, hh:=1150
+[ ]
+770: fntnum1 current font is min10
+771: set2 17727("453F) type=0 h:=18155262+630598=18785860, hh:=1190
+[䔿]
+774: pop
+level 1:(h=0,v=13238272,w=0,x=0,y=786432,z=0,hh=0,vv=839)
+775: y0 786432 v:=13238272+786432=14024704, vv:=888
+776: push
+level 1:(h=0,v=14024704,w=0,x=0,y=786432,z=0,hh=0,vv=888)
+777: right3 1310720 h:=0+1310720=1310720, hh:=83
+781: set2 9250("2422) type=0 h:=1310720+630598=1941318, hh:=123
+[ ␢]
+784: fntnum0 current font is ec-lmr10
+785: set1 197("C5) h:=1941318+491520=2432838, hh:=154
+787: set1 191("BF) h:=2432838+491520=2924358, hh:=185
+789: w3 218453 h:=2924358+218453=3142811, hh:=199
+[ ]
+793: set1 197("C5) h:=3142811+491520=3634331, hh:=230
+795: set1 191("BF) h:=3634331+491520=4125851, hh:=261
+797: fntnum1 current font is min10
+798: set2 17727("453F) type=0 h:=4125851+630598=4756449, hh:=301
+[䔿]
+801: fntnum0 current font is ec-lmr10
+802: set1 197("C5) h:=4756449+491520=5247969, hh:=332
+804: set1 191("BF) h:=5247969+491520=5739489, hh:=363
+806: w0 218453 h:=5739489+218453=5957942, hh:=377
+807: setchar97 ("61) h:=5957942+327680=6285622, hh:=398
+[ a]
+808: set1 197("C5) h:=6285622+491520=6777142, hh:=429
+810: set1 191("BF) h:=6777142+491520=7268662, hh:=460
+812: w0 218453 h:=7268662+218453=7487115, hh:=474
+[ ]
+813: set1 197("C5) h:=7487115+491520=7978635, hh:=505
+815: set1 191("BF) h:=7978635+491520=8470155, hh:=536
+817: w0 218453 h:=8470155+218453=8688608, hh:=550
+[ ]
+818: fntnum1 current font is min10
+819: set2 17727("453F) type=0 h:=8688608+630598=9319206, hh:=590
+822: w0 218453 h:=9319206+218453=9537659, hh:=604
+[䔿 ]
+823: fntnum0 current font is ec-lmr10
+824: set1 197("C5) h:=9537659+491520=10029179, hh:=635
+826: set1 191("BF) h:=10029179+491520=10520699, hh:=666
+828: w0 218453 h:=10520699+218453=10739152, hh:=680
+[ ]
+829: set1 197("C5) h:=10739152+491520=11230672, hh:=711
+831: set1 191("BF) h:=11230672+491520=11722192, hh:=742
+833: w0 218453 h:=11722192+218453=11940645, hh:=756
+[ ]
+834: fntnum1 current font is min10
+835: set2 17727("453F) type=0 h:=11940645+630598=12571243, hh:=796
+838: right3 436906 h:=12571243+436906=13008149, hh:=824
+[䔿 ]
+842: fntnum0 current font is ec-lmr10
+843: set1 197("C5) h:=13008149+491520=13499669, hh:=855
+845: set1 191("BF) h:=13499669+491520=13991189, hh:=886
+847: w0 218453 h:=13991189+218453=14209642, hh:=900
+[ ]
+848: fntnum1 current font is min10
+849: set2 17727("453F) type=0 h:=14209642+630598=14840240, hh:=940
+852: w0 218453 h:=14840240+218453=15058693, hh:=954
+853: set2 9252("2424) type=0 h:=15058693+630598=15689291, hh:=994
+[䔿 ␤]
+856: fntnum0 current font is ec-lmr10
+857: set1 255("FF) h:=15689291+327680=16016971, hh:=1015
+859: pop
+level 1:(h=0,v=14024704,w=0,x=0,y=786432,z=0,hh=0,vv=888)
+860: y0 786432 v:=14024704+786432=14811136, vv:=938
+861: push
+level 1:(h=0,v=14811136,w=0,x=0,y=786432,z=0,hh=0,vv=938)
+862: right3 1310720 h:=0+1310720=1310720, hh:=83
+[ ]
+866: fntnum1 current font is min10
+867: set2 9252("2424) type=0 h:=1310720+630598=1941318, hh:=123
+[␤]
+870: pop
+level 1:(h=0,v=14811136,w=0,x=0,y=786432,z=0,hh=0,vv=938)
+871: y0 786432 v:=14811136+786432=15597568, vv:=988
+872: push
+level 1:(h=0,v=15597568,w=0,x=0,y=786432,z=0,hh=0,vv=988)
+873: right3 1310720 h:=0+1310720=1310720, hh:=83
+[ ]
+877: fntnum0 current font is ec-lmr10
+878: setchar46 ("2E) h:=1310720+182043=1492763, hh:=95
+879: setchar97 ("61) h:=1492763+327680=1820443, hh:=116
+880: setchar46 ("2E) h:=1820443+182043=2002486, hh:=128
+881: setchar46 ("2E) h:=2002486+182043=2184529, hh:=140
+882: setchar46 ("2E) h:=2184529+182043=2366572, hh:=152
+883: setchar99 ("63) h:=2366572+291275=2657847, hh:=170
+884: setchar46 ("2E) h:=2657847+182043=2839890, hh:=182
+885: setchar98 ("62) h:=2839890+364085=3203975, hh:=205
+886: setchar46 ("2E) h:=3203975+182043=3386018, hh:=216
+887: setchar46 ("2E) h:=3386018+182043=3568061, hh:=228
+[.a...c.b..]
+888: pop
+level 1:(h=0,v=15597568,w=0,x=0,y=786432,z=0,hh=0,vv=988)
+889: y0 786432 v:=15597568+786432=16384000, vv:=1038
+890: push
+level 1:(h=0,v=16384000,w=0,x=0,y=786432,z=0,hh=0,vv=1038)
+891: right3 1310720 h:=0+1310720=1310720, hh:=83
+895: setchar46 ("2E) h:=1310720+182043=1492763, hh:=95
+896: setchar97 ("61) h:=1492763+327680=1820443, hh:=116
+897: setchar46 ("2E) h:=1820443+182043=2002486, hh:=128
+898: setchar46 ("2E) h:=2002486+182043=2184529, hh:=140
+899: setchar46 ("2E) h:=2184529+182043=2366572, hh:=152
+900: setchar99 ("63) h:=2366572+291275=2657847, hh:=170
+901: setchar46 ("2E) h:=2657847+182043=2839890, hh:=182
+902: setchar98 ("62) h:=2839890+364085=3203975, hh:=205
+903: setchar46 ("2E) h:=3203975+182043=3386018, hh:=216
+904: setchar46 ("2E) h:=3386018+182043=3568061, hh:=228
+[ .a...c.b..]
+905: pop
+level 1:(h=0,v=16384000,w=0,x=0,y=786432,z=0,hh=0,vv=1038)
+906: y0 786432 v:=16384000+786432=17170432, vv:=1088
+907: push
+level 1:(h=0,v=17170432,w=0,x=0,y=786432,z=0,hh=0,vv=1088)
+908: push
+level 2:(h=0,v=17170432,w=0,x=0,y=786432,z=0,hh=0,vv=1088)
+909: fntnum1 current font is min10
+910: set2 8739("2223) type=0 h:=0+630598=630598, hh:=40
+[∣]
+913: pop
+level 2:(h=0,v=17170432,w=0,x=0,y=786432,z=0,hh=0,vv=1088)
+914: right3 630598 h:=0+630598=630598, hh:=40
+[ ]
+918: fntnum0 current font is ec-lmr10
+919: setchar99 ("63) h:=630598+291275=921873, hh:=58
+920: setchar115 ("73) h:=921873+258506=1180379, hh:=74
+921: setchar110 ("6E) h:=1180379+364085=1544464, hh:=97
+922: setchar97 ("61) h:=1544464+327680=1872144, hh:=118
+923: setchar109 ("6D) h:=1872144+546111=2418255, hh:=153
+924: setchar101 ("65) h:=2418255+291275=2709530, hh:=171
+925: setchar50 ("32) h:=2709530+327680=3037210, hh:=192
+926: setchar46 ("2E) h:=3037210+182043=3219253, hh:=204
+[csname2.]
+927: pop
+level 1:(h=0,v=17170432,w=0,x=0,y=786432,z=0,hh=0,vv=1088)
+928: y0 786432 v:=17170432+786432=17956864, vv:=1137
+929: push
+level 1:(h=0,v=17956864,w=0,x=0,y=786432,z=0,hh=0,vv=1137)
+930: right3 1310720 h:=0+1310720=1310720, hh:=83
+934: setchar83 ("53) h:=1310720+364085=1674805, hh:=106
+935: setchar72 ("48) h:=1674805+491520=2166325, hh:=137
+936: setchar79 ("4F) h:=2166325+509738=2676063, hh:=169
+937: right2 -18205 h:=2676063-18205=2657858, hh:=168
+940: setchar87 ("57) h:=2657858+673578=3331436, hh:=211
+941: right3 218453 h:=3331436+218453=3549889, hh:=225
+945: setchar46 ("2E) h:=3549889+182043=3731932, hh:=237
+946: setchar46 ("2E) h:=3731932+182043=3913975, hh:=249
+947: setchar46 ("2E) h:=3913975+182043=4096018, hh:=261
+[ SHOW ...]
+948: pop
+level 1:(h=0,v=17956864,w=0,x=0,y=786432,z=0,hh=0,vv=1137)
+949: y0 786432 v:=17956864+786432=18743296, vv:=1187
+950: push
+level 1:(h=0,v=18743296,w=0,x=0,y=786432,z=0,hh=0,vv=1187)
+951: right3 1310720 h:=0+1310720=1310720, hh:=83
+955: setchar83 ("53) h:=1310720+364085=1674805, hh:=106
+956: setchar84 ("54) h:=1674805+473301=2148106, hh:=136
+957: setchar82 ("52) h:=2148106+482410=2630516, hh:=167
+958: setchar73 ("49) h:=2630516+236650=2867166, hh:=182
+959: setchar78 ("4E) h:=2867166+491520=3358686, hh:=213
+960: setchar71 ("47) h:=3358686+514261=3872947, hh:=246
+961: right3 218453 h:=3872947+218453=4091400, hh:=259
+965: setchar92 ("5C) h:=4091400+327680=4419080, hh:=280
+[ STRING \]
+966: set1 197("C5) h:=4419080+491520=4910600, hh:=311
+968: set1 191("BF) h:=4910600+491520=5402120, hh:=342
+970: setchar46 ("2E) h:=5402120+182043=5584163, hh:=354
+971: setchar92 ("5C) h:=5584163+327680=5911843, hh:=375
+[.\]
+972: fntnum1 current font is min10
+973: set2 17727("453F) type=0 h:=5911843+630598=6542441, hh:=415
+[䔿]
+976: fntnum0 current font is ec-lmr10
+977: setchar46 ("2E) h:=6542441+182043=6724484, hh:=427
+978: setchar92 ("5C) h:=6724484+327680=7052164, hh:=448
+[.\]
+979: set1 197("C5) h:=7052164+491520=7543684, hh:=479
+981: set1 191("BF) h:=7543684+491520=8035204, hh:=510
+983: setchar46 ("2E) h:=8035204+182043=8217247, hh:=522
+984: setchar92 ("5C) h:=8217247+327680=8544927, hh:=543
+[.\]
+985: fntnum1 current font is min10
+986: set2 17727("453F) type=0 h:=8544927+630598=9175525, hh:=583
+[䔿]
+989: pop
+level 1:(h=0,v=18743296,w=0,x=0,y=786432,z=0,hh=0,vv=1187)
+990: y0 786432 v:=18743296+786432=19529728, vv:=1237
+991: push
+level 1:(h=0,v=19529728,w=0,x=0,y=786432,z=0,hh=0,vv=1237)
+992: right3 1310720 h:=0+1310720=1310720, hh:=83
+[ ]
+996: fntnum0 current font is ec-lmr10
+997: setchar77 ("4D) h:=1310720+600768=1911488, hh:=121
+998: setchar69 ("45) h:=1911488+446005=2357493, hh:=149
+999: setchar65 ("41) h:=2357493+491520=2849013, hh:=180
+1000: setchar78 ("4E) h:=2849013+491520=3340533, hh:=211
+1001: setchar73 ("49) h:=3340533+236650=3577183, hh:=226
+1002: setchar78 ("4E) h:=3577183+491520=4068703, hh:=257
+1003: setchar71 ("47) h:=4068703+514261=4582964, hh:=290
+1004: right3 218453 h:=4582964+218453=4801417, hh:=304
+1008: setchar109 ("6D) h:=4801417+546111=5347528, hh:=339
+1009: setchar97 ("61) h:=5347528+327680=5675208, hh:=360
+1010: setchar99 ("63) h:=5675208+291275=5966483, hh:=378
+1011: setchar114 ("72) h:=5966483+256683=6223166, hh:=394
+1012: setchar111 ("6F) h:=6223166+327680=6550846, hh:=415
+1013: setchar58 ("3A) h:=6550846+182043=6732889, hh:=427
+1014: setchar45 ("2D) h:=6732889+218453=6951342, hh:=441
+1015: setchar62 ("3E) h:=6951342+509738=7461080, hh:=473
+1016: setchar97 ("61) h:=7461080+327680=7788760, hh:=494
+1017: setchar46 ("2E) h:=7788760+182043=7970803, hh:=506
+1018: setchar109 ("6D) h:=7970803+546111=8516914, hh:=541
+1019: setchar97 ("61) h:=8516914+327680=8844594, hh:=562
+1020: setchar99 ("63) h:=8844594+291275=9135869, hh:=580
+1021: setchar114 ("72) h:=9135869+256683=9392552, hh:=596
+1022: setchar111 ("6F) h:=9392552+327680=9720232, hh:=617
+1023: setchar58 ("3A) h:=9720232+182043=9902275, hh:=629
+1024: setchar45 ("2D) h:=9902275+218453=10120728, hh:=643
+1025: setchar62 ("3E) h:=10120728+509738=10630466, hh:=675
+1026: setchar98 ("62) h:=10630466+364085=10994551, hh:=698
+1027: setchar46 ("2E) h:=10994551+182043=11176594, hh:=710
+1028: setchar109 ("6D) h:=11176594+546111=11722705, hh:=745
+1029: setchar97 ("61) h:=11722705+327680=12050385, hh:=765
+1030: setchar99 ("63) h:=12050385+291275=12341660, hh:=783
+1031: setchar114 ("72) h:=12341660+256683=12598343, hh:=799
+1032: setchar111 ("6F) h:=12598343+327680=12926023, hh:=820
+1033: setchar58 ("3A) h:=12926023+182043=13108066, hh:=832
+1034: setchar45 ("2D) h:=13108066+218453=13326519, hh:=846
+1035: setchar62 ("3E) h:=13326519+509738=13836257, hh:=878
+1036: setchar97 ("61) h:=13836257+327680=14163937, hh:=899
+1037: setchar46 ("2E) h:=14163937+182043=14345980, hh:=911
+1038: setchar109 ("6D) h:=14345980+546111=14892091, hh:=945
+1039: setchar97 ("61) h:=14892091+327680=15219771, hh:=966
+1040: setchar99 ("63) h:=15219771+291275=15511046, hh:=984
+1041: setchar114 ("72) h:=15511046+256683=15767729, hh:=1000
+1042: setchar111 ("6F) h:=15767729+327680=16095409, hh:=1021
+1043: setchar58 ("3A) h:=16095409+182043=16277452, hh:=1033
+1044: setchar45 ("2D) h:=16277452+218453=16495905, hh:=1047
+1045: setchar62 ("3E) h:=16495905+509738=17005643, hh:=1079
+1046: setchar98 ("62) h:=17005643+364085=17369728, hh:=1102
+1047: setchar46 ("2E) h:=17369728+182043=17551771, hh:=1114
+[MEANING macro:->a.macro:->b.macro:->a.macro:->b.]
+1048: pop
+level 1:(h=0,v=19529728,w=0,x=0,y=786432,z=0,hh=0,vv=1237)
+1049: pop
+level 0:(h=0,v=42152922,w=0,x=0,y=0,z=0,hh=0,vv=2670)
+1050: down3 1572864 v:=42152922+1572864=43725786, vv:=2770
+1054: eop
diff --git a/Build/source/texk/web2c/ptexdir/tests/p-pknj-sa.typ b/Build/source/texk/web2c/ptexdir/tests/p-pknj-sa.typ
new file mode 100644
index 00000000000..9373f0bac64
--- /dev/null
+++ b/Build/source/texk/web2c/ptexdir/tests/p-pknj-sa.typ
@@ -0,0 +1,829 @@
+Options selected:
+ Starting page = *
+ Maximum number of pages = 1000000
+ Output level = 4 (the works)
+ Resolution = 300.00000000 pixels per inch
+numerator/denominator=25400000/473628672
+magnification=1000; 0.00006334 pixels per DVI unit
+' TeX output 20XX.XX.XX'
+Postamble starts at byte 1061.
+maxv=43725786, maxh=30785863, maxstackdepth=3, totalpages=1
+Font 1: min10 (JFM)---loaded at size 655360 DVI units
+Font 0: ec-lmr10---loaded at size 655360 DVI units
+
+42: beginning of page 37726
+87: push
+level 0:(h=0,v=0,w=0,x=0,y=0,z=0,hh=0,vv=0)
+88: down3 -917504 v:=0-917504=-917504, vv:=-58
+92: pop
+level 0:(h=0,v=0,w=0,x=0,y=0,z=0,hh=0,vv=0)
+93: down4 42152922 v:=0+42152922=42152922, vv:=2670
+98: push
+level 0:(h=0,v=42152922,w=0,x=0,y=0,z=0,hh=0,vv=2670)
+99: down4 -41497562 v:=42152922-41497562=655360, vv:=42
+104: push
+level 1:(h=0,v=655360,w=0,x=0,y=0,z=0,hh=0,vv=42)
+105: right3 1310720 h:=0+1310720=1310720, hh:=83
+[ ]
+109: fntdef1 0: ec-lmr10
+133: fntnum0 current font is ec-lmr10
+134: setchar65 ("41) h:=1310720+491520=1802240, hh:=114
+135: setchar58 ("3A) h:=1802240+182043=1984283, hh:=126
+136: w3 218453 h:=1984283+218453=2202736, hh:=140
+[A: ]
+140: set1 197("C5) h:=2202736+491520=2694256, hh:=171
+142: set1 191("BF) h:=2694256+491520=3185776, hh:=202
+144: w0 218453 h:=3185776+218453=3404229, hh:=216
+[ ]
+145: set1 197("C5) h:=3404229+491520=3895749, hh:=247
+147: set1 191("BF) h:=3895749+491520=4387269, hh:=278
+149: w0 218453 h:=4387269+218453=4605722, hh:=292
+[ ]
+150: fntdef1 1: min10
+171: fntnum1 current font is min10
+172: set2 17727("453F) type=0 h:=4605722+630598=5236320, hh:=332
+[䔿]
+175: pop
+level 1:(h=0,v=655360,w=0,x=0,y=0,z=0,hh=0,vv=42)
+176: y3 786432 v:=655360+786432=1441792, vv:=91
+180: push
+level 1:(h=0,v=1441792,w=0,x=0,y=786432,z=0,hh=0,vv=91)
+181: push
+level 2:(h=0,v=1441792,w=0,x=0,y=786432,z=0,hh=0,vv=91)
+182: set2 8739("2223) type=0 h:=0+630598=630598, hh:=40
+[∣]
+185: pop
+level 2:(h=0,v=1441792,w=0,x=0,y=786432,z=0,hh=0,vv=91)
+186: right3 630598 h:=0+630598=630598, hh:=40
+[ ]
+190: fntnum0 current font is ec-lmr10
+191: setchar92 ("5C) h:=630598+327680=958278, hh:=61
+192: setchar109 ("6D) h:=958278+546111=1504389, hh:=96
+193: setchar101 ("65) h:=1504389+291275=1795664, hh:=114
+194: setchar97 ("61) h:=1795664+327680=2123344, hh:=135
+195: setchar110 ("6E) h:=2123344+364085=2487429, hh:=158
+196: setchar105 ("69) h:=2487429+182043=2669472, hh:=170
+197: setchar110 ("6E) h:=2669472+364085=3033557, hh:=193
+198: setchar103 ("67) h:=3033557+327680=3361237, hh:=214
+199: setchar46 ("2E) h:=3361237+182043=3543280, hh:=226
+[\meaning.]
+200: pop
+level 1:(h=0,v=1441792,w=0,x=0,y=786432,z=0,hh=0,vv=91)
+201: y0 786432 v:=1441792+786432=2228224, vv:=141
+202: push
+level 1:(h=0,v=2228224,w=0,x=0,y=786432,z=0,hh=0,vv=141)
+203: right3 1310720 h:=0+1310720=1310720, hh:=83
+207: setchar109 ("6D) h:=1310720+546111=1856831, hh:=118
+208: setchar97 ("61) h:=1856831+327680=2184511, hh:=139
+209: setchar99 ("63) h:=2184511+291275=2475786, hh:=157
+210: setchar114 ("72) h:=2475786+256683=2732469, hh:=173
+211: setchar111 ("6F) h:=2732469+327680=3060149, hh:=194
+212: setchar58 ("3A) h:=3060149+182043=3242192, hh:=206
+213: setchar45 ("2D) h:=3242192+218453=3460645, hh:=220
+214: setchar62 ("3E) h:=3460645+509738=3970383, hh:=252
+215: setchar109 ("6D) h:=3970383+546111=4516494, hh:=287
+216: setchar97 ("61) h:=4516494+327680=4844174, hh:=308
+217: setchar99 ("63) h:=4844174+291275=5135449, hh:=326
+218: setchar114 ("72) h:=5135449+256683=5392132, hh:=342
+219: setchar111 ("6F) h:=5392132+327680=5719812, hh:=363
+220: setchar58 ("3A) h:=5719812+182043=5901855, hh:=375
+221: setchar45 ("2D) h:=5901855+218453=6120308, hh:=389
+222: setchar62 ("3E) h:=6120308+509738=6630046, hh:=421
+[ macro:->macro:->]
+223: set1 197("C5) h:=6630046+491520=7121566, hh:=452
+225: set1 191("BF) h:=7121566+491520=7613086, hh:=483
+227: w3 218453 h:=7613086+218453=7831539, hh:=496
+[ ]
+231: set1 197("C5) h:=7831539+491520=8323059, hh:=527
+233: set1 191("BF) h:=8323059+491520=8814579, hh:=558
+235: w0 218453 h:=8814579+218453=9033032, hh:=572
+[ ]
+236: fntnum1 current font is min10
+237: set2 17727("453F) type=0 h:=9033032+630598=9663630, hh:=612
+[䔿]
+240: pop
+level 1:(h=0,v=2228224,w=0,x=0,y=786432,z=0,hh=0,vv=141)
+241: y0 786432 v:=2228224+786432=3014656, vv:=191
+242: push
+level 1:(h=0,v=3014656,w=0,x=0,y=786432,z=0,hh=0,vv=191)
+243: right3 1310720 h:=0+1310720=1310720, hh:=83
+[ ]
+247: fntnum0 current font is ec-lmr10
+248: setchar109 ("6D) h:=1310720+546111=1856831, hh:=118
+249: setchar97 ("61) h:=1856831+327680=2184511, hh:=139
+250: setchar99 ("63) h:=2184511+291275=2475786, hh:=157
+251: setchar114 ("72) h:=2475786+256683=2732469, hh:=173
+252: setchar111 ("6F) h:=2732469+327680=3060149, hh:=194
+253: setchar58 ("3A) h:=3060149+182043=3242192, hh:=206
+254: setchar45 ("2D) h:=3242192+218453=3460645, hh:=220
+255: setchar62 ("3E) h:=3460645+509738=3970383, hh:=252
+[macro:->]
+256: set1 197("C5) h:=3970383+491520=4461903, hh:=283
+258: set1 191("BF) h:=4461903+491520=4953423, hh:=314
+260: w3 218453 h:=4953423+218453=5171876, hh:=328
+[ ]
+264: set1 197("C5) h:=5171876+491520=5663396, hh:=359
+266: set1 191("BF) h:=5663396+491520=6154916, hh:=390
+268: w0 218453 h:=6154916+218453=6373369, hh:=404
+[ ]
+269: fntnum1 current font is min10
+270: set2 17727("453F) type=0 h:=6373369+630598=7003967, hh:=444
+[䔿]
+273: pop
+level 1:(h=0,v=3014656,w=0,x=0,y=786432,z=0,hh=0,vv=191)
+274: y0 786432 v:=3014656+786432=3801088, vv:=241
+275: push
+level 1:(h=0,v=3801088,w=0,x=0,y=786432,z=0,hh=0,vv=241)
+276: push
+level 2:(h=0,v=3801088,w=0,x=0,y=786432,z=0,hh=0,vv=241)
+277: set2 8739("2223) type=0 h:=0+630598=630598, hh:=40
+[∣]
+280: pop
+level 2:(h=0,v=3801088,w=0,x=0,y=786432,z=0,hh=0,vv=241)
+281: right3 630598 h:=0+630598=630598, hh:=40
+[ ]
+285: fntnum0 current font is ec-lmr10
+286: setchar92 ("5C) h:=630598+327680=958278, hh:=61
+287: setchar106 ("6A) h:=958278+200245=1158523, hh:=74
+288: setchar111 ("6F) h:=1158523+327680=1486203, hh:=95
+289: setchar98 ("62) h:=1486203+364085=1850288, hh:=118
+290: setchar110 ("6E) h:=1850288+364085=2214373, hh:=141
+291: setchar97 ("61) h:=2214373+327680=2542053, hh:=162
+292: setchar109 ("6D) h:=2542053+546111=3088164, hh:=197
+293: setchar101 ("65) h:=3088164+291275=3379439, hh:=215
+294: setchar46 ("2E) h:=3379439+182043=3561482, hh:=227
+[\jobname.]
+295: pop
+level 1:(h=0,v=3801088,w=0,x=0,y=786432,z=0,hh=0,vv=241)
+296: y0 786432 v:=3801088+786432=4587520, vv:=291
+297: push
+level 1:(h=0,v=4587520,w=0,x=0,y=786432,z=0,hh=0,vv=291)
+298: push
+level 2:(h=0,v=4587520,w=0,x=0,y=786432,z=0,hh=0,vv=291)
+299: fntnum1 current font is min10
+300: set2 8739("2223) type=0 h:=0+630598=630598, hh:=40
+[∣]
+303: pop
+level 2:(h=0,v=4587520,w=0,x=0,y=786432,z=0,hh=0,vv=291)
+304: right3 630598 h:=0+630598=630598, hh:=40
+[ ]
+308: fntnum0 current font is ec-lmr10
+309: setchar111 ("6F) h:=630598+327680=958278, hh:=61
+310: setchar110 ("6E) h:=958278+364085=1322363, hh:=84
+311: setchar101 ("65) h:=1322363+291275=1613638, hh:=102
+312: setchar108 ("6C) h:=1613638+182043=1795681, hh:=114
+313: setchar101 ("65) h:=1795681+291275=2086956, hh:=132
+314: setchar116 ("74) h:=2086956+254870=2341826, hh:=148
+315: setchar116 ("74) h:=2341826+254870=2596696, hh:=164
+316: setchar101 ("65) h:=2596696+291275=2887971, hh:=182
+317: setchar114 ("72) h:=2887971+256683=3144654, hh:=198
+318: setchar46 ("2E) h:=3144654+182043=3326697, hh:=210
+[oneletter.]
+319: pop
+level 1:(h=0,v=4587520,w=0,x=0,y=786432,z=0,hh=0,vv=291)
+320: y0 786432 v:=4587520+786432=5373952, vv:=340
+321: push
+level 1:(h=0,v=5373952,w=0,x=0,y=786432,z=0,hh=0,vv=340)
+322: right3 1310720 h:=0+1310720=1310720, hh:=83
+326: setchar92 ("5C) h:=1310720+327680=1638400, hh:=104
+[ \]
+327: set1 196("C4) h:=1638400+491520=2129920, hh:=135
+329: setchar46 ("2E) h:=2129920+182043=2311963, hh:=147
+330: setchar92 ("5C) h:=2311963+327680=2639643, hh:=168
+[.\]
+331: set1 197("C5) h:=2639643+491520=3131163, hh:=199
+333: setchar46 ("2E) h:=3131163+182043=3313206, hh:=211
+334: setchar92 ("5C) h:=3313206+327680=3640886, hh:=232
+[.\]
+335: set1 255("FF) h:=3640886+327680=3968566, hh:=253
+337: setchar46 ("2E) h:=3968566+182043=4150609, hh:=265
+338: right3 291271 h:=4150609+291271=4441880, hh:=281
+[. ]
+342: set1 191("BF) h:=4441880+491520=4933400, hh:=312
+344: pop
+level 1:(h=0,v=5373952,w=0,x=0,y=786432,z=0,hh=0,vv=340)
+345: y0 786432 v:=5373952+786432=6160384, vv:=390
+346: push
+level 1:(h=0,v=6160384,w=0,x=0,y=786432,z=0,hh=0,vv=390)
+347: push
+level 2:(h=0,v=6160384,w=0,x=0,y=786432,z=0,hh=0,vv=390)
+348: fntnum1 current font is min10
+349: set2 8739("2223) type=0 h:=0+630598=630598, hh:=40
+[∣]
+352: pop
+level 2:(h=0,v=6160384,w=0,x=0,y=786432,z=0,hh=0,vv=390)
+353: right3 630598 h:=0+630598=630598, hh:=40
+[ ]
+357: fntnum0 current font is ec-lmr10
+358: setchar99 ("63) h:=630598+291275=921873, hh:=58
+359: setchar115 ("73) h:=921873+258506=1180379, hh:=74
+360: setchar110 ("6E) h:=1180379+364085=1544464, hh:=97
+361: setchar97 ("61) h:=1544464+327680=1872144, hh:=118
+362: setchar109 ("6D) h:=1872144+546111=2418255, hh:=153
+363: setchar101 ("65) h:=2418255+291275=2709530, hh:=171
+364: setchar49 ("31) h:=2709530+327680=3037210, hh:=192
+365: setchar46 ("2E) h:=3037210+182043=3219253, hh:=204
+[csname1.]
+366: pop
+level 1:(h=0,v=6160384,w=0,x=0,y=786432,z=0,hh=0,vv=390)
+367: y0 786432 v:=6160384+786432=6946816, vv:=440
+368: push
+level 1:(h=0,v=6946816,w=0,x=0,y=786432,z=0,hh=0,vv=440)
+369: right3 1310720 h:=0+1310720=1310720, hh:=83
+373: setchar92 ("5C) h:=1310720+327680=1638400, hh:=104
+[ \]
+374: set1 197("C5) h:=1638400+491520=2129920, hh:=135
+376: set1 191("BF) h:=2129920+491520=2621440, hh:=166
+378: setchar92 ("5C) h:=2621440+327680=2949120, hh:=187
+[\]
+379: set1 197("C5) h:=2949120+491520=3440640, hh:=218
+381: set1 191("BF) h:=3440640+491520=3932160, hh:=249
+383: setchar44 ("2C) h:=3932160+182043=4114203, hh:=261
+384: w3 218453 h:=4114203+218453=4332656, hh:=274
+388: setchar92 ("5C) h:=4332656+327680=4660336, hh:=295
+[, \]
+389: set1 197("C5) h:=4660336+491520=5151856, hh:=326
+391: set1 191("BF) h:=5151856+491520=5643376, hh:=357
+393: setchar44 ("2C) h:=5643376+182043=5825419, hh:=369
+394: w0 218453 h:=5825419+218453=6043872, hh:=383
+395: setchar92 ("5C) h:=6043872+327680=6371552, hh:=404
+[, \]
+396: fntnum1 current font is min10
+397: set2 17727("453F) type=0 h:=6371552+630598=7002150, hh:=444
+[䔿]
+400: pop
+level 1:(h=0,v=6946816,w=0,x=0,y=786432,z=0,hh=0,vv=440)
+401: y0 786432 v:=6946816+786432=7733248, vv:=490
+402: push
+level 1:(h=0,v=7733248,w=0,x=0,y=786432,z=0,hh=0,vv=490)
+403: right3 1310720 h:=0+1310720=1310720, hh:=83
+[ ]
+407: fntnum0 current font is ec-lmr10
+408: setchar92 ("5C) h:=1310720+327680=1638400, hh:=104
+[\]
+409: fntnum1 current font is min10
+410: set2 9250("2422) type=0 h:=1638400+630598=2268998, hh:=144
+[␢]
+413: fntnum0 current font is ec-lmr10
+414: setchar61 ("3D) h:=2268998+509738=2778736, hh:=176
+415: setchar62 ("3E) h:=2778736+509738=3288474, hh:=208
+416: right3 218453 h:=3288474+218453=3506927, hh:=222
+420: setchar104 ("68) h:=3506927+364085=3871012, hh:=245
+421: setchar111 ("6F) h:=3871012+327680=4198692, hh:=266
+422: setchar103 ("67) h:=4198692+327680=4526372, hh:=287
+423: setchar101 ("65) h:=4526372+291275=4817647, hh:=305
+424: setchar44 ("2C) h:=4817647+182043=4999690, hh:=317
+[=> hoge,]
+425: pop
+level 1:(h=0,v=7733248,w=0,x=0,y=786432,z=0,hh=0,vv=490)
+426: y0 786432 v:=7733248+786432=8519680, vv:=540
+427: push
+level 1:(h=0,v=8519680,w=0,x=0,y=786432,z=0,hh=0,vv=540)
+428: right3 1310720 h:=0+1310720=1310720, hh:=83
+432: setchar92 ("5C) h:=1310720+327680=1638400, hh:=104
+[ \]
+433: set1 227("E3) h:=1638400+327680=1966080, hh:=125
+435: set1 129("81) h:=1966080+491520=2457600, hh:=156
+437: w2 -18205 h:=2457600-18205=2439395, hh:=155
+440: set1 130("82) h:=2439395+473301=2912696, hh:=185
+442: setchar61 ("3D) h:=2912696+509738=3422434, hh:=217
+443: setchar62 ("3E) h:=3422434+509738=3932172, hh:=249
+444: right3 218453 h:=3932172+218453=4150625, hh:=263
+448: setchar112 ("70) h:=4150625+364085=4514710, hh:=286
+449: setchar105 ("69) h:=4514710+182043=4696753, hh:=298
+450: setchar121 ("79) h:=4696753+345898=5042651, hh:=320
+451: w0 -18205 h:=5042651-18205=5024446, hh:=319
+452: setchar111 ("6F) h:=5024446+327680=5352126, hh:=340
+453: setchar44 ("2C) h:=5352126+182043=5534169, hh:=352
+[=> piyo,]
+454: pop
+level 1:(h=0,v=8519680,w=0,x=0,y=786432,z=0,hh=0,vv=540)
+455: y0 786432 v:=8519680+786432=9306112, vv:=589
+456: push
+level 1:(h=0,v=9306112,w=0,x=0,y=786432,z=0,hh=0,vv=589)
+457: right3 1310720 h:=0+1310720=1310720, hh:=83
+461: setchar92 ("5C) h:=1310720+327680=1638400, hh:=104
+[ \]
+462: set1 227("E3) h:=1638400+327680=1966080, hh:=125
+464: set1 129("81) h:=1966080+491520=2457600, hh:=156
+466: w2 -18205 h:=2457600-18205=2439395, hh:=155
+469: set1 130("82) h:=2439395+473301=2912696, hh:=185
+471: setchar61 ("3D) h:=2912696+509738=3422434, hh:=217
+472: setchar62 ("3E) h:=3422434+509738=3932172, hh:=249
+473: right3 218453 h:=3932172+218453=4150625, hh:=263
+477: setchar112 ("70) h:=4150625+364085=4514710, hh:=286
+478: setchar105 ("69) h:=4514710+182043=4696753, hh:=298
+479: setchar121 ("79) h:=4696753+345898=5042651, hh:=320
+480: w0 -18205 h:=5042651-18205=5024446, hh:=319
+481: setchar111 ("6F) h:=5024446+327680=5352126, hh:=340
+482: setchar46 ("2E) h:=5352126+182043=5534169, hh:=352
+[=> piyo.]
+483: pop
+level 1:(h=0,v=9306112,w=0,x=0,y=786432,z=0,hh=0,vv=589)
+484: y0 786432 v:=9306112+786432=10092544, vv:=639
+485: push
+level 1:(h=0,v=10092544,w=0,x=0,y=786432,z=0,hh=0,vv=639)
+486: push
+level 2:(h=0,v=10092544,w=0,x=0,y=786432,z=0,hh=0,vv=639)
+487: fntnum1 current font is min10
+488: set2 8739("2223) type=0 h:=0+630598=630598, hh:=40
+[∣]
+491: pop
+level 2:(h=0,v=10092544,w=0,x=0,y=786432,z=0,hh=0,vv=639)
+492: right3 630598 h:=0+630598=630598, hh:=40
+[ ]
+496: fntnum0 current font is ec-lmr10
+497: setchar48 ("30) h:=630598+327680=958278, hh:=61
+498: setchar120 ("78) h:=958278+345898=1304176, hh:=83
+499: setchar70 ("46) h:=1304176+427818=1731994, hh:=110
+500: setchar70 ("46) h:=1731994+427818=2159812, hh:=137
+501: setchar46 ("2E) h:=2159812+182043=2341855, hh:=149
+[0xFF.]
+502: pop
+level 1:(h=0,v=10092544,w=0,x=0,y=786432,z=0,hh=0,vv=639)
+503: y0 786432 v:=10092544+786432=10878976, vv:=689
+504: push
+level 1:(h=0,v=10878976,w=0,x=0,y=786432,z=0,hh=0,vv=689)
+505: right3 1310720 h:=0+1310720=1310720, hh:=83
+[ ]
+509: fntnum1 current font is min10
+510: set2 9250("2422) type=0 h:=1310720+630598=1941318, hh:=123
+[␢]
+513: fntnum0 current font is ec-lmr10
+514: setchar44 ("2C) h:=1941318+182043=2123361, hh:=135
+515: w3 218453 h:=2123361+218453=2341814, hh:=148
+519: setchar40 ("28) h:=2341814+254870=2596684, hh:=164
+520: setchar92 ("5C) h:=2596684+327680=2924364, hh:=185
+[, (\]
+521: set1 255("FF) h:=2924364+327680=3252044, hh:=206
+523: set1 197("C5) h:=3252044+491520=3743564, hh:=237
+525: set1 255("FF) h:=3743564+327680=4071244, hh:=258
+527: set1 191("BF) h:=4071244+491520=4562764, hh:=289
+529: setchar58 ("3A) h:=4562764+182043=4744807, hh:=301
+530: setchar109 ("6D) h:=4744807+546111=5290918, hh:=336
+531: setchar97 ("61) h:=5290918+327680=5618598, hh:=357
+532: setchar99 ("63) h:=5618598+291275=5909873, hh:=375
+533: setchar114 ("72) h:=5909873+256683=6166556, hh:=391
+534: setchar111 ("6F) h:=6166556+327680=6494236, hh:=412
+535: setchar58 ("3A) h:=6494236+182043=6676279, hh:=424
+536: setchar45 ("2D) h:=6676279+218453=6894732, hh:=438
+537: setchar62 ("3E) h:=6894732+509738=7404470, hh:=470
+[:macro:->]
+538: fntnum1 current font is min10
+539: set2 9250("2422) type=0 h:=7404470+630598=8035068, hh:=510
+[␢]
+542: fntnum0 current font is ec-lmr10
+543: setchar41 ("29) h:=8035068+254870=8289938, hh:=526
+544: setchar44 ("2C) h:=8289938+182043=8471981, hh:=538
+545: w0 218453 h:=8471981+218453=8690434, hh:=550
+546: setchar92 ("5C) h:=8690434+327680=9018114, hh:=571
+[), \]
+547: set1 255("FF) h:=9018114+327680=9345794, hh:=592
+549: set1 197("C5) h:=9345794+491520=9837314, hh:=623
+551: set1 255("FF) h:=9837314+327680=10164994, hh:=644
+553: set1 191("BF) h:=10164994+491520=10656514, hh:=675
+555: setchar44 ("2C) h:=10656514+182043=10838557, hh:=687
+556: w0 218453 h:=10838557+218453=11057010, hh:=700
+557: setchar109 ("6D) h:=11057010+546111=11603121, hh:=735
+558: setchar97 ("61) h:=11603121+327680=11930801, hh:=756
+559: setchar99 ("63) h:=11930801+291275=12222076, hh:=774
+560: setchar114 ("72) h:=12222076+256683=12478759, hh:=790
+561: setchar111 ("6F) h:=12478759+327680=12806439, hh:=811
+562: setchar58 ("3A) h:=12806439+182043=12988482, hh:=823
+563: setchar45 ("2D) h:=12988482+218453=13206935, hh:=837
+564: setchar62 ("3E) h:=13206935+509738=13716673, hh:=869
+565: setchar40 ("28) h:=13716673+254870=13971543, hh:=885
+566: setchar92 ("5C) h:=13971543+327680=14299223, hh:=906
+[, macro:->(\]
+567: set1 255("FF) h:=14299223+327680=14626903, hh:=927
+569: set1 197("C5) h:=14626903+491520=15118423, hh:=958
+571: set1 255("FF) h:=15118423+327680=15446103, hh:=979
+573: set1 191("BF) h:=15446103+491520=15937623, hh:=1010
+575: setchar58 ("3A) h:=15937623+182043=16119666, hh:=1022
+576: setchar109 ("6D) h:=16119666+546111=16665777, hh:=1057
+577: setchar97 ("61) h:=16665777+327680=16993457, hh:=1078
+578: setchar99 ("63) h:=16993457+291275=17284732, hh:=1096
+579: setchar114 ("72) h:=17284732+256683=17541415, hh:=1112
+580: setchar111 ("6F) h:=17541415+327680=17869095, hh:=1133
+581: setchar58 ("3A) h:=17869095+182043=18051138, hh:=1145
+582: setchar45 ("2D) h:=18051138+218453=18269591, hh:=1159
+583: setchar62 ("3E) h:=18269591+509738=18779329, hh:=1191
+[:macro:->]
+584: fntnum1 current font is min10
+585: set2 9250("2422) type=0 h:=18779329+630598=19409927, hh:=1231
+[␢]
+588: fntnum0 current font is ec-lmr10
+589: setchar41 ("29) h:=19409927+254870=19664797, hh:=1247
+590: setchar46 ("2E) h:=19664797+182043=19846840, hh:=1259
+[).]
+591: pop
+level 1:(h=0,v=10878976,w=0,x=0,y=786432,z=0,hh=0,vv=689)
+592: y0 786432 v:=10878976+786432=11665408, vv:=739
+593: push
+level 1:(h=0,v=11665408,w=0,x=0,y=786432,z=0,hh=0,vv=739)
+594: right3 1310720 h:=0+1310720=1310720, hh:=83
+[ ]
+598: fntnum1 current font is min10
+599: set2 9252("2424) type=0 h:=1310720+630598=1941318, hh:=123
+[␤]
+602: fntnum0 current font is ec-lmr10
+603: setchar44 ("2C) h:=1941318+182043=2123361, hh:=135
+604: w3 218453 h:=2123361+218453=2341814, hh:=148
+608: setchar40 ("28) h:=2341814+254870=2596684, hh:=164
+609: setchar92 ("5C) h:=2596684+327680=2924364, hh:=185
+[, (\]
+610: set1 255("FF) h:=2924364+327680=3252044, hh:=206
+612: setchar58 ("3A) h:=3252044+182043=3434087, hh:=218
+613: setchar109 ("6D) h:=3434087+546111=3980198, hh:=253
+614: setchar97 ("61) h:=3980198+327680=4307878, hh:=274
+615: setchar99 ("63) h:=4307878+291275=4599153, hh:=292
+616: setchar114 ("72) h:=4599153+256683=4855836, hh:=308
+617: setchar111 ("6F) h:=4855836+327680=5183516, hh:=329
+618: setchar58 ("3A) h:=5183516+182043=5365559, hh:=341
+619: setchar45 ("2D) h:=5365559+218453=5584012, hh:=355
+620: setchar62 ("3E) h:=5584012+509738=6093750, hh:=387
+[:macro:->]
+621: fntnum1 current font is min10
+622: set2 9252("2424) type=0 h:=6093750+630598=6724348, hh:=427
+[␤]
+625: fntnum0 current font is ec-lmr10
+626: setchar41 ("29) h:=6724348+254870=6979218, hh:=443
+627: setchar44 ("2C) h:=6979218+182043=7161261, hh:=455
+628: w0 218453 h:=7161261+218453=7379714, hh:=467
+629: setchar92 ("5C) h:=7379714+327680=7707394, hh:=488
+[), \]
+630: set1 255("FF) h:=7707394+327680=8035074, hh:=509
+632: setchar44 ("2C) h:=8035074+182043=8217117, hh:=521
+633: w0 218453 h:=8217117+218453=8435570, hh:=534
+634: setchar109 ("6D) h:=8435570+546111=8981681, hh:=569
+635: setchar97 ("61) h:=8981681+327680=9309361, hh:=590
+636: setchar99 ("63) h:=9309361+291275=9600636, hh:=608
+637: setchar114 ("72) h:=9600636+256683=9857319, hh:=624
+638: setchar111 ("6F) h:=9857319+327680=10184999, hh:=645
+639: setchar58 ("3A) h:=10184999+182043=10367042, hh:=657
+640: setchar45 ("2D) h:=10367042+218453=10585495, hh:=671
+641: setchar62 ("3E) h:=10585495+509738=11095233, hh:=703
+642: setchar40 ("28) h:=11095233+254870=11350103, hh:=719
+643: setchar92 ("5C) h:=11350103+327680=11677783, hh:=740
+[, macro:->(\]
+644: set1 255("FF) h:=11677783+327680=12005463, hh:=761
+646: setchar58 ("3A) h:=12005463+182043=12187506, hh:=773
+647: setchar109 ("6D) h:=12187506+546111=12733617, hh:=808
+648: setchar97 ("61) h:=12733617+327680=13061297, hh:=829
+649: setchar99 ("63) h:=13061297+291275=13352572, hh:=847
+650: setchar114 ("72) h:=13352572+256683=13609255, hh:=863
+651: setchar111 ("6F) h:=13609255+327680=13936935, hh:=884
+652: setchar58 ("3A) h:=13936935+182043=14118978, hh:=896
+653: setchar45 ("2D) h:=14118978+218453=14337431, hh:=910
+654: setchar62 ("3E) h:=14337431+509738=14847169, hh:=942
+[:macro:->]
+655: fntnum1 current font is min10
+656: set2 9252("2424) type=0 h:=14847169+630598=15477767, hh:=982
+[␤]
+659: fntnum0 current font is ec-lmr10
+660: setchar41 ("29) h:=15477767+254870=15732637, hh:=998
+661: setchar46 ("2E) h:=15732637+182043=15914680, hh:=1010
+[).]
+662: pop
+level 1:(h=0,v=11665408,w=0,x=0,y=786432,z=0,hh=0,vv=739)
+663: y0 786432 v:=11665408+786432=12451840, vv:=789
+664: push
+level 1:(h=0,v=12451840,w=0,x=0,y=786432,z=0,hh=0,vv=789)
+665: push
+level 2:(h=0,v=12451840,w=0,x=0,y=786432,z=0,hh=0,vv=789)
+666: fntnum1 current font is min10
+667: set2 8739("2223) type=0 h:=0+630598=630598, hh:=40
+[∣]
+670: pop
+level 2:(h=0,v=12451840,w=0,x=0,y=786432,z=0,hh=0,vv=789)
+671: right3 630598 h:=0+630598=630598, hh:=40
+[ ]
+675: fntnum0 current font is ec-lmr10
+676: setchar99 ("63) h:=630598+291275=921873, hh:=58
+677: setchar111 ("6F) h:=921873+327680=1249553, hh:=79
+678: setchar110 ("6E) h:=1249553+364085=1613638, hh:=102
+679: right2 -18205 h:=1613638-18205=1595433, hh:=101
+682: setchar116 ("74) h:=1595433+254870=1850303, hh:=117
+683: setchar101 ("65) h:=1850303+291275=2141578, hh:=135
+684: setchar120 ("78) h:=2141578+345898=2487476, hh:=157
+685: setchar116 ("74) h:=2487476+254870=2742346, hh:=173
+686: setchar115 ("73) h:=2742346+258506=3000852, hh:=189
+687: setchar46 ("2E) h:=3000852+182043=3182895, hh:=201
+[contexts.]
+688: pop
+level 1:(h=0,v=12451840,w=0,x=0,y=786432,z=0,hh=0,vv=789)
+689: y0 786432 v:=12451840+786432=13238272, vv:=839
+690: push
+level 1:(h=0,v=13238272,w=0,x=0,y=786432,z=0,hh=0,vv=839)
+691: right3 1310720 h:=0+1310720=1310720, hh:=83
+[ ]
+695: fntnum1 current font is min10
+696: set2 9250("2422) type=0 h:=1310720+630598=1941318, hh:=123
+[␢]
+699: fntnum0 current font is ec-lmr10
+700: set1 197("C5) h:=1941318+491520=2432838, hh:=154
+702: set1 191("BF) h:=2432838+491520=2924358, hh:=185
+704: w3 218453 h:=2924358+218453=3142811, hh:=199
+708: setchar97 ("61) h:=3142811+327680=3470491, hh:=220
+[ a]
+709: set1 197("C5) h:=3470491+491520=3962011, hh:=251
+711: set1 191("BF) h:=3962011+491520=4453531, hh:=282
+713: w0 218453 h:=4453531+218453=4671984, hh:=296
+[ ]
+714: set1 197("C5) h:=4671984+491520=5163504, hh:=327
+716: set1 191("BF) h:=5163504+491520=5655024, hh:=358
+718: w0 218453 h:=5655024+218453=5873477, hh:=372
+[ ]
+719: fntnum1 current font is min10
+720: set2 17727("453F) type=0 h:=5873477+630598=6504075, hh:=412
+723: w0 218453 h:=6504075+218453=6722528, hh:=426
+[䔿 ]
+724: fntnum0 current font is ec-lmr10
+725: set1 197("C5) h:=6722528+491520=7214048, hh:=457
+727: set1 191("BF) h:=7214048+491520=7705568, hh:=488
+729: w0 218453 h:=7705568+218453=7924021, hh:=502
+[ ]
+730: set1 197("C5) h:=7924021+491520=8415541, hh:=533
+732: set1 191("BF) h:=8415541+491520=8907061, hh:=564
+734: w0 218453 h:=8907061+218453=9125514, hh:=578
+[ ]
+735: fntnum1 current font is min10
+736: set2 17727("453F) type=0 h:=9125514+630598=9756112, hh:=618
+739: x3 436906 h:=9756112+436906=10193018, hh:=646
+[䔿 ]
+743: fntnum0 current font is ec-lmr10
+744: set1 197("C5) h:=10193018+491520=10684538, hh:=677
+746: set1 191("BF) h:=10684538+491520=11176058, hh:=708
+748: w0 218453 h:=11176058+218453=11394511, hh:=722
+[ ]
+749: fntnum1 current font is min10
+750: set2 17727("453F) type=0 h:=11394511+630598=12025109, hh:=762
+753: x0 436906 h:=12025109+436906=12462015, hh:=789
+754: set2 9252("2424) type=0 h:=12462015+630598=13092613, hh:=829
+[䔿 ␤]
+757: fntnum0 current font is ec-lmr10
+758: setchar109 ("6D) h:=13092613+546111=13638724, hh:=864
+759: setchar97 ("61) h:=13638724+327680=13966404, hh:=885
+760: setchar99 ("63) h:=13966404+291275=14257679, hh:=903
+761: setchar114 ("72) h:=14257679+256683=14514362, hh:=919
+762: setchar111 ("6F) h:=14514362+327680=14842042, hh:=940
+763: setchar58 ("3A) h:=14842042+182043=15024085, hh:=952
+764: setchar45 ("2D) h:=15024085+218453=15242538, hh:=966
+765: setchar62 ("3E) h:=15242538+509738=15752276, hh:=998
+[macro:->]
+766: set1 197("C5) h:=15752276+491520=16243796, hh:=1029
+768: set1 191("BF) h:=16243796+491520=16735316, hh:=1060
+770: w0 218453 h:=16735316+218453=16953769, hh:=1074
+[ ]
+771: set1 197("C5) h:=16953769+491520=17445289, hh:=1105
+773: set1 191("BF) h:=17445289+491520=17936809, hh:=1136
+775: w0 218453 h:=17936809+218453=18155262, hh:=1150
+[ ]
+776: fntnum1 current font is min10
+777: set2 17727("453F) type=0 h:=18155262+630598=18785860, hh:=1190
+[䔿]
+780: pop
+level 1:(h=0,v=13238272,w=0,x=0,y=786432,z=0,hh=0,vv=839)
+781: y0 786432 v:=13238272+786432=14024704, vv:=888
+782: push
+level 1:(h=0,v=14024704,w=0,x=0,y=786432,z=0,hh=0,vv=888)
+783: right3 1310720 h:=0+1310720=1310720, hh:=83
+787: set2 9250("2422) type=0 h:=1310720+630598=1941318, hh:=123
+[ ␢]
+790: fntnum0 current font is ec-lmr10
+791: set1 197("C5) h:=1941318+491520=2432838, hh:=154
+793: set1 191("BF) h:=2432838+491520=2924358, hh:=185
+795: w3 218453 h:=2924358+218453=3142811, hh:=199
+[ ]
+799: set1 197("C5) h:=3142811+491520=3634331, hh:=230
+801: set1 191("BF) h:=3634331+491520=4125851, hh:=261
+803: fntnum1 current font is min10
+804: set2 17727("453F) type=0 h:=4125851+630598=4756449, hh:=301
+[䔿]
+807: fntnum0 current font is ec-lmr10
+808: set1 197("C5) h:=4756449+491520=5247969, hh:=332
+810: set1 191("BF) h:=5247969+491520=5739489, hh:=363
+812: w0 218453 h:=5739489+218453=5957942, hh:=377
+813: setchar97 ("61) h:=5957942+327680=6285622, hh:=398
+[ a]
+814: set1 197("C5) h:=6285622+491520=6777142, hh:=429
+816: set1 191("BF) h:=6777142+491520=7268662, hh:=460
+818: w0 218453 h:=7268662+218453=7487115, hh:=474
+[ ]
+819: set1 197("C5) h:=7487115+491520=7978635, hh:=505
+821: set1 191("BF) h:=7978635+491520=8470155, hh:=536
+823: w0 218453 h:=8470155+218453=8688608, hh:=550
+[ ]
+824: fntnum1 current font is min10
+825: set2 17727("453F) type=0 h:=8688608+630598=9319206, hh:=590
+828: w0 218453 h:=9319206+218453=9537659, hh:=604
+[䔿 ]
+829: fntnum0 current font is ec-lmr10
+830: set1 197("C5) h:=9537659+491520=10029179, hh:=635
+832: set1 191("BF) h:=10029179+491520=10520699, hh:=666
+834: w0 218453 h:=10520699+218453=10739152, hh:=680
+[ ]
+835: set1 197("C5) h:=10739152+491520=11230672, hh:=711
+837: set1 191("BF) h:=11230672+491520=11722192, hh:=742
+839: w0 218453 h:=11722192+218453=11940645, hh:=756
+[ ]
+840: fntnum1 current font is min10
+841: set2 17727("453F) type=0 h:=11940645+630598=12571243, hh:=796
+844: right3 436906 h:=12571243+436906=13008149, hh:=824
+[䔿 ]
+848: fntnum0 current font is ec-lmr10
+849: set1 197("C5) h:=13008149+491520=13499669, hh:=855
+851: set1 191("BF) h:=13499669+491520=13991189, hh:=886
+853: w0 218453 h:=13991189+218453=14209642, hh:=900
+[ ]
+854: fntnum1 current font is min10
+855: set2 17727("453F) type=0 h:=14209642+630598=14840240, hh:=940
+858: w0 218453 h:=14840240+218453=15058693, hh:=954
+859: set2 9252("2424) type=0 h:=15058693+630598=15689291, hh:=994
+[䔿 ␤]
+862: fntnum0 current font is ec-lmr10
+863: set1 255("FF) h:=15689291+327680=16016971, hh:=1015
+865: pop
+level 1:(h=0,v=14024704,w=0,x=0,y=786432,z=0,hh=0,vv=888)
+866: y0 786432 v:=14024704+786432=14811136, vv:=938
+867: push
+level 1:(h=0,v=14811136,w=0,x=0,y=786432,z=0,hh=0,vv=938)
+868: right3 1310720 h:=0+1310720=1310720, hh:=83
+[ ]
+872: fntnum1 current font is min10
+873: set2 9252("2424) type=0 h:=1310720+630598=1941318, hh:=123
+[␤]
+876: pop
+level 1:(h=0,v=14811136,w=0,x=0,y=786432,z=0,hh=0,vv=938)
+877: y0 786432 v:=14811136+786432=15597568, vv:=988
+878: push
+level 1:(h=0,v=15597568,w=0,x=0,y=786432,z=0,hh=0,vv=988)
+879: right3 1310720 h:=0+1310720=1310720, hh:=83
+[ ]
+883: fntnum0 current font is ec-lmr10
+884: setchar46 ("2E) h:=1310720+182043=1492763, hh:=95
+885: setchar97 ("61) h:=1492763+327680=1820443, hh:=116
+886: setchar46 ("2E) h:=1820443+182043=2002486, hh:=128
+887: setchar46 ("2E) h:=2002486+182043=2184529, hh:=140
+888: setchar46 ("2E) h:=2184529+182043=2366572, hh:=152
+889: setchar99 ("63) h:=2366572+291275=2657847, hh:=170
+890: setchar46 ("2E) h:=2657847+182043=2839890, hh:=182
+891: setchar98 ("62) h:=2839890+364085=3203975, hh:=205
+892: setchar46 ("2E) h:=3203975+182043=3386018, hh:=216
+893: setchar46 ("2E) h:=3386018+182043=3568061, hh:=228
+[.a...c.b..]
+894: pop
+level 1:(h=0,v=15597568,w=0,x=0,y=786432,z=0,hh=0,vv=988)
+895: y0 786432 v:=15597568+786432=16384000, vv:=1038
+896: push
+level 1:(h=0,v=16384000,w=0,x=0,y=786432,z=0,hh=0,vv=1038)
+897: right3 1310720 h:=0+1310720=1310720, hh:=83
+901: setchar46 ("2E) h:=1310720+182043=1492763, hh:=95
+902: setchar97 ("61) h:=1492763+327680=1820443, hh:=116
+903: setchar46 ("2E) h:=1820443+182043=2002486, hh:=128
+904: setchar46 ("2E) h:=2002486+182043=2184529, hh:=140
+905: setchar46 ("2E) h:=2184529+182043=2366572, hh:=152
+906: setchar99 ("63) h:=2366572+291275=2657847, hh:=170
+907: setchar46 ("2E) h:=2657847+182043=2839890, hh:=182
+908: setchar98 ("62) h:=2839890+364085=3203975, hh:=205
+909: setchar46 ("2E) h:=3203975+182043=3386018, hh:=216
+910: setchar46 ("2E) h:=3386018+182043=3568061, hh:=228
+[ .a...c.b..]
+911: pop
+level 1:(h=0,v=16384000,w=0,x=0,y=786432,z=0,hh=0,vv=1038)
+912: y0 786432 v:=16384000+786432=17170432, vv:=1088
+913: push
+level 1:(h=0,v=17170432,w=0,x=0,y=786432,z=0,hh=0,vv=1088)
+914: push
+level 2:(h=0,v=17170432,w=0,x=0,y=786432,z=0,hh=0,vv=1088)
+915: fntnum1 current font is min10
+916: set2 8739("2223) type=0 h:=0+630598=630598, hh:=40
+[∣]
+919: pop
+level 2:(h=0,v=17170432,w=0,x=0,y=786432,z=0,hh=0,vv=1088)
+920: right3 630598 h:=0+630598=630598, hh:=40
+[ ]
+924: fntnum0 current font is ec-lmr10
+925: setchar99 ("63) h:=630598+291275=921873, hh:=58
+926: setchar115 ("73) h:=921873+258506=1180379, hh:=74
+927: setchar110 ("6E) h:=1180379+364085=1544464, hh:=97
+928: setchar97 ("61) h:=1544464+327680=1872144, hh:=118
+929: setchar109 ("6D) h:=1872144+546111=2418255, hh:=153
+930: setchar101 ("65) h:=2418255+291275=2709530, hh:=171
+931: setchar50 ("32) h:=2709530+327680=3037210, hh:=192
+932: setchar46 ("2E) h:=3037210+182043=3219253, hh:=204
+[csname2.]
+933: pop
+level 1:(h=0,v=17170432,w=0,x=0,y=786432,z=0,hh=0,vv=1088)
+934: y0 786432 v:=17170432+786432=17956864, vv:=1137
+935: push
+level 1:(h=0,v=17956864,w=0,x=0,y=786432,z=0,hh=0,vv=1137)
+936: right3 1310720 h:=0+1310720=1310720, hh:=83
+940: setchar83 ("53) h:=1310720+364085=1674805, hh:=106
+941: setchar72 ("48) h:=1674805+491520=2166325, hh:=137
+942: setchar79 ("4F) h:=2166325+509738=2676063, hh:=169
+943: right2 -18205 h:=2676063-18205=2657858, hh:=168
+946: setchar87 ("57) h:=2657858+673578=3331436, hh:=211
+947: right3 218453 h:=3331436+218453=3549889, hh:=225
+951: setchar46 ("2E) h:=3549889+182043=3731932, hh:=237
+952: setchar46 ("2E) h:=3731932+182043=3913975, hh:=249
+953: setchar46 ("2E) h:=3913975+182043=4096018, hh:=261
+[ SHOW ...]
+954: pop
+level 1:(h=0,v=17956864,w=0,x=0,y=786432,z=0,hh=0,vv=1137)
+955: y0 786432 v:=17956864+786432=18743296, vv:=1187
+956: push
+level 1:(h=0,v=18743296,w=0,x=0,y=786432,z=0,hh=0,vv=1187)
+957: right3 1310720 h:=0+1310720=1310720, hh:=83
+961: setchar83 ("53) h:=1310720+364085=1674805, hh:=106
+962: setchar84 ("54) h:=1674805+473301=2148106, hh:=136
+963: setchar82 ("52) h:=2148106+482410=2630516, hh:=167
+964: setchar73 ("49) h:=2630516+236650=2867166, hh:=182
+965: setchar78 ("4E) h:=2867166+491520=3358686, hh:=213
+966: setchar71 ("47) h:=3358686+514261=3872947, hh:=246
+967: right3 218453 h:=3872947+218453=4091400, hh:=259
+971: setchar92 ("5C) h:=4091400+327680=4419080, hh:=280
+[ STRING \]
+972: set1 197("C5) h:=4419080+491520=4910600, hh:=311
+974: set1 191("BF) h:=4910600+491520=5402120, hh:=342
+976: setchar46 ("2E) h:=5402120+182043=5584163, hh:=354
+977: setchar92 ("5C) h:=5584163+327680=5911843, hh:=375
+[.\]
+978: fntnum1 current font is min10
+979: set2 17727("453F) type=0 h:=5911843+630598=6542441, hh:=415
+[䔿]
+982: fntnum0 current font is ec-lmr10
+983: setchar46 ("2E) h:=6542441+182043=6724484, hh:=427
+984: setchar92 ("5C) h:=6724484+327680=7052164, hh:=448
+[.\]
+985: set1 197("C5) h:=7052164+491520=7543684, hh:=479
+987: set1 191("BF) h:=7543684+491520=8035204, hh:=510
+989: setchar46 ("2E) h:=8035204+182043=8217247, hh:=522
+990: setchar92 ("5C) h:=8217247+327680=8544927, hh:=543
+[.\]
+991: fntnum1 current font is min10
+992: set2 17727("453F) type=0 h:=8544927+630598=9175525, hh:=583
+[䔿]
+995: pop
+level 1:(h=0,v=18743296,w=0,x=0,y=786432,z=0,hh=0,vv=1187)
+996: y0 786432 v:=18743296+786432=19529728, vv:=1237
+997: push
+level 1:(h=0,v=19529728,w=0,x=0,y=786432,z=0,hh=0,vv=1237)
+998: right3 1310720 h:=0+1310720=1310720, hh:=83
+[ ]
+1002: fntnum0 current font is ec-lmr10
+1003: setchar77 ("4D) h:=1310720+600768=1911488, hh:=121
+1004: setchar69 ("45) h:=1911488+446005=2357493, hh:=149
+1005: setchar65 ("41) h:=2357493+491520=2849013, hh:=180
+1006: setchar78 ("4E) h:=2849013+491520=3340533, hh:=211
+1007: setchar73 ("49) h:=3340533+236650=3577183, hh:=226
+1008: setchar78 ("4E) h:=3577183+491520=4068703, hh:=257
+1009: setchar71 ("47) h:=4068703+514261=4582964, hh:=290
+1010: right3 218453 h:=4582964+218453=4801417, hh:=304
+1014: setchar109 ("6D) h:=4801417+546111=5347528, hh:=339
+1015: setchar97 ("61) h:=5347528+327680=5675208, hh:=360
+1016: setchar99 ("63) h:=5675208+291275=5966483, hh:=378
+1017: setchar114 ("72) h:=5966483+256683=6223166, hh:=394
+1018: setchar111 ("6F) h:=6223166+327680=6550846, hh:=415
+1019: setchar58 ("3A) h:=6550846+182043=6732889, hh:=427
+1020: setchar45 ("2D) h:=6732889+218453=6951342, hh:=441
+1021: setchar62 ("3E) h:=6951342+509738=7461080, hh:=473
+1022: setchar97 ("61) h:=7461080+327680=7788760, hh:=494
+1023: setchar46 ("2E) h:=7788760+182043=7970803, hh:=506
+1024: setchar109 ("6D) h:=7970803+546111=8516914, hh:=541
+1025: setchar97 ("61) h:=8516914+327680=8844594, hh:=562
+1026: setchar99 ("63) h:=8844594+291275=9135869, hh:=580
+1027: setchar114 ("72) h:=9135869+256683=9392552, hh:=596
+1028: setchar111 ("6F) h:=9392552+327680=9720232, hh:=617
+1029: setchar58 ("3A) h:=9720232+182043=9902275, hh:=629
+1030: setchar45 ("2D) h:=9902275+218453=10120728, hh:=643
+1031: setchar62 ("3E) h:=10120728+509738=10630466, hh:=675
+1032: setchar98 ("62) h:=10630466+364085=10994551, hh:=698
+1033: setchar46 ("2E) h:=10994551+182043=11176594, hh:=710
+1034: setchar109 ("6D) h:=11176594+546111=11722705, hh:=745
+1035: setchar97 ("61) h:=11722705+327680=12050385, hh:=765
+1036: setchar99 ("63) h:=12050385+291275=12341660, hh:=783
+1037: setchar114 ("72) h:=12341660+256683=12598343, hh:=799
+1038: setchar111 ("6F) h:=12598343+327680=12926023, hh:=820
+1039: setchar58 ("3A) h:=12926023+182043=13108066, hh:=832
+1040: setchar45 ("2D) h:=13108066+218453=13326519, hh:=846
+1041: setchar62 ("3E) h:=13326519+509738=13836257, hh:=878
+1042: setchar97 ("61) h:=13836257+327680=14163937, hh:=899
+1043: setchar46 ("2E) h:=14163937+182043=14345980, hh:=911
+1044: setchar109 ("6D) h:=14345980+546111=14892091, hh:=945
+1045: setchar97 ("61) h:=14892091+327680=15219771, hh:=966
+1046: setchar99 ("63) h:=15219771+291275=15511046, hh:=984
+1047: setchar114 ("72) h:=15511046+256683=15767729, hh:=1000
+1048: setchar111 ("6F) h:=15767729+327680=16095409, hh:=1021
+1049: setchar58 ("3A) h:=16095409+182043=16277452, hh:=1033
+1050: setchar45 ("2D) h:=16277452+218453=16495905, hh:=1047
+1051: setchar62 ("3E) h:=16495905+509738=17005643, hh:=1079
+1052: setchar98 ("62) h:=17005643+364085=17369728, hh:=1102
+1053: setchar46 ("2E) h:=17369728+182043=17551771, hh:=1114
+[MEANING macro:->a.macro:->b.macro:->a.macro:->b.]
+1054: pop
+level 1:(h=0,v=19529728,w=0,x=0,y=786432,z=0,hh=0,vv=1237)
+1055: pop
+level 0:(h=0,v=42152922,w=0,x=0,y=0,z=0,hh=0,vv=2670)
+1056: down3 1572864 v:=42152922+1572864=43725786, vv:=2770
+1060: eop
diff --git a/Build/source/texk/web2c/ptexdir/tests/pknj-e.log b/Build/source/texk/web2c/ptexdir/tests/pknj-e.log
new file mode 100644
index 00000000000..4f42dab30bb
--- /dev/null
+++ b/Build/source/texk/web2c/ptexdir/tests/pknj-e.log
@@ -0,0 +1,223 @@
+ restricted \write18 enabled.
+ %&-line parsing enabled.
+**printkanji
+(./printkanji.tex
+\openout1 = `p-pknj-e.out'.
+
+
+{MSG ^^c5^^bf ^^c5^^bf 顛 }
+{TOT ^^c5^^bf ^^c5^^bf 顛 }
+> \A=macro:
+->^^c5^^bf ^^c5^^bf 顛 .
+l.29 \show\A
+
+
+■\meaning. {MSG macro:->macro:->^^c5^^bf ^^c5^^bf 顛 }
+{TOT macro:->macro:->^^c5^^bf ^^c5^^bf 顛 }
+> \B=macro:
+->macro:->^^c5^^bf ^^c5^^bf 顛 .
+l.40 \show\B
+
+
+■\jobname. {MSG *あ*p-pknj-e*:p-pknj-e:*macro:->:p-pknj-e:*}
+{TOT *あ*p-pknj-e*:p-pknj-e:*macro:->:p-pknj-e:*}
+
+■oneletter. {MSG \catcode`\^^c5= 197}
+{TOT \catcode`\^^c5= 197}
+{MSG \catcode`^^c5 = 197}
+{TOT \catcode`^^c5 = 197}
+{MSG \catcode`^^c5^^bf = 197}
+{TOT \catcode`^^c5^^bf = 197}
+{MSG \catcode`顛 = 50623}
+{TOT \catcode`顛 = 50623}
+■csname1.
+> \顛=macro:
+->hoge.
+l.72 \show\顛
+
+
+> \^^c5^^bf=macro:
+->piyo.
+l.73 \show\^^c5^^bf
+
+
+> \^^c5^^bf=macro:
+->piyo.
+<recently read> \^^c5^^bf
+
+l.74 \expandafter\show\csname ^^c5^^bf\endcsname
+
+
+{MSG \顛=>macro:->hoge}
+{TOT \顛=>macro:->hoge}
+{MSG \^^c5^^bf=>macro:->piyo}
+{TOT \^^c5^^bf=>macro:->piyo}
+{MSG \^^c5^^bf=>macro:->piyo}
+{TOT \^^c5^^bf=>macro:->piyo}
+{MSG \^^c5^^bf, \^^c5^^bf, \^^c5^^bf, \顛.}
+{TOT \^^c5^^bf, \^^c5^^bf, \^^c5^^bf, \顛.}
+
+{MSG \あ=> hoge, \^^a4^^a2=> piyo. \^^a4^^a2=> piyo. }
+{TOT \あ=> hoge, \^^a4^^a2=> piyo. \^^a4^^a2=> piyo. }
+■0xFF.
+{MSG あ, ^^c5^^bf ^^c5^^bf 顛 , \^^ff^^c5^^ff^^bf, macro:->(\^^ff^^c5^^ff^^bf:m
+acro:->あ).}
+{TOT あ, ^^c5^^bf ^^c5^^bf 顛 , \^^ff^^c5^^ff^^bf, macro:->(\^^ff^^c5^^ff^^bf:m
+acro:->あ).}
+{MSG い, (\^^ff:macro:->い), \^^ff, macro:->(\^^ff:macro:->い).}
+{TOT い, (\^^ff:macro:->い), \^^ff, macro:->(\^^ff:macro:->い).}
+■contexts.
+! Undefined control sequence.
+\@->\A \undefined
+
+\a ->^^c5^^bf a^^c5^^bf ^^c5^^bf 顛 \@
+ ^^c5^^bf 顛
+\+#1->\^^c5^^bf \顛 \^^ff^^c5^^ff^^bf #1
+ \^^ff macro:->^^c5^^bf ^^c5^^bf 顛 ...
+l.136 \+\a
+
+The control sequence at the end of the top line
+of your error message was never \def'ed. If you have
+misspelled it (e.g., `\hobx'), type `I' and the correct
+spelling (e.g., `I\hbox'). Otherwise just continue,
+and I'll forget about whatever was undefined.
+
+! Undefined control sequence.
+\@->\A \undefined
+
+\a ->^^c5^^bf a^^c5^^bf ^^c5^^bf 顛 \@
+ ^^c5^^bf 顛
+l.138 ...5^^ff^^bf ^^c5^^bf \^^c5^^bf ^^c5^^bf顛\a
+ \^^ff ^^ff\^^c5^^bf
+The control sequence at the end of the top line
+of your error message was never \def'ed. If you have
+misspelled it (e.g., `\hobx'), type `I' and the correct
+spelling (e.g., `I\hbox'). Otherwise just continue,
+and I'll forget about whatever was undefined.
+
+! Undefined control sequence.
+l.140 \^^c5
+ \^^ff\^^c4\^^fe
+The control sequence at the end of the top line
+of your error message was never \def'ed. If you have
+misspelled it (e.g., `\hobx'), type `I' and the correct
+spelling (e.g., `I\hbox'). Otherwise just continue,
+and I'll forget about whatever was undefined.
+
+! Undefined control sequence.
+l.140 \^^c5\^^ff\^^c4
+ \^^fe
+The control sequence at the end of the top line
+of your error message was never \def'ed. If you have
+misspelled it (e.g., `\hobx'), type `I' and the correct
+spelling (e.g., `I\hbox'). Otherwise just continue,
+and I'll forget about whatever was undefined.
+
+! Undefined control sequence.
+l.140 \^^c5\^^ff\^^c4\^^fe
+
+The control sequence at the end of the top line
+of your error message was never \def'ed. If you have
+misspelled it (e.g., `\hobx'), type `I' and the correct
+spelling (e.g., `I\hbox'). Otherwise just continue,
+and I'll forget about whatever was undefined.
+
+\^^c5^^bf\^^ff^^c5
+! Undefined control sequence.
+\b ->\^^c5^^be
+ .\^^c5^^bf .\^^c5^^bd ..\転 .\顛 .\貼 .
+l.154 \b
+
+The control sequence at the end of the top line
+of your error message was never \def'ed. If you have
+misspelled it (e.g., `\hobx'), type `I' and the correct
+spelling (e.g., `I\hbox'). Otherwise just continue,
+and I'll forget about whatever was undefined.
+
+! Undefined control sequence.
+\b ->\^^c5^^be .\^^c5^^bf .\^^c5^^bd
+ ..\転 .\顛 .\貼 .
+l.154 \b
+
+The control sequence at the end of the top line
+of your error message was never \def'ed. If you have
+misspelled it (e.g., `\hobx'), type `I' and the correct
+spelling (e.g., `I\hbox'). Otherwise just continue,
+and I'll forget about whatever was undefined.
+
+! Undefined control sequence.
+\b ...5^^be .\^^c5^^bf .\^^c5^^bd ..\転 .\顛 .\貼
+ .
+l.154 \b
+
+The control sequence at the end of the top line
+of your error message was never \def'ed. If you have
+misspelled it (e.g., `\hobx'), type `I' and the correct
+spelling (e.g., `I\hbox'). Otherwise just continue,
+and I'll forget about whatever was undefined.
+
+! Undefined control sequence.
+l.156 \^^c5^^be
+ .\^^c5^^bf.\^^c5^^bd..\転.\顛.\貼.
+The control sequence at the end of the top line
+of your error message was never \def'ed. If you have
+misspelled it (e.g., `\hobx'), type `I' and the correct
+spelling (e.g., `I\hbox'). Otherwise just continue,
+and I'll forget about whatever was undefined.
+
+! Undefined control sequence.
+l.156 \^^c5^^be.\^^c5^^bf.\^^c5^^bd
+ ..\転.\顛.\貼.
+The control sequence at the end of the top line
+of your error message was never \def'ed. If you have
+misspelled it (e.g., `\hobx'), type `I' and the correct
+spelling (e.g., `I\hbox'). Otherwise just continue,
+and I'll forget about whatever was undefined.
+
+! Undefined control sequence.
+l.156 \^^c5^^be.\^^c5^^bf.\^^c5^^bd..\転.\顛.\貼
+ .
+The control sequence at the end of the top line
+of your error message was never \def'ed. If you have
+misspelled it (e.g., `\hobx'), type `I' and the correct
+spelling (e.g., `I\hbox'). Otherwise just continue,
+and I'll forget about whatever was undefined.
+
+■csname2.
+> \^^c5^^bf=macro:
+->a.
+l.162 SHOW \show\^^c5^^bf
+ .\show\顛.%
+
+> \顛=macro:
+->b.
+l.162 SHOW \show\^^c5^^bf.\show\顛
+ .%
+
+> \^^c5^^bf=macro:
+->a.
+<recently read> \^^c5^^bf
+
+l.163 \expandafter\show\csname ^^c5^^bf\endcsname
+ .%
+
+> \顛=macro:
+->b.
+<recently read> \顛
+
+l.164 \expandafter\show\csname 顛\endcsname
+
+
+\^^e3^^81^^82\あ [50623]
+Memory usage before: 1270&5512; after: 118&5077; still untouched: 4992459
+ )
+Here is how much of TeX's memory you used:
+ 25 strings out of 497935
+ 137 string characters out of 6219478
+ 7541 words of memory out of 5000000
+ 969 multiletter control sequences out of 15000+600000
+ 3271 words of font info for 2 fonts, out of 8000000 for 9000
+ 0 hyphenation exceptions out of 8191
+ 6i,4n,2p,97b,22s stack positions out of 10000i,1000n,20000p,200000b,200000s
+
+Output written on p-pknj-e.dvi (1 page, 1140 bytes).
diff --git a/Build/source/texk/web2c/ptexdir/tests/pknj-s.log b/Build/source/texk/web2c/ptexdir/tests/pknj-s.log
new file mode 100644
index 00000000000..7f753e894b5
--- /dev/null
+++ b/Build/source/texk/web2c/ptexdir/tests/pknj-s.log
@@ -0,0 +1,223 @@
+ restricted \write18 enabled.
+ %&-line parsing enabled.
+**printkanji
+(./printkanji.tex
+\openout1 = `p-pknj-s.out'.
+
+
+{MSG ^^c5^^bf ^^c5^^bf 顛 }
+{TOT ^^c5^^bf ^^c5^^bf 顛 }
+> \A=macro:
+->^^c5^^bf ^^c5^^bf 顛 .
+l.29 \show\A
+
+
+■\meaning. {MSG macro:->macro:->^^c5^^bf ^^c5^^bf 顛 }
+{TOT macro:->macro:->^^c5^^bf ^^c5^^bf 顛 }
+> \B=macro:
+->macro:->^^c5^^bf ^^c5^^bf 顛 .
+l.40 \show\B
+
+
+■\jobname. {MSG *あ*p-pknj-s*:p-pknj-s:*macro:->:p-pknj-s:*}
+{TOT *あ*p-pknj-s*:p-pknj-s:*macro:->:p-pknj-s:*}
+
+■oneletter. {MSG \catcode`\^^c5= 197}
+{TOT \catcode`\^^c5= 197}
+{MSG \catcode`^^c5 = 197}
+{TOT \catcode`^^c5 = 197}
+{MSG \catcode`^^c5^^bf = 197}
+{TOT \catcode`^^c5^^bf = 197}
+{MSG \catcode`顛 = 37726}
+{TOT \catcode`顛 = 37726}
+■csname1.
+> \顛=macro:
+->hoge.
+l.72 \show\顛
+
+
+> \^^c5^^bf=macro:
+->piyo.
+l.73 \show\^^c5^^bf
+
+
+> \^^c5^^bf=macro:
+->piyo.
+<recently read> \^^c5^^bf
+
+l.74 \expandafter\show\csname ^^c5^^bf\endcsname
+
+
+{MSG \顛=>macro:->hoge}
+{TOT \顛=>macro:->hoge}
+{MSG \^^c5^^bf=>macro:->piyo}
+{TOT \^^c5^^bf=>macro:->piyo}
+{MSG \^^c5^^bf=>macro:->piyo}
+{TOT \^^c5^^bf=>macro:->piyo}
+{MSG \^^c5^^bf, \^^c5^^bf, \^^c5^^bf, \顛.}
+{TOT \^^c5^^bf, \^^c5^^bf, \^^c5^^bf, \顛.}
+
+{MSG \あ=> hoge, \^^e3^^81^^82=> piyo. \^^e3^^81^^82=> piyo. }
+{TOT \あ=> hoge, \^^e3^^81^^82=> piyo. \^^e3^^81^^82=> piyo. }
+■0xFF.
+{MSG あ, ^^c5^^bf ^^c5^^bf 顛 , \^^ff^^c5^^ff^^bf, macro:->(\^^ff^^c5^^ff^^bf:m
+acro:->あ).}
+{TOT あ, ^^c5^^bf ^^c5^^bf 顛 , \^^ff^^c5^^ff^^bf, macro:->(\^^ff^^c5^^ff^^bf:m
+acro:->あ).}
+{MSG い, (\^^ff:macro:->い), \^^ff, macro:->(\^^ff:macro:->い).}
+{TOT い, (\^^ff:macro:->い), \^^ff, macro:->(\^^ff:macro:->い).}
+■contexts.
+! Undefined control sequence.
+\@->\A \undefined
+
+\a ->^^c5^^bf a^^c5^^bf ^^c5^^bf 顛 \@
+ ^^c5^^bf 顛
+\+#1->\^^c5^^bf \顛 \^^ff^^c5^^ff^^bf #1
+ \^^ff macro:->^^c5^^bf ^^c5^^bf 顛 ...
+l.136 \+\a
+
+The control sequence at the end of the top line
+of your error message was never \def'ed. If you have
+misspelled it (e.g., `\hobx'), type `I' and the correct
+spelling (e.g., `I\hbox'). Otherwise just continue,
+and I'll forget about whatever was undefined.
+
+! Undefined control sequence.
+\@->\A \undefined
+
+\a ->^^c5^^bf a^^c5^^bf ^^c5^^bf 顛 \@
+ ^^c5^^bf 顛
+l.138 ...5^^ff^^bf ^^c5^^bf \^^c5^^bf ^^c5^^bf顛\a
+ \^^ff ^^ff\^^c5^^bf
+The control sequence at the end of the top line
+of your error message was never \def'ed. If you have
+misspelled it (e.g., `\hobx'), type `I' and the correct
+spelling (e.g., `I\hbox'). Otherwise just continue,
+and I'll forget about whatever was undefined.
+
+! Undefined control sequence.
+l.140 \^^c5
+ \^^ff\^^c4\^^fe
+The control sequence at the end of the top line
+of your error message was never \def'ed. If you have
+misspelled it (e.g., `\hobx'), type `I' and the correct
+spelling (e.g., `I\hbox'). Otherwise just continue,
+and I'll forget about whatever was undefined.
+
+! Undefined control sequence.
+l.140 \^^c5\^^ff\^^c4
+ \^^fe
+The control sequence at the end of the top line
+of your error message was never \def'ed. If you have
+misspelled it (e.g., `\hobx'), type `I' and the correct
+spelling (e.g., `I\hbox'). Otherwise just continue,
+and I'll forget about whatever was undefined.
+
+! Undefined control sequence.
+l.140 \^^c5\^^ff\^^c4\^^fe
+
+The control sequence at the end of the top line
+of your error message was never \def'ed. If you have
+misspelled it (e.g., `\hobx'), type `I' and the correct
+spelling (e.g., `I\hbox'). Otherwise just continue,
+and I'll forget about whatever was undefined.
+
+\^^c5^^bf\^^ff^^c5
+! Undefined control sequence.
+\b ->\^^c5^^be
+ .\^^c5^^bf .\^^c5^^bd ..\転 .\顛 .\貼 .
+l.154 \b
+
+The control sequence at the end of the top line
+of your error message was never \def'ed. If you have
+misspelled it (e.g., `\hobx'), type `I' and the correct
+spelling (e.g., `I\hbox'). Otherwise just continue,
+and I'll forget about whatever was undefined.
+
+! Undefined control sequence.
+\b ->\^^c5^^be .\^^c5^^bf .\^^c5^^bd
+ ..\転 .\顛 .\貼 .
+l.154 \b
+
+The control sequence at the end of the top line
+of your error message was never \def'ed. If you have
+misspelled it (e.g., `\hobx'), type `I' and the correct
+spelling (e.g., `I\hbox'). Otherwise just continue,
+and I'll forget about whatever was undefined.
+
+! Undefined control sequence.
+\b ...5^^be .\^^c5^^bf .\^^c5^^bd ..\転 .\顛 .\貼
+ .
+l.154 \b
+
+The control sequence at the end of the top line
+of your error message was never \def'ed. If you have
+misspelled it (e.g., `\hobx'), type `I' and the correct
+spelling (e.g., `I\hbox'). Otherwise just continue,
+and I'll forget about whatever was undefined.
+
+! Undefined control sequence.
+l.156 \^^c5^^be
+ .\^^c5^^bf.\^^c5^^bd..\転.\顛.\貼.
+The control sequence at the end of the top line
+of your error message was never \def'ed. If you have
+misspelled it (e.g., `\hobx'), type `I' and the correct
+spelling (e.g., `I\hbox'). Otherwise just continue,
+and I'll forget about whatever was undefined.
+
+! Undefined control sequence.
+l.156 \^^c5^^be.\^^c5^^bf.\^^c5^^bd
+ ..\転.\顛.\貼.
+The control sequence at the end of the top line
+of your error message was never \def'ed. If you have
+misspelled it (e.g., `\hobx'), type `I' and the correct
+spelling (e.g., `I\hbox'). Otherwise just continue,
+and I'll forget about whatever was undefined.
+
+! Undefined control sequence.
+l.156 \^^c5^^be.\^^c5^^bf.\^^c5^^bd..\転.\顛.\貼
+ .
+The control sequence at the end of the top line
+of your error message was never \def'ed. If you have
+misspelled it (e.g., `\hobx'), type `I' and the correct
+spelling (e.g., `I\hbox'). Otherwise just continue,
+and I'll forget about whatever was undefined.
+
+■csname2.
+> \^^c5^^bf=macro:
+->a.
+l.162 SHOW \show\^^c5^^bf
+ .\show\顛.%
+
+> \顛=macro:
+->b.
+l.162 SHOW \show\^^c5^^bf.\show\顛
+ .%
+
+> \^^c5^^bf=macro:
+->a.
+<recently read> \^^c5^^bf
+
+l.163 \expandafter\show\csname ^^c5^^bf\endcsname
+ .%
+
+> \顛=macro:
+->b.
+<recently read> \顛
+
+l.164 \expandafter\show\csname 顛\endcsname
+
+
+\^^e3^^81^^82\あ [37726]
+Memory usage before: 1274&5515; after: 118&5078; still untouched: 4992456
+ )
+Here is how much of TeX's memory you used:
+ 24 strings out of 497935
+ 135 string characters out of 6219475
+ 7544 words of memory out of 5000000
+ 968 multiletter control sequences out of 15000+600000
+ 3271 words of font info for 2 fonts, out of 8000000 for 9000
+ 0 hyphenation exceptions out of 8191
+ 6i,4n,2p,97b,22s stack positions out of 10000i,1000n,20000p,200000b,200000s
+
+Output written on p-pknj-s.dvi (1 page, 1148 bytes).
diff --git a/Build/source/texk/web2c/ptexdir/tests/printkanji.tex b/Build/source/texk/web2c/ptexdir/tests/printkanji.tex
index a8f1d4cb5a0..d176d9a53bf 100644
--- a/Build/source/texk/web2c/ptexdir/tests/printkanji.tex
+++ b/Build/source/texk/web2c/ptexdir/tests/printkanji.tex
@@ -2,7 +2,16 @@
\tracingstats=1000
\noautoxspacing
\newlinechar=`\^^J
-\font\x=ec-lmtt10 \x
+\font\x=ec-lmr10 \x
+\ifx\kanjiskip\undefined\else
+ \ifnum\jis"2121="3000
+ \jfont\jpy=umin10 at 10pt\jpy
+ \else
+ \jfont\jpy=min10 at 10pt\jpy
+ \fi
+\fi
+
+
\immediate\openout1=\jobname.out
\def\MSG#1{%
\message{\string{MSG #1\string}}%
@@ -135,7 +144,6 @@ A: \A
\catcode`\^^bf=11
\catcode`\^^bd=11
\catcode`\^^ff=11
-\font\x=ec-lmtt10 \x\scrollmode
\def\^^c5^^bf{a}
\def\顛{b}
diff --git a/Build/source/texk/web2c/ptexdir/tests/ptexlineendmode.tex b/Build/source/texk/web2c/ptexdir/tests/ptexlineendmode.tex
index 5dce40d2e12..cf38ff5e8d6 100644
--- a/Build/source/texk/web2c/ptexdir/tests/ptexlineendmode.tex
+++ b/Build/source/texk/web2c/ptexdir/tests/ptexlineendmode.tex
@@ -1,4 +1,12 @@
-\font\fn=ec-lmr10\fn\catcode`\^^9f=11
+\font\fn=ec-lmr10\fn
+\ifx\kanjiskip\undefined\else
+ \ifnum\jis"2121="3000
+ \jfont\jpy=umin10 at 10pt\jpy
+ \else
+ \jfont\jpy=min10 at 10pt\jpy
+ \fi
+\fi
+\catcode`\^^9f=11
\def\◆{◇}\def\漢{◇}\def\^^9f{S}
\ptexlineendmode=0 % p4.0.0標準
diff --git a/Build/source/texk/web2c/ptexdir/tests/ucs-e.log b/Build/source/texk/web2c/ptexdir/tests/ucs-e.log
new file mode 100644
index 00000000000..33428e2db9e
--- /dev/null
+++ b/Build/source/texk/web2c/ptexdir/tests/ucs-e.log
@@ -0,0 +1,28 @@
+ restricted \write18 enabled.
+ %&-line parsing enabled.
+**p-ucs
+(./p-ucs.tex
+<EUC>
+\ucs.
+\cntA=\count26
+162: 41457
+163: 41458
+165: 41455
+167: 41464
+168: 41391
+172: 41676
+176: 41451
+177: 41438
+180: 41389
+182: 41721
+215: 41439
+247: 41440
+\toucs.
+-1, -1, -1, -1, -1
+-1, 34821, 34722
+12354, 29081, -1
+41393, 41405, 41409, 41410, 41412, 41437, 41455, 41457, 41458, 41676
+41393, 41405, 41409, 41410, 41412, 41437, 41455, 41457, 41458, 41676
+41393, 41405, 41409, 41410, 41412, 41437, 41455, 41457, 41458, 41676
+ )
+No pages of output.
diff --git a/Build/source/texk/web2c/ptexdir/tests/ucs-s.log b/Build/source/texk/web2c/ptexdir/tests/ucs-s.log
new file mode 100644
index 00000000000..576b027e823
--- /dev/null
+++ b/Build/source/texk/web2c/ptexdir/tests/ucs-s.log
@@ -0,0 +1,28 @@
+ restricted \write18 enabled.
+ %&-line parsing enabled.
+**p-ucs
+(./p-ucs.tex
+<SJIS>
+\ucs.
+\cntA=\count26
+162: 33169
+163: 33170
+165: 33167
+167: 33176
+168: 33102
+172: 33226
+176: 33163
+177: 33149
+180: 33100
+182: 33271
+215: 33150
+247: 33152
+\toucs.
+-1, -1, -1, -1, -1
+12450, 29081, -1
+-1, -1, -1
+33104, 33116, 33120, 33121, 33123, 33148, 33167, 33169, 33170, 33226
+33104, 33116, 33120, 33121, 33123, 33148, 33167, 33169, 33170, 33226
+33104, 33116, 33120, 33121, 33123, 33148, 33167, 33169, 33170, 33226
+ )
+No pages of output.
diff --git a/Build/source/texk/web2c/ptexdir/tests/ucs.tex b/Build/source/texk/web2c/ptexdir/tests/ucs.tex
index de8a32fc1df..520a7485d57 100644
--- a/Build/source/texk/web2c/ptexdir/tests/ucs.tex
+++ b/Build/source/texk/web2c/ptexdir/tests/ucs.tex
@@ -9,7 +9,8 @@
\typeout{\string\ucs.}
\newcount\cntA
\loop\ifnum\cntA<256
- \ifnum0>\ucs\cntA\else\typeout{\the\cntA: \ucs\cntA (\Uchar\ucs\cntA)}\fi
+ \ifnum0>\ucs\cntA\else\typeout{\the\cntA: \ucs\cntA %
+ \ifx\Uchar\undefined\else(\Uchar\ucs\cntA)\fi}\fi
\advance\cntA1
\repeat
@@ -23,6 +24,12 @@
\typeout{\toucs"0, \toucs"80, \toucs"D7, \toucs"A7, \toucs"10000}
\typeout{\toucs"8341, \toucs"EAA4, \toucs"EAA5}
\typeout{\toucs"A4A2, \toucs"F4A6, \toucs"F4A7}
+\typeout{\jis"2131, \jis"213D, \jis"2141, \jis"2142, \jis"2144,
+ \jis"215D, \jis"216F, \jis"2171, \jis"2172, \jis"224C}
+\typeout{\ucs"203E, \ucs"2014, \ucs"301C, \ucs"2016, \ucs"2026,
+ \ucs"2212, \ucs"00A5, \ucs"00A2, \ucs"00A3, \ucs"00AC}
+\typeout{\ucs"FFE3, \ucs"2015, \ucs"FF5E, \ucs"2225, \ucs"22EF,
+ \ucs"FF0D, \ucs"FFE5, \ucs"FFE0, \ucs"FFE1, \ucs"FFE2}
\bye
diff --git a/Build/source/texk/web2c/showstream.ch b/Build/source/texk/web2c/showstream.ch
index d0347a795c4..17a6a70e401 100644
--- a/Build/source/texk/web2c/showstream.ch
+++ b/Build/source/texk/web2c/showstream.ch
@@ -72,7 +72,7 @@ show_lists_code: begin begin_diagnostic; show_activities;
end;
@y
show_lists_code:
- begin @<Adjust |selector| based on |show_stream|@>
+ begin @<Adjust |selector| based on |show_stream|@>@;
begin_diagnostic; show_activities;
end;
@z
@@ -132,13 +132,9 @@ While the other cases have separate blocks
@ @<Show the current meaning of a token...@>=
begin get_token;
@y
-@ @<Adjust |selector| based on |show_stream|@>=
-if (show_stream>=0) and (show_stream<no_print) and write_open[show_stream] then
- selector:=show_stream;
-
@ @<Show the current meaning of a token...@>=
begin get_token;
- @<Adjust |selector| based on |show_stream|@>
+ @<Adjust |selector| based on |show_stream|@>@;
@z
@x
@@ -147,7 +143,7 @@ begin scan_register_num; fetch_box(p); begin_diagnostic;
@y
@ @<Show the current contents of a box@>=
begin scan_register_num; fetch_box(p);
-@<Adjust |selector| based on |show_stream|@>
+@<Adjust |selector| based on |show_stream|@>@;
begin_diagnostic;
@z
@@ -157,19 +153,29 @@ begin p:=the_toks;
@y
@ @<Show the current value of some parameter...@>=
begin p:=the_toks;
-@<Adjust |selector| based on |show_stream|@>
+@<Adjust |selector| based on |show_stream|@>@;
@z
@x
show_groups: begin begin_diagnostic; show_save_groups;
@y
-show_groups: begin @<Adjust |selector| based on |show_stream|@>
+show_groups: begin @<Adjust |selector| based on |show_stream|@>@;
begin_diagnostic; show_save_groups;
@z
@x
show_ifs: begin begin_diagnostic; print_nl(""); print_ln;
@y
-show_ifs: begin @<Adjust |selector| based on |show_stream|@>
+show_ifs: begin @<Adjust |selector| based on |show_stream|@>@;
begin_diagnostic; print_nl(""); print_ln;
@z
+
+@x
+@* \[55] Index.
+@y
+@ @<Adjust |selector| based on |show_stream|@>=
+if (show_stream>=0) and (show_stream<no_print) and write_open[show_stream] then
+ selector:=show_stream;
+
+@* \[55] Index.
+@z
diff --git a/Build/source/texk/web2c/tangle.ch b/Build/source/texk/web2c/tangle.ch
index 5bb61fe776e..acf8986301e 100644
--- a/Build/source/texk/web2c/tangle.ch
+++ b/Build/source/texk/web2c/tangle.ch
@@ -73,7 +73,7 @@ procedure initialize;
var @<Local variables for initialization@>@/
begin @<Set initial values@>@/
@y
-@<Define |parse_arguments|@>
+@<Define \(|parse_arguments|@>
procedure initialize;
var @<Local variables for initialization@>@/
begin
@@ -302,6 +302,12 @@ the replacement text.
chopped_id[s]:=buffer[i]+@'40
@z
+@x [6.61] l.1086
+else @<Define and output a new string of the pool@>;
+@y
+else @<Define \(and output a new string of the pool@>;
+@z
+
@x [6.63] l.1102 - Case smashing options/strict checking.
if c<>"_" then
begin if c>="a" then c:=c-@'40; {merge lowercase with uppercase}
@@ -321,7 +327,7 @@ if l-double_chars=2 then {this string is for a single character}
else begin equiv[p]:=string_ptr+@'100000;
l:=l-double_chars-1;
@y
-@<Define and output a new string...@>=
+@<Define \(and output a new string...@>=
begin ilk[p]:=numeric; {strings are like numeric macros}
if l-double_chars=2 then {this string is for a single character}
equiv[p]:=buffer[id_first+1]+@'10000000000
@@ -681,7 +687,7 @@ Parse a Unix-style command line.
@d argument_is (#) == (strcmp (long_options[option_index].name, #) = 0)
-@<Define |parse_arguments|@> =
+@<Define \(|parse_arguments|@> =
procedure parse_arguments;
const n_options = 9; {Pascal won't count array lengths for us.}
var @!long_options: array[0..n_options] of getopt_struct;
diff --git a/Build/source/texk/web2c/tests/dump-basic.tex b/Build/source/texk/web2c/tests/dump-basic.tex
new file mode 100644
index 00000000000..209f3630b56
--- /dev/null
+++ b/Build/source/texk/web2c/tests/dump-basic.tex
@@ -0,0 +1,6 @@
+\ifx\fmtname\undefined
+ \input basic
+ \def\fmtname{basic}
+ \expandafter\dump
+\fi
+\end
diff --git a/Build/source/texk/web2c/tests/ec-lmr10.tfm b/Build/source/texk/web2c/tests/ec-lmr10.tfm
new file mode 100644
index 00000000000..719e4f082e8
--- /dev/null
+++ b/Build/source/texk/web2c/tests/ec-lmr10.tfm
Binary files differ
diff --git a/Build/source/texk/web2c/texprofdir/ChangeLog b/Build/source/texk/web2c/texprofdir/ChangeLog
new file mode 100644
index 00000000000..a4079b33063
--- /dev/null
+++ b/Build/source/texk/web2c/texprofdir/ChangeLog
@@ -0,0 +1,16 @@
+2024-09-07 Karl Berry <karl@freefriends.org>
+
+ * am/texprof.am (texprofile_c): depend on texprofile-tangle,
+ not texprof-tangle.
+ (nodist_texprof_SOURCES): not needed, since these programs
+ have one source file; and texprof does not depend on texprofile.
+
+2024-09-02 Martin Ruckert <martin.ruckert@hm.edu>
+
+ Adding the texprofile manual page
+
+2024-09-02 Martin Ruckert <martin.ruckert@hm.edu>
+
+ Initial commit
+
+(This file public domain.)
diff --git a/Build/source/texk/web2c/texprofdir/README.TXT b/Build/source/texk/web2c/texprofdir/README.TXT
new file mode 100644
index 00000000000..d099b850f13
--- /dev/null
+++ b/Build/source/texk/web2c/texprofdir/README.TXT
@@ -0,0 +1,5 @@
+TeXprof is an extended version of TeX that collects run time data for the
+TeXprofile program.
+
+TeXprof and TeXprofile together implement profiling for TeX input files.
+
diff --git a/Build/source/texk/web2c/texprofdir/am/texprof.am b/Build/source/texk/web2c/texprofdir/am/texprof.am
new file mode 100644
index 00000000000..6b5ae25f3e5
--- /dev/null
+++ b/Build/source/texk/web2c/texprofdir/am/texprof.am
@@ -0,0 +1,76 @@
+## $Id$
+## texk/web2c/texprofdir/am/texprof.am: Makefile fragment for TeXprof
+##
+## Copyright 2024 Martin Ruckert <ruckert@cs.hm.edu>
+## You may freely use, modify and/or distribute this file.
+
+if TEXPROF
+bin_PROGRAMS += texprof texprofile
+
+# convenience target to build all the programs here.
+texprofall: texprof texprofile
+
+# See hitex.am for comments about handling man.
+man1_MANS = texprofdir/man/texprof.man texprofdir/man/texprofile.man
+endif TEXPROF
+
+texprof_CPPFLAGS = $(AM_CPPFLAGS)
+texprof_CPPFLAGS += -I$(srcdir)/libmd5
+texprof_CPPFLAGS += -DINIT -DSTAT -DWEB2CVERSION="\"${WEB2CVERSION}\""
+# -DDEBUG drops into an interactive debugging session.
+texprof_LDADD = $(KPATHSEA_LIBS) libmd5.a
+texprof_DEPENDENCIES = $(KPATHSEA_DEPEND) libmd5.a
+#
+texprofile_CPPFLAGS = $(AM_CPPFLAGS)
+texprofile_LDADD =
+texprofile_DEPENDENCIES =
+#
+# TeXprof CWEB sources
+texprof_web = texprofdir/texprof.w
+texprofile_web = texprofdir/texprofile.w
+
+# Creating several files: need stamp file and two rules with identical recipes
+texprof_ctangle_sh = CWEBINPUTS=$(srcdir)/texprofdir AM_V_P=$(AM_V_P) $(SHELL) ./tangle-sh $@ $(CTANGLE)
+
+# For each cweb (.w) source file, list the files generated.
+texprof_c = texprof.c
+texprofile_c = texprofile.c
+
+$(texprof_c): texprof-tangle
+$(texprofile_c): texprofile-tangle
+
+#
+texprof-tangle: ctangle$(EXEEXT) texprofdir/texprof.w tangle-sh
+ $(texprof_ctangle_sh) texprof
+
+texprofile-tangle: ctangle$(EXEEXT) texprofdir/texprofile.w tangle-sh
+ $(texprof_ctangle_sh) texprofile
+
+EXTRA_DIST += texprofdir/ChangeLog $(texprof_web)
+
+DISTCLEANFILES += $(nodist_texprof_SOURCES)
+DISTCLEANFILES += texprofile-tangle texprof-tangle
+
+## TeXprof tests
+#
+#texprof_tests = texprofdir/tests/hello.test \
+# texprofdir/tests/rule.test
+#
+#texprofdir/tests/hello.log: texprof$(EXEEXT) histretch$(EXEEXT)
+#texprofdir/tests/rule.log: texprof$(EXEEXT) histretch$(EXEEXT) hishrink$(EXEEXT)
+#
+#EXTRA_DIST += $(texprof_tests)
+#
+#if TEXPROF
+#TESTS += $(texprof_tests)
+#endif TEXPROF
+#
+## hello.test
+#EXTRA_DIST += texprofdir/tests/hello.tex
+#DISTCLEANFILES += hello.log
+
+## rule.test
+#EXTRA_DIST += texprofdir/tests/rule.tex
+#DISTCLEANFILES += rule.log
+
+# (end of texprof.am)
diff --git a/Build/source/texk/web2c/texprofdir/doc/texprof.hnt b/Build/source/texk/web2c/texprofdir/doc/texprof.hnt
new file mode 100644
index 00000000000..fb3d7c162f1
--- /dev/null
+++ b/Build/source/texk/web2c/texprofdir/doc/texprof.hnt
Binary files differ
diff --git a/Build/source/texk/web2c/texprofdir/doc/texprof.pdf b/Build/source/texk/web2c/texprofdir/doc/texprof.pdf
new file mode 100644
index 00000000000..d07addfa31e
--- /dev/null
+++ b/Build/source/texk/web2c/texprofdir/doc/texprof.pdf
Binary files differ
diff --git a/Build/source/texk/web2c/texprofdir/doc/texprofile.hnt b/Build/source/texk/web2c/texprofdir/doc/texprofile.hnt
new file mode 100644
index 00000000000..507675ef317
--- /dev/null
+++ b/Build/source/texk/web2c/texprofdir/doc/texprofile.hnt
Binary files differ
diff --git a/Build/source/texk/web2c/texprofdir/doc/texprofile.pdf b/Build/source/texk/web2c/texprofdir/doc/texprofile.pdf
new file mode 100644
index 00000000000..eca97af5571
--- /dev/null
+++ b/Build/source/texk/web2c/texprofdir/doc/texprofile.pdf
Binary files differ
diff --git a/Build/source/texk/web2c/texprofdir/doc/texprofileman.hnt b/Build/source/texk/web2c/texprofdir/doc/texprofileman.hnt
new file mode 100644
index 00000000000..8cd8af1aa55
--- /dev/null
+++ b/Build/source/texk/web2c/texprofdir/doc/texprofileman.hnt
Binary files differ
diff --git a/Build/source/texk/web2c/texprofdir/doc/texprofileman.pdf b/Build/source/texk/web2c/texprofdir/doc/texprofileman.pdf
new file mode 100644
index 00000000000..57e23386aa7
--- /dev/null
+++ b/Build/source/texk/web2c/texprofdir/doc/texprofileman.pdf
Binary files differ
diff --git a/Build/source/texk/web2c/texprofdir/man/texprof.man b/Build/source/texk/web2c/texprofdir/man/texprof.man
new file mode 100644
index 00000000000..b83047e9590
--- /dev/null
+++ b/Build/source/texk/web2c/texprofdir/man/texprof.man
@@ -0,0 +1,345 @@
+.TH TEXPROF 1 "2 September 2024" "Version 1.0"
+.\"=====================================================================
+.if n .ds MF Metafont
+.if t .ds MF Metafont
+.if t .ds TX \fRT\\h'-0.1667m'\\v'0.20v'E\\v'-0.20v'\\h'-0.125m'X\fP
+.if n .ds TX TeX
+.ie t .ds OX \fIT\v'+0.25m'E\v'-0.25m'X\fP
+.el .ds OX TeX
+.\" BX definition must follow TX so BX can use TX
+.if t .ds BX \fRB\s-2IB\s0\fP\*(TX
+.if n .ds BX BibTeX
+.\" LX definition must follow TX so LX can use TX
+.if t .ds LX \fRL\\h'-0.36m'\\v'-0.15v'\s-2A\s0\\h'-0.15m'\\v'0.15v'\fP\*(TX
+.if n .ds LX LaTeX
+.if t .ds AX \fRA\\h'-0.1667m'\\v'0.20v'M\\v'-0.20v'\\h'-0.125m'S\fP\*(TX
+.if n .ds AX AmSTeX
+.if t .ds AY \fRA\\h'-0.1667m'\\v'0.20v'M\\v'-0.20v'\\h'-0.125m'S\fP\*(LX
+.if n .ds AY AmSLaTeX
+.if n .ds WB Web
+.if t .ds WB W\s-2EB\s0
+.\"=====================================================================
+.SH NAME
+texprof \- A profiler for TeX source files
+.SH SYNOPSIS
+.B texprof
+.RI [ options ]
+.RI [ \fB&\fPformat ]
+.RI [ file | \fB\e\fPcommands ]
+.\"=====================================================================
+.SH DESCRIPTION
+Run the \*(TX profiler on
+.IR file ,
+usually creating
+.IR file.dvi
+and
+.IR file.tprof .
+If the
+.IR file
+argument has no extension, ".tex" will be appended to it.
+Instead of a file name, a list of \*(TX commands can be given, the first
+of which must start with a backslash.
+With a
+.BI & format
+argument, the \*(TX profiler uses a different set of precompiled commands,
+contained in
+.IR format\fB.fmt\fP ;
+it is usually better to use the
+.B -fmt
+.I format
+option instead.
+.PP
+The \*(TX profiler is a version of \*(TX that measures the time
+\*(TX needs to execute individual input lines or macros.
+It writes the collected data to a file with extension
+.IR .tprof .
+A separate program,
+.B texprofile
+(see
+.BR texprofile (1)),
+is used to display the content of a
+.IR .tprof
+file in a readable form.
+
+.PP
+The typical use of the \*(TX profiler is with pre generated formats.
+The
+.B texprof
+command uses the equivalent of the plain \*(TX format, and the
+.B latexprof
+command uses the equivalent of the \*(LX format. To investigate
+the timing of macros targeted to
+.B pdftex
+or
+.B pdflatex,
+you can use
+.B pdftexprof
+or
+.B pdflatexprof.
+
+.PP
+The \*(TX profiler's handling of its command-line arguments is similar
+to that of the other \*(TX programs in the
+.I web2c
+implementation.
+
+.\"=====================================================================
+.SH OPTIONS
+This version of the \*(TX profiler understands the following command line
+options.
+.TP
+.BI -cnf-line \ string
+Parse
+.I string
+as a
+.I texmf.cnf
+configuration line. See the Kpathsea manual.
+.TP
+.B -etex
+Enable the e-\*(TX extensions. This option is only effective in
+combination with
+.BR -ini .
+See
+.BR etex (1).
+.TP
+.B -file-line-error
+Print error messages in the form
+.I file:line:error
+which is similar to the way many compilers format them.
+.TP
+.B -no-file-line-error
+Disable printing error messages in the
+.I file:line:error
+style.
+.TP
+.BI -fmt \ format
+Use
+.I format
+as the name of the format to be used, instead of the name by which
+the \*(TX profiler was called or a
+.I %&
+line.
+.TP
+.B -help
+Print help message and exit.
+.TP
+.B -ini
+Start in
+.I INI
+mode, which is used to dump formats. The
+.I INI
+mode can be used for typesetting, but no format is preloaded, and
+basic initializations like setting catcodes may be required.
+.TP
+.BI -interaction \ mode
+Sets the interaction mode. The mode can be either
+.IR batchmode ,
+.IR nonstopmode ,
+.IR scrollmode ,
+and
+.IR errorstopmode .
+The meaning of these modes is the same as that of the corresponding
+\ecommands.
+.TP
+.BI -jobname \ name
+Use
+.I name
+for the job name, instead of deriving it from the name of the input file.
+.TP
+.BI -kpathsea-debug \ bitmask
+Sets path searching debugging flags according to the bitmask. See the
+.I Kpathsea
+manual for details.
+.TP
+.B -ltx
+Enable the \*(LX extensions. This option is only effective in
+combination with
+.BR -ini .
+See
+.BR latex (1).
+.TP
+.BI -mktex \ fmt
+Enable
+.RI mktex fmt ,
+where
+.I fmt
+must be either
+.IR tex ,
+.IR tfm ,
+.IR fmt ,
+or
+.IR pk .
+.TP
+.BI -no-mktex \ fmt
+Disable
+.RI mktex fmt ,
+where
+.I fmt
+must be either
+.IR tex ,
+.IR tfm ,
+.IR fmt ,
+or
+.IR pk .
+.TP
+.BI -output-directory \ directory
+Write output files in
+.I directory
+instead of the current directory. Look up input files in
+.I directory
+first, then along the normal search path.
+.TP
+.B -parse-first-line
+If the first line of the main input file begins with
+.I %&
+parse it to look for a dump name.
+.TP
+.B -no-parse-first-line
+Disable parsing of the first line of the main input file.
+.TP
+.B -pdf
+Enable the simulation of common
+.B pdftex
+primitives. This option requires the
+.BR -ini
+option and implies the
+.BR -ltx
+option.
+See
+.BR pdftex (1).
+.TP
+.B -prof
+Enable profiling as soon as \*(TX enters the main loop. If tis option
+it not used, profiling needs to switched on using the \eprofileon
+primitive. Profiling can be switched of with the \eprofileoff
+primitive.
+.TP
+.BI -progname \ name
+Pretend to be program
+.IR name .
+This affects both the format used and the search paths.
+.TP
+.B -version
+Print version information and exit.
+.\"=====================================================================
+.SH ENVIRONMENT
+See the Kpathsea library documentation (e.g., the `Path specifications'
+node) for precise details of how the environment variables are used.
+The
+.B kpsewhich
+utility can be used to query the values of the variables.
+.PP
+.TP
+.B TEXMFOUTPUT
+Normally, \*(TX puts its output files in the current directory. If
+any output file cannot be opened there, it tries to open it in the
+directory specified in the environment variable TEXMFOUTPUT.
+There is no default value for that variable. For example, if you say
+.I texprof paper
+and the current directory is not writable and TEXMFOUTPUT has
+the value
+.IR /tmp ,
+\*(TX attempts to create
+.I /tmp/paper.log ,
+.I /tmp/paper.dvi ,
+and
+.I /tmp/paper.tprof .
+TEXMFOUTPUT is also checked for input
+files, as \*(TX often generates files that need to be subsequently
+read; for input, no suffixes (such as ``.tex'') are added by default,
+the input name is simply checked as given.
+.TP
+.B TEXINPUTS
+Search path for
+.I \einput
+and
+.I \eopenin
+files.
+This normally starts with ``.'', so
+that user files are found before system files. An empty path
+component will be replaced with the paths defined in the
+.I texmf.cnf
+file. For example, set TEXINPUTS to ".:/home/user/tex:" to prepend the
+current directory and ``/home/user/tex'' to the standard search path.
+.TP
+.B TEXFORMATS
+Search path for format files.
+.TP
+.B TFMFONTS
+Search path for font metric
+.RI ( .tfm )
+files.
+.TP
+.B SOURCE_DATE_EPOCH
+If set, its value, taken to be in epoch-seconds, will be used for the
+creation date and as the reference moment for the time related
+primitives of \*(LX. This is useful for making reproducible builds.
+.TP
+.B FORCE_SOURCE_DATE
+If set to the value "1", the time-related \*(TX primitives
+.RI ( \eyear ,
+.IR \emonth ,
+.IR \eday ,
+.IR \etime )
+are also initialized from the value of SOURCE_DATE_EPOCH. This is not
+recommended if there is any viable alternative.
+.PP
+Many, many more environment variables may be consulted related to path
+searching. See the Kpathsea manual.
+.\"=====================================================================
+.SH FILES
+The location of the files mentioned below varies from system to
+system. Use the
+.B kpsewhich
+utility to find their locations.
+.TP
+.I *.tfm
+Metric files for \*(TX's fonts.
+.TP
+.I *.fmt
+Predigested \*(TX format files.
+.TP
+.I *.pk *.pfb
+Font files used by \*(TX.
+.\"=====================================================================
+.SH NOTES
+This manual page is not meant to be exhaustive. The complete
+documentation for the \*(TX profiler can be found in
+.IR texprof.pdf .
+Further information can be found in the manual of the
+.IR "Kpathsea library" .
+.\"=====================================================================
+.SH BUGS
+This version of the \*(TX profiler does not implement all of the
+primitives that pdftex provides, and further, it will not produce
+the same side effects.
+.\"=====================================================================
+.SH AVAILABILITY
+The \*(TX profiler should compile on a large variety of machine architectures
+and operating systems. The function to obtain timing information id POSIX
+specific.
+.PP
+The sources of the \*(TX profiler are hosted at
+.BR https://github.com/ruckertm/HINT
+.\"=====================================================================
+.SH "SEE ALSO"
+.BR texprofiler (1),
+.BR tex (1),
+.BR latex (1),
+.BR pdftex (1),
+.BR pdflatex (1), and
+.BR kpsewhich (1).
+.\"=====================================================================
+.SH AUTHORS
+The primary author of the \*(TX profiler is Martin Ruckert,
+with e\*(TX extensions by Peter Breitenlohner,
+\*(LX extensions by Thierry Laronde,
+and
+the kpathsearch library by Karl Berry.
+.PP
+\*(TX was designed by Donald E. Knuth,
+who implemented it using his \*(WB system for Pascal programs.
+.PP
+Many, many more contributed to the typesetting system now known as \*(TX;
+far too many to name all of them here.
+.\" vim: syntax=nroff
diff --git a/Build/source/texk/web2c/texprofdir/man/texprofile.man b/Build/source/texk/web2c/texprofdir/man/texprofile.man
new file mode 100644
index 00000000000..6fe0ce616e3
--- /dev/null
+++ b/Build/source/texk/web2c/texprofdir/man/texprofile.man
@@ -0,0 +1,596 @@
+'\" t
+.\" Manual page created with latex2man on Mon Sep 9 16:27:26 2024
+.\" NOTE: This file is generated, DO NOT EDIT.
+.de Vb
+.ft CW
+.nf
+..
+.de Ve
+.ft R
+
+.fi
+..
+.TH "TEXPROFILE" "1" "6\-9\-2024" "Displaying TeX profiles " "Displaying TeX profiles "
+.SH NAME
+
+.PP
+texprofile
+\- Displaying the profile data collected by texprof\&.
+.PP
+.SH SYNOPSIS
+
+.PP
+texprofile
+[Options] \fIinputfile\fP
+.PP
+.SH DESCRIPTION
+
+texprofile
+reads the binary \fIinputfile\fP
+as produced by
+texprof\&.
+The extension \&.tprof
+of the input file can be
+omitted. It analyses the data and presents the results in a variety of
+tables, either optimized for human readability or as CSV (comma
+separated values) files for further processing.
+.PP
+The output is written to the standard output stream where it can be redirected
+to a file or viewed immediately.
+.PP
+.SH OPTIONS
+
+There are four types of options: general options, table options,
+selection options, and formatting options.
+.PP
+The general options:
+.TP
+\fB\-?\fP \fB\-h\fP \fB\-\-help\fP
+ Display a short help text and exit the program.
+.TP
+\fB\-\-version\fP
+ Display the version information and exit.
+.PP
+The table options determine the tables that will be displayed.
+These options all use upper case letters.
+If no table option is given, only some global information is shown.
+.PP
+.TP
+\fB\-T\fP
+ Show the table of the top 10 input lines.
+.TP
+\fB\-G\fP
+ Show the table of the macro call graph.
+.TP
+\fB\-C\fP
+ Show the table of times per TeX command.
+.TP
+\fB\-L\fP
+ Show the table of times per input line.
+.TP
+\fB\-R\fP
+ Show the table of raw time stamps.
+.TP
+\fB\-S\fP
+ Show the table of macro stack changes.
+.TP
+\fB\-F\fP
+ Show the table of all input files.
+.TP
+\fB\-M\fP
+ Show the table of all macros called.
+.TP
+\fB\-A\fP
+ Show important tables (equal to \-TGFC) tables.
+.TP
+\fB\-N\fP
+ Do not show the table of global information.
+.PP
+The selection options allow to reduce the amount of data that is shown
+in the table by omitting table entries that contribute little to the
+overall runtime.
+.PP
+.TP
+\fB\-p\fP\fIn\fP
+ Do not show information for table entries
+with cumulative time below \fIn\fP
+percent. The default is 1.0 percent.
+.TP
+\fB\-t\fP\fIn\fP
+ Limit the number of input lines shown in the
+table of the ``Top Ten\&'' to \fIn\fP\&.
+This option is only useful
+with the \fB\-T\fP
+option.
+.PP
+The formatting option determine the formatting of the information in the tables
+as well as the selection of information that is shown in the tables.
+.PP
+.TP
+\fB\-i\fP
+ Add the macros file and line numbers after the macro name.
+This option is useful to distinguish two macros that share
+the same name.
+.TP
+\fB\-m\fP
+ Optimize output for machine readability. When optimized for human readability,
+times are rounded and given in a short form adding a unit identifier:
+s for seconds, ms for milliseconds, us for micro seconds, and ns for
+nanoseconds. This format is inconvenient if you want to import the data
+for example into a spread\-sheet for further processing.
+With the \fB\-m\fP
+option times are simply given in nano seconds without units.
+Similar the combined time for the total time used for a macro and the
+time used as a sub\-macro from the call graph is given in a single column
+with a slash separating both numbers is nice for human readers.
+With the \fB\-m\fP
+option both numbers will simply be given in separate columns.
+.PP
+.TP
+\fB\-n\fP
+ Show the time stamp numbers. This option is useful together with
+the \fB\-R\fP
+option, if you import the table into a database or spread\-sheet.
+If you sort the data by various properties into different
+categories, the time stamp number can be used to keeping the entries
+within a category sorted by time.
+.PP
+.TP
+\fB\-s\fP
+ Show the changes of the macro stack.
+This option is useful with the \fB\-R\fP
+option if you want to see the changes
+in the macro stack (see \fB\-S\fP)
+together with the timing information in
+a single table.
+.PP
+.SH DATA FORMATS
+
+.SS FILE NUMBERS
+While texprof
+is running, every TeX input file is given a
+unique file number. The file name alone is often not unique because
+two files in different directories might have the same file
+name. Displaying the full file name with the complete path is however often
+not very
+convenient. So if you observe the same file name together with
+different file numbers, you can use the option \fB\-F\fP
+to get a table of all
+file numbers and their full file names.
+.PP
+But even within the same
+directory, TeX can read different files with the same name during one
+run: TeX can open a file for writing, write content to the file, close
+it, open it for reading, read it and close it again. Then TeX might
+repeat this process a second time, or multiple times, reusing the same
+file name over and over again. texprof
+will assign a new file
+number to this file each time it is opened for reading. So you can
+tell from the sequence of file numbers which one was the first, the
+second, or the third.
+.PP
+Last not least, there are some special file
+numbers:
+.TP
+0 \- unknown
+If the file is unknown, which should rarely happen,
+the file number 0 is used.
+.TP
+1\- system
+texprof
+will map
+time intervals that are used to execute certain system routines
+to the ``system\&'' pseudo file
+using the line numbers to identify the specific routine
+like producing the output DVI file (shipout), breaking
+a paragraph into lines (linebrk), or breaking the
+document into pages (buildpg). These times do not depend on the
+use of macros but simply on the size of the document.
+.TP
+2 \- terminal
+TeX commands entered on the command line or interactively are mapped
+to file number 2.
+.PP
+.SS TIME
+If the option \fB\-m\fP
+is given times are given as nanoseconds.
+Otherwise, times are rounded to at most 4 digits precission and
+displayed with a unit: seconds (s), milliseconds (ms), microseconds
+(um), or nanoseconds (ns).
+.PP
+.SS MACRO NAMES
+Macro names are shown with the leading backslash. Since macro names
+are often not unique, the command line option \fB\-i\fP
+can be used to
+show after the macro name in square brackets the file number and the
+line number where the macro is defined. Unless two macros with the
+same name are defined in the same file and line this is sufficient to
+uniquely identify a macro. A macro defined with ``let\&'' results in a
+complet copy of the original macro. Therefore it will not reference
+the file and line of the ``let\&'' command but the same file and line as
+the original macro.
+.PP
+.SH EXAMPLES
+
+Let\&'s assume that you issue the command texprof
+\fB\-prof\fP
+\fIhello.tex\fP\&.
+This will run texprof
+on the input file
+\fIhello.tex\fP
+with option \fB\-prof\fP\&.
+The program texprof
+will load the plain TeX format and then process hello.tex
+to
+produce hello.log
+and hello.dvi\&.
+It will execute
+exactly the same steps that TeX would execute if you had issued the
+command tex
+\fIhello.tex\fP\&.
+.PP
+In addition to hello.log
+and hello.dvi,
+texprof
+will also produce the file hello.tprof
+containing time
+measurements made while texprof
+was running. The option
+\fB\-prof\fP
+will switch on the gathering of timing data as soon as
+texprof
+enters its main control procedure; The file
+hello.tprof
+will contain a time measurement, called a time
+stamp, for every command that TeX executed while processing the input.
+A binary format is used to store all that data in a simple and compact
+form. Still the file hello.tprof
+might become very big.
+.PP
+The program texprofile
+is used to extract and analyse the data
+contained in hello.tprof
+and produce useful output. Here are
+some examples:
+.PP
+.TP
+texprofile \fIhello\fP
+ Without further options texprofile
+will write some general
+information to the standard output, like the total time measured,
+the number of samples, the average time per sample, etc.
+The general information is always given unless explicitely disabled
+with the \fB\-N\fP
+option or with the \fB\-m\fP
+option.
+.PP
+.TP
+texprofile \fB\-T\fP \fIhello\fP
+ With the option \fB\-T\fP,
+texprofile
+will map each time stamp
+to a specific line of input, add up the time intervalls for each input
+line separately, and output a table showing the ten lines that have
+the highest cummulative time.
+The table has the following seven columns:
+.RS
+.TP
+1. file
+The first column contains the input file number as explained above.
+The input file name if shown in column 7.
+.TP
+2. line
+The line number.
+.TP
+3. percent
+The time spent in this line and file as a percentage of the total
+time measured as given as part of the general information.
+.TP
+4. absolute
+The absolute time spent in this line and file.
+.TP
+5. count
+The number of times the execution path entered this line.
+Note, that a
+macro call usually redirects the execution path to another line
+from where the execution will return after the macro call has completed.
+Reentering the line after such an excursion to an other line will
+cause this counter to be incremented. In summary, this counter might
+reflect the number of partial executions of a line not the number
+of full executions of the entire line.
+.TP
+6. average
+The average time spent in the line is simply computed by dividing the
+value in column 4 by the value in column 5.
+.TP
+7. file
+The input file name. The corresponding file number is shown in column 1.
+.RE
+.RS
+.PP
+.RE
+.TP
+texprofile \fB\-G\fP \fIhello\fP
+ With the option \fB\-G\fP,
+texprofile
+will map each time stamp
+either to file input or to a macro body.
+The table shown is divided into several sections, the first section
+is devoted to file input, each of the following sections is devoted
+to a specific macro.
+.br
+Each section starts with a header. The header of the
+first section is ``File\&'' the header of a later section is the macro name.
+.br
+The first line after the header gives the total time spent in the section
+in two different formats:
+.RS
+.TP
+1. time
+Column 1 gives it as an absolut time.
+.TP
+3. percentage
+Column 3 gives it as a percentage of the total time measured.
+.RE
+.RS
+.PP
+For the first section with the header ``File\&'', the absolute time
+will be equal to the total time measured because texprof
+did spent all the time processing the file \fIhello\fP\&.
+And consequently the value in column 3 will be 100%\&.
+.br
+To accomplish a task, a macro usually calls other
+macros that we call child macros in the following.
+The following lines in the table will give a breakdown
+of the time shown in the first line.
+The breakdown starts with a line showing the time spent in the section
+excluding the time spent in child macros. It shows in column 4 the number
+of times the macro was called.
+.RS
+.RE
+.TP
+1. time
+Column 1 gives the absolut time spent in the section excluding the time
+spent in child macros
+.TP
+3. percentage
+Column 3 gives the time from column 1 as a percentage of the total time
+spent in this section as given in the previous line.
+.TP
+4. count/total
+Column 4 gives the number of times the section was called.
+.RE
+.RS
+.PP
+The lines that follow in the table show the time spent in one of
+the child macros. Column 2, 4, and 5 need some explanation.
+.RS
+.RE
+.TP
+2. loop
+The only case where column 2 is not empty is the case of a
+recursive macro. A recursive macro is a macro that along the
+chain of macro calls eventually calls itself creating a
+recursive loop. At this point, a macro becomesits own descendant
+and at the same time its own ancestor.
+.br
+Therefore texprofile
+maintains for each child macro two
+accumulators for the elapsed time: For the time shown in
+column 2 labeled ``loop\&'', texprofile
+adds up the time
+differences observed at the return of a child macro. For the
+time shown in the column labeled ``time\&'' and ``percent\&'', it
+subtracts from the time differences observed at the return of a
+child macro all those time differences that were already added
+to one of the other lines in the time breakdown: the macro itself
+or one of the other child macros.
+So the times shown in column 1 of line 2 and the following lines
+will add up to the time shown in column 1 of the line 1;
+and the percentages shown in column 3 of line 2 and the following lines
+will add up to 100%\&.
+The time shown in column 3 will show show the total time needed
+to accomplish the sub task assigned to the respective child macro.
+.TP
+4. count/total
+Column 4 shows two counts n/m for the macro named in column 5.
+m is the total number of calls to the macro and n is the number of
+calls as a child macro in the current section. The number n will
+always be less or equal to m\&.
+.RS
+.PP
+.RE
+.TP
+5. child
+Column 5 shows the name of the child macro as explained above.
+.RE
+.RS
+.PP
+.RE
+.TP
+texprofile \fB\-G\fP \fB\-m\fP \fIhello\fP
+ This table will contain the same data as the previous example
+but this time the \fB\-m\fP
+option will optimize the output for
+machine readability.
+.RS
+.TP
+.B *
+There are no column headers.
+.TP
+.B *
+The times in column 1 ``time\&'' and column 2 ``loop\&''
+are given in nanoseconds without a unit identifier.
+.TP
+.B *
+The numbers n/m in column 4 are now shown in two separate
+columns 4 and 5; the macro name moves from column 5 to column 6.
+.RE
+.RS
+.PP
+.RE
+.TP
+texprofile \fB\-F\fP \fIhello\fP
+ With the option \fB\-F\fP,
+texprofile
+outputs the table
+of all of TeX\&'s input files using 5 columns:
+.RS
+.TP
+1. file
+The file number as explained above.
+.TP
+2. lines
+The difference between the highest and the lowest
+line number found in the profile data for this file. This is
+usually only a subset of all the lines of the file.
+.TP
+3. percent
+The percentage of the total time measured that is attibuted to the file.
+.TP
+4. time
+The absolute time that is attibuted to the file.
+.TP
+5. name
+The full file name of the file.
+.RE
+.RS
+.PP
+.RE
+.TP
+texprofile \fB\-C\fP \fIhello\fP
+ With the option \fB\-C\fP,
+texprofile
+outputs the table
+of all TeX commands executed while profiling
+using 6 columns:
+.RS
+.TP
+1. cmd
+The command code used internaly by TeX.
+Usually the same number is used by TeX for several
+closely related tasks.
+There is a special command code 101 which is used to
+for the time that is spend on system routines that are
+mapped to the system file as explained above.
+.TP
+2. time
+The total time used for the command.
+.TP
+3. percent
+The percentage of the total time measured used for the command.
+.TP
+4. count
+The number of times this command was executed.
+.TP
+5. average
+The average time needed to execute the command.
+This is simpy the value in column 2 divided by the value in column 4.
+.TP
+6. name
+A verbal description of the command or commands that share this
+command code.
+.RE
+.RS
+.PP
+.RE
+.TP
+texprofile \fB\-R\fP \fB\-m\fP \fB\-n\fP \fIhello\fP
+ With the options \fB\-R\fP
+\fB\-m\fP,
+texprofile
+outputs the table
+of raw time measurements as observed by texprof
+optimized for machine
+readability. The option \fB\-n\fP
+adds a column for the number
+of each time measurements.
+Because of the \fB\-m\fP
+option, the table has no column headers.
+The table has 7 columns:
+.RS
+.TP
+1. number
+The number of the time measurment.
+These numbers are strictly increasing but not necessarily consecutive.
+They can be used to keep the measurements sorted in the order in which
+they were made.
+.TP
+2. file
+The file number as explained above.
+.TP
+3. line
+The line number as explained above.
+.TP
+4. time
+The time interval in nano seconds.
+.TP
+5. command
+The command name as given in column 6 of the previous example.
+.TP
+6. level
+The nesting level of the macro call stack.
+.TP
+7. macro
+The macro name.
+.RE
+.RS
+.PP
+This table contains all the timing information gathered during the run
+of texprof\&.
+Some information about macro calls is contained in
+column 6. More information about macro calls could be added
+by using the \fB\-s\fP
+option. The information about macro returns
+can be obtained from column 6.
+The table can be imported to a spread\-sheet or a database for further
+analysis.
+.RE
+.PP
+.SH BUGS
+
+If the last command in a macro body is a macro call, we call this a tail call.
+If such a tail call reads ahead to scan the following input
+for possible arguments, the look\-ahead mechaism of TeX might push further
+macros or new input files on TeX\&'s input stack.
+These entries will remain on top of TeXprof\&'s macro nesting stack,
+even if TeX backs up all these tokens on its input stack.
+This can cause an attribution of runtime to those entries as sub entries
+of the tail call. If this explanation sounds complicated to you, it is
+because the situation is indeed complicated.
+.PP
+.SH SEE ALSO
+
+\fItexprof\fP(1)
+.PP
+.SH VERSION
+
+Version: 1.0 of 6-9-2024
+.TP
+Copyright
+(C)2024, Martin Ruckert,
+.br
+Hochschule München, Lothstrasse 64, D\-80335 München
+.PP
+.TP
+Distribution
+texprofile
+is distributed with TeX Live.
+More recent versions might be found on GitHub
+\fBhttps://github.com/ruckertm/HINT\fP\&.
+.PP
+.TP
+License
+This program can be redistributed and/or modified under the
+terms of the MIT/X11 license.
+.PP
+.SH AUTHOR
+
+Martin Ruckert
+.br
+Munich University of Applied Sciences
+.br
+Email: \fBmartin.ruckert@hm.edu\fP
+.br
+.PP
+.\" NOTE: This file is generated, DO NOT EDIT.
diff --git a/Build/source/texk/web2c/texprofdir/tex/latexprof.ini b/Build/source/texk/web2c/texprofdir/tex/latexprof.ini
new file mode 100644
index 00000000000..889e6bec4ae
--- /dev/null
+++ b/Build/source/texk/web2c/texprofdir/tex/latexprof.ini
@@ -0,0 +1 @@
+\input latex.ltx
diff --git a/Build/source/texk/web2c/texprofdir/tex/pdflatexprof.ini b/Build/source/texk/web2c/texprofdir/tex/pdflatexprof.ini
new file mode 100644
index 00000000000..0460d192e29
--- /dev/null
+++ b/Build/source/texk/web2c/texprofdir/tex/pdflatexprof.ini
@@ -0,0 +1,2 @@
+\pdfoutput=1
+\input latex.ltx
diff --git a/Build/source/texk/web2c/texprofdir/tex/pdftexprof.ini b/Build/source/texk/web2c/texprofdir/tex/pdftexprof.ini
new file mode 100644
index 00000000000..8ee45c92265
--- /dev/null
+++ b/Build/source/texk/web2c/texprofdir/tex/pdftexprof.ini
@@ -0,0 +1,3 @@
+\pdfoutput=1
+\input plain.tex
+\dump
diff --git a/Build/source/texk/web2c/texprofdir/tex/texprof.ini b/Build/source/texk/web2c/texprofdir/tex/texprof.ini
new file mode 100644
index 00000000000..74e0658f5b7
--- /dev/null
+++ b/Build/source/texk/web2c/texprofdir/tex/texprof.ini
@@ -0,0 +1,2 @@
+\input plain.tex
+\dump
diff --git a/Build/source/texk/web2c/texprofdir/texprof.w b/Build/source/texk/web2c/texprofdir/texprof.w
new file mode 100644
index 00000000000..722106017fa
--- /dev/null
+++ b/Build/source/texk/web2c/texprofdir/texprof.w
@@ -0,0 +1,33304 @@
+% TeXprof is copyright (C) 2024 by Martin Ruckert
+% Hochschule Muenchen, Lothstrasse 64, 80336 Muenchen
+% and put under the MIT/X11 license.
+%
+% Prote is copyright (C) 2021 by Thierry Laronde and put under
+% the MIT/X11 license.
+%
+% As TeX and e-TeX are reserved names for the unchanged (except for the
+% necessary implementation of system dependencies) instances of, resp.,
+% Donald E. Knuth's program and the NTS team's e-TeX implementation, the
+% name Prote is reserved for this kerTeX implementation.
+%
+% Hence, this is neither TeX (even if it is compatible at start) nor
+% e-TeX (even if is compatible when entering the first set of
+% extensions).
+
+% Version 0 was released in September 1982 after it passed a variety of tests.
+% Version 1 was released in November 1983 after thorough testing.
+% Version 1.1 fixed ``disappearing font identifiers'' et alia (July 1984).
+% Version 1.2 allowed `0' in response to an error, et alia (October 1984).
+% Version 1.3 made memory allocation more flexible and local (November 1984).
+% Version 1.4 fixed accents right after line breaks, et alia (April 1985).
+% Version 1.5 fixed \the\toks after other expansion in \edefs (August 1985).
+% Version 2.0 (almost identical to 1.5) corresponds to "Volume B" (April 1986).
+% Version 2.1 corrected anomalies in discretionary breaks (January 1987).
+% Version 2.2 corrected "(Please type...)" with null \endlinechar (April 1987).
+% Version 2.3 avoided incomplete page in premature termination (August 1987).
+% Version 2.4 fixed \noaligned rules in indented displays (August 1987).
+% Version 2.5 saved cur_order when expanding tokens (September 1987).
+% Version 2.6 added 10sp slop when shipping leaders (November 1987).
+% Version 2.7 improved rounding of negative-width characters (November 1987).
+% Version 2.8 fixed weird bug if no \patterns are used (December 1987).
+% Version 2.9 made \csname\endcsname's "relax" local (December 1987).
+% Version 2.91 fixed \outer\def\a0{}\a\a bug (April 1988).
+% Version 2.92 fixed \patterns, also file names with complex macros (May 1988).
+% Version 2.93 fixed negative halving in allocator when mem_min<0 (June 1988).
+% Version 2.94 kept open_log_file from calling fatal_error (November 1988).
+% Version 2.95 solved that problem a better way (December 1988).
+% Version 2.96 corrected bug in "Infinite shrinkage" recovery (January 1989).
+% Version 2.97 corrected blunder in creating 2.95 (February 1989).
+% Version 2.98 omitted save_for_after at outer level (March 1989).
+% Version 2.99 caught $$\begingroup\halign..$$ (June 1989).
+% Version 2.991 caught .5\ifdim.6... (June 1989).
+% Version 2.992 introduced major changes for 8-bit extensions (September 1989).
+% Version 2.993 fixed a save_stack synchronization bug et alia (December 1989).
+% Version 3.0 fixed unusual displays; was more \output robust (March 1990).
+% Version 3.1 fixed nullfont, disabled \write{\the\prevgraf} (September 1990).
+% Version 3.14 fixed unprintable font names and corrected typos (March 1991).
+% Version 3.141 more of same; reconstituted ligatures better (March 1992).
+% Version 3.1415 preserved nonexplicit kerns, tidied up (February 1993).
+% Version 3.14159 allowed fontmemsize to change; bulletproofing (March 1995).
+% Version 3.141592 fixed \xleaders, glueset, weird alignments (December 2002).
+% Version 3.1415926 was a general cleanup with minor fixes (February 2008).
+% Version 3.14159265 was similar (January 2014).
+% Version 3.141592653 was similar but more extensive (January 2021).
+
+% A reward of $327.68 will be paid to the first finder of any remaining bug.
+
+% This is the 1.1 version of Prote, developed during August 2021,
+% and corrected during september/october 2021 and amended in august 2023
+% for file primitives behavior matching input behavior.
+%
+% 1.0: adds primitives needed by LaTeX as listed in ltnews31.
+% - 2022-07-21: tiddying formal fix: a spurious line was a left over
+% of a removed paragraph (pointed by Martin Ruckert). Suppressed.
+%
+% 1.1: 2023-08-01: the new file primitives are used in LaTeX expecting
+% the input behavior that ".tex" be appended if no extension. So
+% modified to provide this.
+%
+% History towards 1.0 release:
+% 0.99.4:
+% - typos and style corrections provided by Martin Ruckert for
+% clean translation in Pascal (forward declarations and underscores
+% omitted);
+% - adjustements against e-TeX 2.6 instead of 2.1 (T. Laronde).
+% 0.99.7:
+% - \expanded was using an e-TeX procedure not doing def handling
+% => using scan_toks() now. Reported by Martin Ruckert and
+% test code narrowed down by Phelype Oleinik.
+% 0.99.8:
+% - \expanded again: scan_toks() sets def_ref. But back_list()
+% has to take not the refernce count, but the first token.
+% Passing def_ref, a next invocation of scan_toks() was inserting
+% a missing left brace and then reading pass the end of the token
+% list to find a matching right one. def_ref => link(def_ref).
+% Test code narrowed down by Phelype Oleinik (a great help!).
+% - The API for file related primitives has been changed: no error
+% is reported on failure to find/open and nothing is returned
+% (matches current other implementations).
+% 0.99.9:
+% - Fix typos in Pascal replacement text; xchg_buffer_length=0 =>
+% xchg_buffer_length:=0 (caught by Martin Ruckert).
+% 0.99.10:
+% - KerTeX Public License -> X11/MIT license.
+% 1.0:
+% - Just naming the official release. No change.
+%
+% This work was done by Thierry Laronde and is under the MIT/X11
+% license.
+%
+%
+
+% Here is TeX material that gets inserted after \input webmac
+\def\hang{\hangindent 3em\noindent\ignorespaces}
+\def\hangg#1 {\hang\hbox{#1 }}
+\def\textindent#1{\hangindent2.5em\noindent\hbox to2.5em{\hss#1 }\ignorespaces}
+\font\ninerm=cmr9
+\let\mc=\ninerm % medium caps for names like SAIL
+\def\Prote{{\tenrm P\kern-0.1em R\kern-0.15em\raise.11ex\hbox{o}%
+ \kern-0.22em T\kern-0.05em E}}
+\def\texprof{{\TeX}prof}
+\ifpdftex
+\sanitizecommand{\texprof}{TeXprof}
+\sanitizecommand{\eTeX}{eTeX}
+\sanitizecommand{\Prote}{PRoTE}
+\fi
+\font\tenlogo=logo10
+\def\MP{{\tenlogo META}\-{\tenlogo POST}}
+\def\eTeX{$\varepsilon$-\TeX}
+\font\revrm=xbmc10 % for right-to-left text
+% to generate xbmc10 (i.e., reflected cmbx10) use a file
+% xbmc10.mf containing:
+%+++++++++++++++++++++++++++++++++++++++++++++++++
+% if unknown cmbase: input cmbase fi
+% extra_endchar := extra_endchar &
+% "currentpicture:=currentpicture " &
+% "reflectedabout((.5[l,r],0),(.5[l,r],1));";
+% input cmbx10
+%+++++++++++++++++++++++++++++++++++++++++++++++++
+\ifx\beginL\undefined % this is TeX
+ \def\XeT{X\kern-.125em\lower.5ex\hbox{E}\kern-.1667emT}
+ \def\TeXeT{\TeX-\hbox{\revrm \XeT}} % for TeX-XeT
+ \def\TeXXeT{\TeX-\hbox{\revrm -\XeT}} % for TeX--XeT
+\else
+ \ifx\eTeXversion\undefined % this is \TeXeT
+ \def\TeXeT{\TeX-{\revrm\beginR\TeX\endR}} % for TeX-XeT
+ \def\TeXXeT{\TeX-{\revrm\beginR\TeX-\endR}} % for TeX--XeT
+ \else % this is \eTeX
+ \def\TeXeT{\TeX-{\TeXXeTstate=1\revrm\beginR\TeX\endR}} % for TeX-XeT
+ \def\TeXXeT{\TeX-{\TeXXeTstate=1\revrm\beginR\TeX-\endR}} % for TeX--XeT
+ \fi
+\fi
+\def\PASCAL{Pascal}
+\def\pdfTeX{pdf\TeX}
+\def\ph{\hbox{Pascal-H}}
+\def\pct!{{\char`\%}} % percent sign in ordinary text
+\def\grp{\.{\char'173...\char'175}}
+\font\logo=logo10 % font used for the METAFONT logo
+\def\MF{{\logo META}\-{\logo FONT}}
+\def\<#1>{$\langle#1\rangle$}
+\def\section{\mathhexbox278}
+
+\def\(#1){} % this is used to make section names sort themselves better
+\def\9#1{} % this is used for sort keys in the index via @@:sort key}{entry@@>
+
+\let\@@=\relax % we want to be able to \write a \?
+
+\def\title{\texprof}
+\def\LaTeX{L\kern-.36em\raise.3ex\hbox{\sc A}\kern-.15em\TeX}%
+% system dependent redefinitions of \title should come later
+% and should use:
+% \toks0=\expandafter{\title}
+% \edef\title{...\the\toks0...}
+%\let\maybe=\iffalse % print only changed modules
+\def\topofcontents{\hsize 5.5in
+ \vglue 0pt plus 1fil minus 1.5in
+ \def\@@##1]{\hbox to 1in{\hfil##1.\ }}
+ }
+\def\botofcontents{\vskip 0pt plus 1fil minus 1.5in}
+\pageno=3
+\def\glob{13} % this should be the section number of "<Global...>"
+\def\gglob{20, 26} % this should be the next two sections of "<Global...>"
+
+\def\.#1{\leavevmode\hbox{\tentex % typewriter type for strings
+ \let\\=\BS % backslash in a string
+ \let\'=\RQ % right quote in a string
+ \let\`=\LQ % left quote in a string
+ \let\{=\LB % left brace in a string
+ \let\}=\RB % right brace in a string
+ \let\~=\TL % tilde in a string
+ \let\ =\SP % space in a string
+ \let\_=\UL % underline in a string
+ \let\&=\AM % ampersand in a string
+ \let\^=\CF % circumflex in a string
+ #1\kern.05em}}
+\def\&#1{\leavevmode\hbox{\bf\def\_{\UL}%
+ #1\/\kern.05em}} % boldface type for reserved words
+\def\\#1{\leavevmode\hbox{\it\def\_{\UL}%
+ #1\/\kern.05em}} % italic type for identifiers
+\def\vb#1{{\rm #1}}
+%\def\^{\ifmmode\mathchar"222 \else\char`^ \fi} % pointer or hat
+\def\LQ{{\tt\char'22}} % left quote in a string
+\def\RQ{{\tt\char'23}} % right quote in a string
+\def\UL{{\tt\char`\_}} % underline character in a C identifier
+\def\CF{{\tt\char`^}}
+\def\dotdot{\mathrel{.\,.}} % double dot, used only in math mode
+\setbox\MGbox=\hbox{\kern1pt$\rightarrow$\kern1pt} % C pointer to field
+\def\MG{\copy\MGbox}
+@s dotdot TeX
+@s alpha_file int
+@s byte_file int
+@s word_file int
+@s uint8_t int
+@s int16_t int
+@s uint16_t int
+@s int32_t int
+@s uint32_t int
+@s halfword int
+@s nonnegative_integer int
+@s small_number int
+@s glue_ratio double
+@s Xdimen int
+@s Kern int
+@s Font int
+@s Glue int
+@s Disc int
+@s Lig int
+@s Rule int
+@s List int
+@s Kind int
+@s Info int
+@s kpse_file_format_type int
+@s Stretch int
+@s in TeX
+@s line normal
+@s to do
+
+@* Introduction.
+This is \texprof, an implementation of \TeX\ with an extension providing profiling
+capabilities. To be useful nowadays, the profiling capability is not the only
+extension of Donald Knuth's original implementation of \TeX.
+
+To obtain the present \CEE/ program, Donald Knuth's \PASCAL\ program was
+modified in a first step using most of the \eTeX\ extensions by
+Peter Breitenlohner and Phil Taylor.
+@^Breitenlohner, Peter@>
+@^Taylor, Phil@>
+Then further extensions were added using the \Prote\ change file
+of Thierry Laronde.
+@^Laronde, Thierry@>
+Then the constants defining the size of \TeX's data structures,
+notably the size of the |mem| array, were increased considerable. All these
+changes to the \PASCAL\ program are necessary to run a current \LaTeX\ on the
+resulting \TeX\ engine. The next important step was then to translate this
+literate \PASCAL\ program to a literate \CEE/ program using the
+{\tt web2w} source code translator. The use of the \CEE/ based \.{cweb} system
+instead of the \PASCAL\ based \.{WEB} system greatly simplifies the tool chain
+necessary for producing an executable program and allows true source level
+debugging of the resulting program. The latter is indispensable to carry out
+large scale modifications of the \TeX\ program and is of enormous help even
+for smaller modifications.
+The first modification of the resulting \CEE/ program was then the addition
+of \CEE/ routines from the \TeX\ Live project to enable the use of the
+kpath search library by Karl Berry.
+@^Berry, Karl@>
+The last and final step was the addition of profiling capabilities
+which includes, as the latest addition, the definition of numerous
+primitives that enable the \TeX\ profiler to pretend to be \pdfTeX.
+The code used there reuses parts of the \CEE/ version of \pdfTeX\ by
+H\`an Th\^e\llap{\raise 0.5ex\hbox{\'{}}} Th\`anh.
+@^Th\`anh, H\`an Th\^e\llap{\raise 0.5ex\hbox{\'{}}}@>
+
+All these changes left their traces in the text that follows.
+No attempt was made to streamline the text and rid it from the traces
+of its exciting history.
+So with the next paragraph you can start reading Donald Knuth's
+introduction to his \TeX\ program. Enjoy!
+\medskip
+
+This is \TeX, a document compiler intended to produce typesetting of high
+quality.
+The \PASCAL\ program that follows is the definition of \TeX82, a standard
+@:PASCAL}{\PASCAL@>
+@!@:TeX82}{\TeX82@>
+version of \TeX\ that is designed to be highly portable so that identical output
+will be obtainable on a great variety of computers.
+
+The main purpose of the following program is to explain the algorithms of \TeX\
+as clearly as possible. As a result, the program will not necessarily be very
+efficient when a particular \PASCAL\ compiler has translated it into a
+particular machine language. However, the program has been written so that it
+can be tuned to run efficiently in a wide variety of operating environments
+by making comparatively few changes. Such flexibility is possible because
+the documentation that follows is written in the \.{WEB} language, which is
+at a higher level than \PASCAL; the preprocessing step that converts \.{WEB}
+to \PASCAL\ is able to introduce most of the necessary refinements.
+Semi-automatic translation to other languages is also feasible, because the
+program below does not make extensive use of features that are peculiar to
+\PASCAL.
+
+A large piece of software like \TeX\ has inherent complexity that cannot
+be reduced below a certain level of difficulty, although each individual
+part is fairly simple by itself. The \.{WEB} language is intended to make
+the algorithms as readable as possible, by reflecting the way the
+individual program pieces fit together and by providing the
+cross-references that connect different parts. Detailed comments about
+what is going on, and about why things were done in certain ways, have
+been liberally sprinkled throughout the program. These comments explain
+features of the implementation, but they rarely attempt to explain the
+\TeX\ language itself, since the reader is supposed to be familiar with
+{\sl The \TeX book}.
+@.WEB@>
+@:TeXbook}{\sl The \TeX book@>
+
+@ The present implementation has a long ancestry, beginning in the summer
+of~1977, when Michael~F. Plass and Frank~M. Liang designed and coded
+a prototype
+@^Plass, Michael Frederick@>
+@^Liang, Franklin Mark@>
+@^Knuth, Donald Ervin@>
+based on some specifications that the author (in the following, unless
+specified, ``the author'' refers to D.E.~Knuth) had made in May of that year.
+This original proto\TeX\ included macro definitions and elementary
+manipulations on boxes and glue, but it did not have line-breaking,
+page-breaking, mathematical formulas, alignment routines, error recovery,
+or the present semantic nest; furthermore,
+it used character lists instead of token lists, so that a control sequence
+like \.{\\halign} was represented by a list of seven characters. A
+complete version of \TeX\ was designed and coded by the author in late
+1977 and early 1978; that program, like its prototype, was written in the
+{\mc SAIL} language, for which an excellent debugging system was
+available. Preliminary plans to convert the {\mc SAIL} code into a form
+somewhat like the present ``web'' were developed by Luis Trabb~Pardo and
+@^Trabb Pardo, Luis Isidoro@>
+the author at the beginning of 1979, and a complete implementation was
+created by Ignacio~A. Zabala in 1979 and 1980. The \TeX82 program, which
+@^Zabala Salelles, Ignacio Andr\'es@>
+was written by the author during the latter part of 1981 and the early
+part of 1982, also incorporates ideas from the 1979 implementation of
+@^Guibas, Leonidas Ioannis@>
+@^Sedgewick, Robert@>
+@^Wyatt, Douglas Kirk@>
+\TeX\ in {\mc MESA} that was written by Leonidas Guibas, Robert Sedgewick,
+and Douglas Wyatt at the Xerox Palo Alto Research Center. Several hundred
+refinements were introduced into \TeX82 based on the experiences gained with
+the original implementations, so that essentially every part of the system
+has been substantially improved. After the appearance of ``Version 0'' in
+September 1982, this program benefited greatly from the comments of
+many other people, notably David~R. Fuchs and Howard~W. Trickey.
+A final revision in September 1989 extended the input character set to
+eight-bit codes and introduced the ability to hyphenate words from
+different languages, based on some ideas of Michael~J. Ferguson.
+@^Fuchs, David Raymond@>
+@^Trickey, Howard Wellington@>
+@^Ferguson, Michael John@>
+
+No doubt there still is plenty of room for improvement, but the author
+is firmly committed to keeping \TeX82 ``frozen'' from now on; stability
+and reliability are to be its main virtues.
+
+On the other hand, the \.{WEB} description can be extended without changing
+the core of \TeX82 itself, and the program has been designed so that such
+extensions are not extremely difficult to make.
+The |banner| string defined here should be changed whenever \TeX\
+undergoes any modifications, so that it will be clear which version of
+\TeX\ might be the guilty party when a problem arises.
+@^extensions to \TeX@>
+@^system dependencies@>
+
+This program contains code for various features extending \TeX,
+therefore this program is called `\Prote' and not
+`\TeX'; the official name `\TeX' by itself is reserved
+for software systems that are fully compatible with each other.
+A special test suite called the ``\.{TRIP} test'' is available for
+helping to determine whether a particular implementation deserves to be
+known as `\TeX' [cf.~Stanford Computer Science report CS1027,
+November 1984].
+
+A similar test suite called the ``\.{SELLETTE} test'' is available for
+helping to determine whether a particular implementation deserves to be
+known as `\Prote'.
+
+@d eTeX_version 2 /* \.{\\eTeXversion} */
+@d eTeX_revision ".6" /* \.{\\eTeXrevision} */
+@d eTeX_version_string "-2.6" /*current \eTeX\ version*/
+@#
+@d TeX_banner "This is TeX, Version 3.141592653" /*printed when \TeX\ starts*/
+@#
+@#
+@d TEX ETEX /*change program name into |ETEX|*/
+@#
+@d eTeX_states 1 /*number of \eTeX\ state variables in |eqtb|*/
+@#
+@d Prote_version_string "3.141592653-2.6-1.1.0" /*current \Prote\ version*/
+@d Prote_version 1 /* \.{\\Proteversion} */
+@d Prote_revision ".1.0" /* \.{\\Proterevision} */
+@#
+@d Prote_banner "This is Prote, Version " Prote_version_string
+ /*printed when \Prote\ starts*/
+@#
+@d banner Prote_banner
+
+@ Different \PASCAL s have slightly different conventions, and the present
+@!@:PASCAL H}{\ph@>
+program expresses \TeX\ in terms of the \PASCAL\ that was
+available to the author in 1982. Constructions that apply to
+this particular compiler, which we shall call \ph, should help the
+reader see how to make an appropriate interface for other systems
+if necessary. (\ph\ is Charles Hedrick's modification of a compiler
+@^Hedrick, Charles Locke@>
+for the DECsystem-10 that was originally developed at the University of
+Hamburg; cf.\ {\sl Software---Practice and Experience \bf6} (1976),
+29--42. The \TeX\ program below is intended to be adaptable, without
+extensive changes, to most other versions of \PASCAL, so it does not fully
+use the admirable features of \ph. Indeed, a conscious effort has been
+made here to avoid using several idiosyncratic features of standard
+\PASCAL\ itself, so that most of the code can be translated mechanically
+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
+|double=int|; no procedures are declared local to other procedures.)
+
+The portions of this program that involve system-dependent code, where
+changes might be necessary because of differences between \PASCAL\ compilers
+and/or differences between
+operating systems, can be identified by looking at the sections whose
+numbers are listed under `system dependencies' in the index. Furthermore,
+the index entries for `dirty \PASCAL' list all places where the restrictions
+of \PASCAL\ have not been followed perfectly, for one reason or another.
+@!@^system dependencies@>
+@!@^dirty \PASCAL@>
+
+Incidentally, \PASCAL's standard |round| function can be problematical,
+because it disagrees with the IEEE floating-point standard.
+Many implementors have
+therefore chosen to substitute their own home-grown rounding procedure.
+
+@ The following is an outline of the program, whose
+components will be filled in later, using the conventions of \.{cweb}.
+@.WEB@>
+For example, the portion of the program called `\X\glob:Global
+variables\X' below will be replaced by a sequence of variable declarations
+that starts in $\section\glob$ of this documentation. In this way, we are able
+to define each individual global variable when we are prepared to
+understand what it means; we do not have to define all of the globals at
+once. Cross references in $\section\glob$, where it says ``See also
+sections \gglob, \dots,'' also make it possible to look at the set of
+all global variables, if desired. Similar remarks apply to the other
+portions of the program.
+
+The program starts with inserting header files and occassionaly a function
+must be placed before declaring \TeX's macros, because the function
+uses identifiers that \TeX will declare as macros.
+
+@p @<Header files and function declarations@>@;
+@h
+enum {@<Constants in the outer block@>@;
+ @!empty_string=256 /*the empty string follows after 256 characters*/
+};
+@<Types in the outer block@>@;
+@<Forward declarations@>@;
+@<Global variables@>@;
+@#
+static void initialize(void) /*this procedure gets things started properly*/
+ {@+@<Local variables for initialization@>@;
+ @<Initialize whatever \TeX\ might access@>;
+ } @#
+@<Basic printing procedures@>@;
+@<Error handling procedures@>@;
+
+@ The overall \TeX\ program begins with the heading just shown, after which
+comes a bunch of procedure declarations and function declarations.
+Finally we will get to the main program, which begins with the
+comment `|start_here|'. If you want to skip down to the
+main program now, you can look up `|start_here|' in the index.
+But the author suggests that the best way to understand this program
+is to follow pretty much the order of \TeX's components as they appear in the
+\.{WEB} description you are now reading, since the present ordering is
+intended to combine the advantages of the ``bottom up'' and ``top down''
+approaches to the problem of understanding a somewhat complicated system.
+
+@ There is no need to declare labels in \CEE/.
+
+@ Some of the code below is intended to be used only when diagnosing the
+strange behavior that sometimes occurs when \TeX\ is being installed or
+when system wizards are fooling around with \TeX\ without quite knowing
+what they are doing. Such code will not normally be compiled; it is
+delimited by the codewords `$|@t\#\&{ifdef} \.{DEBUG}@>|\ldots|@t\#\&{endif}@>|$', with apologies
+to people who wish to preserve the purity of English.
+
+Similarly, there is some conditional code delimited by
+`$|@t\#\&{ifdef} \.{STAT}@>|\ldots|@t\#\&{endif}@>|$' that is intended for use when statistics are to be
+kept about \TeX's memory usage. The |@t\#\&{ifdef} \.{STAT}@>| $\ldots$ |@t\#\&{endif}@>| code also
+implements diagnostic information for \.{\\tracingparagraphs},
+\.{\\tracingpages}, and \.{\\tracingrestores}.
+@^debugging@>
+
+@ This program has two important variations: (1) There is a long and slow
+version called \.{INITEX}, which does the extra calculations needed to
+@.INITEX@>
+initialize \TeX's internal tables; and (2)~there is a shorter and faster
+production version, which cuts the initialization to a bare minimum.
+Parts of the program that are needed in (1) but not in (2) are delimited by
+the codewords `$|@t\#\&{ifdef} \.{INIT}@>|\ldots|@t\#\&{endif}@>|$'.
+
+\TeX\ Live has established the common practice
+to select the initialization code at runtime
+using the |iniversion| variable.
+
+@<Initialize whatever...@>=
+@<Set initial values of key variables@>@/
+#ifdef @!INIT
+if (iniversion) /* \TeX\ Live*/
+ {@+@<Initialize table entries (done by \.{INITEX} only)@>@;@+}
+#endif
+
+@ The declaration of the necessary type definitions are
+contained in header files.
+
+
+@<Header files and function declarations@>=
+#include <stdbool.h>
+#include <string.h>
+#include <math.h>
+
+@ Further it is necessary to define some build in primitives of
+\PASCAL\ that are otherwise not available in~\CEE/.
+@:PASCAL H}{\ph@>
+
+@d odd(X) ((X)&1)
+@d chr(X) ((unsigned char)(X))
+@d ord(X) ((unsigned int)(X))
+@d abs(X) ((X)>-(X)?(X):-(X))
+@d round(X) ((int)((X)>=0.0?floor((X)+0.5):ceil((X)-0.5)))
+
+@ The following parameters can be changed at compile time to extend or
+reduce \TeX's capacity. They may have different values in \.{INITEX} and
+in production versions of \TeX.
+@.INITEX@>
+@^system dependencies@>
+
+@<Constants...@>=
+@!mem_max=5000000, /*greatest index in \TeX's internal |mem| array;
+ must be strictly less than |max_halfword|;
+ must be equal to |mem_top| in \.{INITEX}, otherwise | >= mem_top|*/
+@!mem_min=0, /*smallest index in \TeX's internal |mem| array;
+ must be |min_halfword| or more;
+ must be equal to |mem_bot| in \.{INITEX}, otherwise | <= mem_bot|*/
+@!buf_size=2000000, /*maximum number of characters simultaneously present in
+ current lines of open files and in control sequences between
+ \.{\\csname} and \.{\\endcsname}; must not exceed |max_halfword|*/
+@!error_line=79, /*width of context lines on terminal error messages*/
+@!half_error_line=50, /*width of first lines of contexts in terminal
+ error messages; should be between 30 and |error_line-15|*/
+@!max_print_line=79, /*width of longest text lines output; should be at least 60*/
+@!stack_size=5000, /*maximum number of simultaneous input sources*/
+@!max_in_open=15, /*maximum number of input files and error insertions that
+ can be going on simultaneously*/
+@!font_max=255, /*maximum internal font number; must not exceed |max_quarterword|
+ and must be at most |font_base+256|*/
+@!font_mem_size=8000000, /*number of words of |font_info| for all fonts*/
+@!param_size=10000, /*maximum number of simultaneous macro parameters*/
+@!nest_size=500, /*maximum number of semantic levels simultaneously active*/
+@!max_strings=500000, /*maximum number of strings; must not exceed |max_halfword|*/
+@!string_vacancies=90000, /*the minimum number of characters that should be
+ available for the user's control sequences and font names,
+ after \TeX's own error messages are stored*/
+@!pool_size=6250000, /*maximum number of characters in strings, including all
+ error messages and help texts, and the names of all fonts and
+ control sequences; must exceed |string_vacancies| by the total
+ length of \TeX's own strings, which is currently about 23000*/
+@!save_size=100000, /*space for saving values outside of current group; must be
+ at most |max_halfword|*/
+@!trie_size=1000000, /*space for hyphenation patterns; should be larger for
+ \.{INITEX} than it is in production versions of \TeX*/
+@!trie_op_size=35111, /*space for ``opcodes'' in the hyphenation patterns*/
+@!dvi_buf_size=16384, /*size of the output buffer; must be a multiple of 8*/
+@!file_name_size=1024, /*file names shouldn't be longer than this*/
+@!xchg_buffer_size=64, /*must be at least 64*/
+ /*size of |eight_bits| buffer for exchange with system routines*/
+
+@ Like the preceding parameters, the following quantities can be changed
+at compile time to extend or reduce \TeX's capacity. But if they are changed,
+it is necessary to rerun the initialization program \.{INITEX}
+@.INITEX@>
+to generate new tables for the production \TeX\ program.
+One can't simply make helter-skelter changes to the following constants,
+since certain rather complex initialization
+numbers are computed from them. They are defined here using
+\.{WEB} macros, instead of being put into the above |enum| list in order to
+emphasize this distinction.
+
+@d mem_bot 0 /*smallest index in the |mem| array dumped by \.{INITEX};
+ must not be less than |mem_min|*/
+@d mem_top 5000000 /*largest index in the |mem| array dumped by \.{INITEX};
+ must be substantially larger than |mem_bot|
+ and not greater than |mem_max|*/
+@d font_base 0 /*smallest internal font number; must not be less
+ than |min_quarterword|*/
+@d hash_size 45000 /*maximum number of control sequences; it should be at most
+ about |(mem_max-mem_min)/(double)10|*/
+@d hash_prime 35999 /*a prime number equal to about 85\pct! of |hash_size|*/
+@d hyph_size 8191 /*another prime; the number of \.{\\hyphenation} exceptions*/
+@^system dependencies@>
+
+@ In case somebody has inadvertently made bad settings of the ``constants,''
+\TeX\ checks them using a global variable called |bad|.
+
+This is the first of many sections of \TeX\ where global variables are
+defined.
+
+@<Glob...@>=
+static int @!bad; /*is some ``constant'' wrong?*/
+
+@ Later on we will say `\ignorespaces|if (mem_max >= max_halfword) bad=14|',
+or something similar. (We can't do that until |max_halfword| has been defined.)
+
+@<Check the ``constant'' values for consistency@>=
+bad=0;
+if ((half_error_line < 30)||(half_error_line > error_line-15)) bad=1;
+if (max_print_line < 60) bad=2;
+if (dvi_buf_size%8!=0) bad=3;
+if (mem_bot+1100 > mem_top) bad=4;
+if (hash_prime > hash_size) bad=5;
+if (max_in_open >= 128) bad=6;
+if (mem_top < 256+11) bad=7; /*we will want |null_list > 255|*/
+
+@ Labels are given symbolic names by the following definitions, so that
+occasional |goto| statements will be meaningful. We insert the label
+`|end|' just before the `\ignorespaces|} |\unskip' of a procedure in
+which we have used the `|goto end|' statement defined below; the label
+`|restart|' is occasionally used at the very beginning of a procedure; and
+the label `|reswitch|' is occasionally used just prior to a |case|
+statement in which some cases change the conditions and we wish to branch
+to the newly applicable case. Loops that are set up with the |loop|
+construction defined below are commonly exited by going to `|done|' or to
+`|found|' or to `|not_found|', and they are sometimes repeated by going to
+`|resume|'. If two or more parts of a subroutine start differently but
+end up the same, the shared code may be gathered together at
+`|common_ending|'.
+
+Incidentally, this program never declares a label that isn't actually used,
+because some fussy \PASCAL\ compilers will complain about redundant labels.
+
+@ Here are some macros for common programming idioms.
+
+@d incr(A) A=A+1 /*increase a variable by unity*/
+@d decr(A) A=A-1 /*decrease a variable by unity*/
+@d negate(A) A=-A /*change the sign of a variable*/
+@d loop @+while (true) @+ /*repeat over and over until a |goto| happens*/
+@f loop else
+ /*\.{WEB}'s |else| acts like `\ignorespaces|while true do|\unskip'*/
+@d do_nothing /*empty statement*/
+@d empty 0 /*symbolic name for a null constant*/
+
+@* The character set.
+In order to make \TeX\ readily portable to a wide variety of
+computers, all of its input text is converted to an internal eight-bit
+code that includes standard ASCII, the ``American Standard Code for
+Information Interchange.'' This conversion is done immediately when each
+character is read in. Conversely, characters are converted from ASCII to
+the user's external representation just before they are output to a
+text file.
+
+Such an internal code is relevant to users of \TeX\ primarily because it
+governs the positions of characters in the fonts. For example, the
+character `\.A' has ASCII code $65=0101$, and when \TeX\ typesets
+this letter it specifies character number 65 in the current font.
+If that font actually has `\.A' in a different position, \TeX\ doesn't
+know what the real position is; the program that does the actual printing from
+\TeX's device-independent files is responsible for converting from ASCII to
+a particular font encoding.
+@^ASCII code@>
+
+\TeX's internal code also defines the value of constants
+that begin with a reverse apostrophe; and it provides an index to the
+\.{\\catcode}, \.{\\mathcode}, \.{\\uccode}, \.{\\lccode}, and \.{\\delcode}
+tables.
+
+@ Characters of text that have been converted to \TeX's internal form
+are said to be of type |ASCII_code|, which is a subrange of the integers.
+
+@<Types...@>=
+typedef uint8_t ASCII_code; /*eight-bit numbers*/
+
+@ The original \PASCAL\ compiler was designed in the late 60s, when six-bit
+character sets were common, so it did not make provision for lowercase
+letters. Nowadays, of course, we need to deal with both capital and small
+letters in a convenient way, especially in a program for typesetting;
+so the present specification of \TeX\ has been written under the assumption
+that the \PASCAL\ compiler and run-time system permit the use of text files
+with more than 64 distinguishable characters. More precisely, we assume that
+the character set contains at least the letters and symbols associated
+with ASCII codes 040 through 0176; all of these characters are now
+available on most computer terminals.
+
+Since we are dealing with more characters than were present in the first
+\PASCAL\ compilers, we have to decide what to call the associated data
+type. Some \PASCAL s use the original name |unsigned char| for the
+characters in text files, even though there now are more than 64 such
+characters, while other \PASCAL s consider |unsigned char| to be a 64-element
+subrange of a larger data type that has some other name.
+
+In order to accommodate this difference, we shall use the name |text_char|
+to stand for the data type of the characters that are converted to and
+from |ASCII_code| when they are input and output. We shall also assume
+that |text_char| consists of the elements |chr(first_text_char)| through
+|chr(last_text_char)|, inclusive. The following definitions should be
+adjusted if necessary.
+@^system dependencies@>
+
+@s text_char char
+@d text_char unsigned char /*the data type of characters in text files*/
+@d first_text_char 0 /*ordinal number of the smallest element of |text_char|*/
+@d last_text_char 255 /*ordinal number of the largest element of |text_char|*/
+
+@<Local variables for init...@>=
+int @!i;
+
+@ The \TeX\ processor converts between ASCII code and
+the user's external character set by means of arrays |xord| and |xchr|
+that are analogous to \PASCAL's |ord| and |chr| functions.
+
+@<Glob...@>=
+static ASCII_code @!xord[256];
+ /*specifies conversion of input characters*/
+static text_char @!xchr[256];
+ /*specifies conversion of output characters*/
+
+@ Since we are assuming that our \PASCAL\ system is able to read and
+write the visible characters of standard ASCII (although not
+necessarily using the ASCII codes to represent them), the following
+assignment statements initialize the standard part of the |xchr| array
+properly, without needing any system-dependent changes. On the other
+hand, it is possible to implement \TeX\ with less complete character
+sets, and in such cases it will be necessary to change something here.
+@^system dependencies@>
+
+@<Set init...@>=
+xchr[040]=' ';
+xchr[041]='!';
+xchr[042]='"';
+xchr[043]='#';
+xchr[044]='$';
+xchr[045]='%';
+xchr[046]='&';
+xchr[047]='\'';@/
+xchr[050]='(';
+xchr[051]=')';
+xchr[052]='*';
+xchr[053]='+';
+xchr[054]=',';
+xchr[055]='-';
+xchr[056]='.';
+xchr[057]='/';@/
+xchr[060]='0';
+xchr[061]='1';
+xchr[062]='2';
+xchr[063]='3';
+xchr[064]='4';
+xchr[065]='5';
+xchr[066]='6';
+xchr[067]='7';@/
+xchr[070]='8';
+xchr[071]='9';
+xchr[072]=':';
+xchr[073]=';';
+xchr[074]='<';
+xchr[075]='=';
+xchr[076]='>';
+xchr[077]='?';@/
+xchr[0100]='@@';
+xchr[0101]='A';
+xchr[0102]='B';
+xchr[0103]='C';
+xchr[0104]='D';
+xchr[0105]='E';
+xchr[0106]='F';
+xchr[0107]='G';@/
+xchr[0110]='H';
+xchr[0111]='I';
+xchr[0112]='J';
+xchr[0113]='K';
+xchr[0114]='L';
+xchr[0115]='M';
+xchr[0116]='N';
+xchr[0117]='O';@/
+xchr[0120]='P';
+xchr[0121]='Q';
+xchr[0122]='R';
+xchr[0123]='S';
+xchr[0124]='T';
+xchr[0125]='U';
+xchr[0126]='V';
+xchr[0127]='W';@/
+xchr[0130]='X';
+xchr[0131]='Y';
+xchr[0132]='Z';
+xchr[0133]='[';
+xchr[0134]='\\';
+xchr[0135]=']';
+xchr[0136]='^';
+xchr[0137]='_';@/
+xchr[0140]='`';
+xchr[0141]='a';
+xchr[0142]='b';
+xchr[0143]='c';
+xchr[0144]='d';
+xchr[0145]='e';
+xchr[0146]='f';
+xchr[0147]='g';@/
+xchr[0150]='h';
+xchr[0151]='i';
+xchr[0152]='j';
+xchr[0153]='k';
+xchr[0154]='l';
+xchr[0155]='m';
+xchr[0156]='n';
+xchr[0157]='o';@/
+xchr[0160]='p';
+xchr[0161]='q';
+xchr[0162]='r';
+xchr[0163]='s';
+xchr[0164]='t';
+xchr[0165]='u';
+xchr[0166]='v';
+xchr[0167]='w';@/
+xchr[0170]='x';
+xchr[0171]='y';
+xchr[0172]='z';
+xchr[0173]='{';
+xchr[0174]='|';
+xchr[0175]='}';
+xchr[0176]='~';@/
+
+@ Some of the ASCII codes without visible characters have been given symbolic
+names in this program because they are used with a special meaning.
+
+@d null_code 00 /*ASCII code that might disappear*/
+@d carriage_return 015 /*ASCII code used at end of line*/
+@d invalid_code 0177 /*ASCII code that many systems prohibit in text files*/
+
+@ The ASCII code is ``standard'' only to a certain extent, since many
+computer installations have found it advantageous to have ready access
+to more than 94 printing characters. Appendix~C of {\sl The \TeX book\/}
+gives a complete specification of the intended correspondence between
+characters and \TeX's internal representation.
+@:TeXbook}{\sl The \TeX book@>
+
+If \TeX\ is being used
+on a garden-variety \PASCAL\ for which only standard ASCII
+codes will appear in the input and output files, it doesn't really matter
+what codes are specified in |xchr[0 dotdot 037]|, but the safest policy is to
+blank everything out by using the code shown below.
+
+However, other settings of |xchr| will make \TeX\ more friendly on
+computers that have an extended character set, so that users can type things
+like `\.^^Z' instead of `\.{\\ne}'. People with extended character sets can
+assign codes arbitrarily, giving an |xchr| equivalent to whatever
+characters the users of \TeX\ are allowed to have in their input files.
+It is best to make the codes correspond to the intended interpretations as
+shown in Appendix~C whenever possible; but this is not necessary. For
+example, in countries with an alphabet of more than 26 letters, it is
+usually best to map the additional letters into codes less than~040.
+To get the most ``permissive'' character set, change |' '| on the
+right of these assignment statements to |chr(i)|.
+@^character set dependencies@>
+@^system dependencies@>
+
+@<Set init...@>=
+for (i=0; i<=037; i++) xchr[i]=chr(i); /* \TeX\ Live*/
+for (i=0177; i<=0377; i++) xchr[i]=chr(i); /* \TeX\ Live*/
+
+@ The following system-independent code makes the |xord| array contain a
+suitable inverse to the information in |xchr|. Note that if |xchr[i]==xchr[j]|
+where |i < j < 0177|, the value of |xord[xchr[i]]| will turn out to be
+|j| or more; hence, standard ASCII code numbers will be used instead of
+codes below 040 in case there is a coincidence.
+
+@<Set init...@>=
+for (i=first_text_char; i<=last_text_char; i++) xord[chr(i)]=invalid_code;
+for (i=0200; i<=0377; i++) xord[xchr[i]]=i;
+for (i=0; i<=0176; i++) xord[xchr[i]]=i;
+
+@* Input and output.
+The bane of portability is the fact that different operating systems treat
+input and output quite differently, perhaps because computer scientists
+have not given sufficient attention to this problem. People have felt somehow
+that input and output are not part of ``real'' programming. Well, it is true
+that some kinds of programming are more fun than others. With existing
+input/output conventions being so diverse and so messy, the only sources of
+joy in such parts of the code are the rare occasions when one can find a
+way to make the program a little less bad than it might have been. We have
+two choices, either to attack I/O now and get it over with, or to postpone
+I/O until near the end. Neither prospect is very attractive, so let's
+get it over with.
+
+The basic operations we need to do are (1)~inputting and outputting of
+text, to or from a file or the user's terminal; (2)~inputting and
+outputting of eight-bit bytes, to or from a file; (3)~instructing the
+operating system to initiate (``open'') or to terminate (``close'') input or
+output from a specified file; (4)~testing whether the end of an input
+file has been reached.
+
+\TeX\ needs to deal with two kinds of files.
+We shall use the term |alpha_file| for a file that contains textual data,
+and the term |byte_file| for a file that contains eight-bit binary information.
+These two types turn out to be the same on many computers, but
+sometimes there is a significant distinction, so we shall be careful to
+distinguish between them. Standard protocols for transferring
+such files from computer to computer, via high-speed networks, are
+now becoming available to more and more communities of users.
+
+The program actually makes use also of a third kind of file, called a
+|word_file|, when dumping and reloading base information for its own
+initialization. We shall define a word file later; but it will be possible
+for us to specify simple operations on word files before they are defined.
+
+@<Types...@>=
+typedef uint8_t eight_bits; /*unsigned one-byte quantity*/
+typedef struct {@+FILE *f;@+text_char@,d;@+} alpha_file; /*files that contain textual data*/
+typedef struct {@+FILE *f;@+eight_bits@,d;@+} byte_file; /*files that contain binary data*/
+
+@ Most of what we need to do with respect to input and output can be handled
+by the I/O facilities that are standard in \PASCAL, i.e., the routines
+called |get|, |put|, |eof|, and so on. But
+standard \PASCAL\ does not allow file variables to be associated with file
+names that are determined at run time, so it cannot be used to implement
+\TeX; some sort of extension to \PASCAL's ordinary |reset| and |rewrite|
+is crucial for our purposes. We shall assume that |name_of_file| is a variable
+of an appropriate type such that the \PASCAL\ run-time system being used to
+implement \TeX\ can open a file whose external name is specified by
+|name_of_file|.
+@^system dependencies@>
+
+@<Glob...@>=
+static unsigned char @!name_of_file0[file_name_size+1]={0},
+ *const @!name_of_file = @!name_of_file0-1;@;@/
+ /*on some systems this may be a \&{record} variable*/
+static int @!name_length;@/ /*this many characters are actually
+ relevant in |name_of_file| (the rest are blank)*/
+
+@ To open files, \TeX\ used \PASCAL's |reset| function.
+We use the {\tt kpathsearch} library to implement new functions
+in the section on \TeX\ Live Integration.
+Here we give only the function prototypes.
+
+\TeX's file-opening functions do not issue their own
+error messages if something goes wrong. If a file identified by
+|name_of_file| cannot be found,
+or if such a file cannot be opened for some other reason
+(e.g., someone may already be trying to write the same file)
+\TeX's file-opening functions return |false|.
+This allows \TeX\ to undertake appropriate corrective action.
+@^system dependencies@>
+
+@p
+static FILE*open_in(char*filename,kpse_file_format_type t,const char*rwb); /* \TeX\ Live*/
+static bool a_open_in(alpha_file *f); /*open a text file for input*/
+static bool b_open_in(byte_file *f); /*open a binary file for input*/
+static bool w_open_in(word_file *f); /*open a word file for input*/
+static FILE *open_out(const char *file_name, const char *file_mode); /* \TeX\ Live*/
+static bool a_open_out(alpha_file *f); /*open a text file for output*/
+static bool b_open_out(byte_file *f); /*open a binary file for output*/
+#ifdef @!INIT
+static bool w_open_out(word_file *f); /*open a word file for output*/
+#endif
+
+@ Files can be closed with the \ph\ routine `|pascal_close(f)|', which
+@:PASCAL H}{\ph@>
+@^system dependencies@>
+should be used when all input or output with respect to |f| has been completed.
+This makes |f| available to be opened again, if desired; and if |f| was used for
+output, the |pascal_close| operation makes the corresponding external file appear
+on the user's area, ready to be read.
+
+These procedures should not generate error messages if a file is
+being closed before it has been successfully opened.
+
+@p static void a_close(alpha_file *f) /*close a text file*/
+{@+pascal_close((*f));
+}
+@#
+static void b_close(byte_file *f) /*close a binary file*/
+{@+pascal_close((*f));
+}
+@#
+static void w_close(word_file *f) /*close a word file*/
+{@+pascal_close((*f));
+}
+
+@ Binary input and output are done with \PASCAL's ordinary |get| and |put|
+procedures, so we don't have to make any other special arrangements for
+binary~I/O. Text output is also easy to do with standard \PASCAL\ routines.
+The treatment of text input is more difficult, however, because
+of the necessary translation to |ASCII_code| values.
+\TeX's conventions should be efficient, and they should
+blend nicely with the user's operating environment.
+
+@ Input from text files is read one line at a time, using a routine called
+|input_ln|. This function is defined in terms of global variables called
+|buffer|, |first|, and |last| that will be described in detail later; for
+now, it suffices for us to know that |buffer| is an array of |ASCII_code|
+values, and that |first| and |last| are indices into this array
+representing the beginning and ending of a line of text.
+
+@<Glob...@>=
+static ASCII_code @!buffer[buf_size+1]; /*lines of characters being read*/
+static int @!first; /*the first unused position in |buffer|*/
+static int @!last; /*end of the line just input to |buffer|*/
+static int @!max_buf_stack; /*largest index used in |buffer|*/
+
+@ The |input_ln| function brings the next line of input from the specified
+file into available positions of the buffer array and returns the value
+|true|, unless the file has already been entirely read, in which case it
+returns |false| and sets |last=first|. In general, the |ASCII_code|
+numbers that represent the next line of the file are input into
+|buffer[first]|, |buffer[first+1]|, \dots, |buffer[last-1]|; and the
+global variable |last| is set equal to |first| plus the length of the
+line. Trailing blanks are removed from the line; thus, either |last==first|
+(in which case the line was entirely blank) or |buffer[last-1]!=' '|.
+
+An overflow error is given, however, if the normal actions of |input_ln|
+would make |last >= buf_size|; this is done so that other parts of \TeX\
+can safely look at the contents of |buffer[last+1]| without overstepping
+the bounds of the |buffer| array. Upon entry to |input_ln|, the condition
+|first < buf_size| will always hold, so that there is always room for an
+``empty'' line.
+
+The variable |max_buf_stack|, which is used to keep track of how large
+the |buf_size| parameter must be to accommodate the present job, is
+also kept up to date by |input_ln|.
+
+If the |bypass_eoln| parameter is |true|, |input_ln| will do a |get|
+before looking at the first character of the line; this skips over
+an |eoln| that was in |f.d|. The procedure does not do a |get| when it
+reaches the end of the line; therefore it can be used to acquire input
+from the user's terminal as well as from ordinary text files.
+
+Standard \PASCAL\ says that a file should have |eoln| immediately
+before |eof|, but \TeX\ needs only a weaker restriction: If |eof|
+occurs in the middle of a line, the system function |eoln| should return
+a |true| result (even though |f.d| will be undefined).
+
+Since the inner loop of |input_ln| is part of \TeX's ``inner loop''---each
+character of input comes in at this place---it is wise to reduce system
+overhead by making use of special routines that read in an entire array
+of characters at once, if such routines are available. The following
+code uses standard \PASCAL\ to illustrate what needs to be done, but
+finer tuning is often possible at well-developed \PASCAL\ sites.
+@^inner loop@>
+
+@p static bool input_ln(alpha_file *f, bool @!bypass_eoln)
+ /*inputs the next line or returns |false|*/
+{@+int last_nonblank; /*|last| with trailing blanks removed*/
+if (bypass_eoln) if (!eof((*f))) get((*f));
+ /*input the first character of the line into |f.d|*/
+last=first; /*cf.\ Matthew 19\thinspace:\thinspace30*/
+if (eof((*f))) return false;
+else{@+last_nonblank=first;
+ while (!eoln((*f)))
+ {@+if (last >= max_buf_stack)
+ {@+max_buf_stack=last+1;
+ if (max_buf_stack==buf_size)
+ @<Report overflow of the input buffer, and abort@>;
+ }
+ buffer[last]=xord[(*f).d];get((*f));incr(last);
+ if (buffer[last-1]!=' ') last_nonblank=last;
+ }
+ last=last_nonblank;return true;
+ }
+}
+
+@ The user's terminal acts essentially like other files of text, except
+that it is used both for input and for output. When the terminal is
+considered an input file, the file variable is called |term_in|, and when it
+is considered an output file the file variable is |term_out|.
+@^system dependencies@>
+
+@<Glob...@>=
+static alpha_file @!term_in; /*the terminal as an input file*/
+static alpha_file @!term_out; /*the terminal as an output file*/
+
+@ Here is how to open the terminal files
+in \ph. The `\.{/I}' switch suppresses the first |get|.
+@:PASCAL H}{\ph@>
+@^system dependencies@>
+
+@d t_open_in term_in.f=stdin /*open the terminal for text input*/
+@d t_open_out term_out.f=stdout /*open the terminal for text output*/
+
+@ Sometimes it is necessary to synchronize the input/output mixture that
+happens on the user's terminal, and three system-dependent
+procedures are used for this
+purpose. The first of these, |update_terminal|, is called when we want
+to make sure that everything we have output to the terminal so far has
+actually left the computer's internal buffers and been sent.
+The second, |clear_terminal|, is called when we wish to cancel any
+input that the user may have typed ahead (since we are about to
+issue an unexpected error message). The third, |wake_up_terminal|,
+is supposed to revive the terminal if the user has disabled it by
+some instruction to the operating system. The following macros show how
+these operations can be specified in \ph:
+@:PASCAL H}{\ph@>
+@^system dependencies@>
+
+@d update_terminal fflush(term_out.f) /*empty the terminal output buffer*/
+@d clear_terminal fflush(term_in.f) /*clear the terminal input buffer*/
+@d wake_up_terminal do_nothing /*cancel the user's cancellation of output*/
+
+@ We need a special routine to read the first line of \TeX\ input from
+the user's terminal. This line is different because it is read before we
+have opened the transcript file; there is sort of a ``chicken and
+egg'' problem here. If the user types `\.{\\input paper}' on the first
+line, or if some macro invoked by that line does such an \.{\\input},
+the transcript file will be named `\.{paper.log}'; but if no \.{\\input}
+commands are performed during the first line of terminal input, the transcript
+file will acquire its default name `\.{texput.log}'. (The transcript file
+will not contain error messages generated by the first line before the
+first \.{\\input} command.)
+@.texput@>
+
+The first line is even more special if we are lucky enough to have an operating
+system that treats \TeX\ differently from a run-of-the-mill \PASCAL\ object
+program. It's nice to let the user start running a \TeX\ job by typing
+a command line like `\.{tex paper}'; in such a case, \TeX\ will operate
+as if the first line of input were `\.{paper}', i.e., the first line will
+consist of the remainder of the command line, after the part that invoked
+\TeX.
+
+The first line is special also because it may be read before \TeX\ has
+input a format file. In such cases, normal error messages cannot yet
+be given. The following code uses concepts that will be explained later.
+(If the \PASCAL\ compiler does not support non-local |@!goto|\unskip, the
+@^system dependencies@>
+statement `|goto exit(0)|' should be replaced by something that
+quietly terminates the program.)
+
+@<Report overflow of the input buffer, and abort@>=
+if (format_ident==0)
+ {@+write_ln(term_out,"Buffer size exceeded!");exit(0);
+@.Buffer size exceeded@>
+ }
+else{@+cur_input.loc_field=first;cur_input.limit_field=last-1;
+ overflow("buffer size", buf_size);
+@:TeX capacity exceeded buffer size}{\quad buffer size@>
+ }
+
+@ Different systems have different ways to get started. But regardless of
+what conventions are adopted, the routine that initializes the terminal
+should satisfy the following specifications:
+
+\yskip\textindent{1)}It should open file |term_in| for input from the
+ terminal. (The file |term_out| will already be open for output to the
+ terminal.)
+
+\textindent{2)}If the user has given a command line, this line should be
+ considered the first line of terminal input. Otherwise the
+ user should be prompted with `\.{**}', and the first line of input
+ should be whatever is typed in response.
+
+\textindent{3)}The first line of input, which might or might not be a
+ command line, should appear in locations |first| to |last-1| of the
+ |buffer| array.
+
+\textindent{4)}The global variable |loc| should be set so that the
+ character to be read next by \TeX\ is in |buffer[loc]|. This
+ character should not be blank, and we should have |loc < last|.
+
+\yskip\noindent(It may be necessary to prompt the user several times
+before a non-blank line comes in. The prompt is `\.{**}' instead of the
+later `\.*' because the meaning is slightly different: `\.{\\input}' need
+not be typed immediately after~`\.{**}'.)
+
+@d loc cur_input.loc_field /*location of first unread character in |buffer|*/
+
+@ The following routine calls |input_command_line|
+to retrieve a possible command line.
+@^system dependencies@>
+
+@p static bool init_terminal(void) /*gets the terminal input started*/
+{@+
+t_open_in;
+if (input_command_line()) return true; /* \TeX\ Live */
+loop@+{@+wake_up_terminal;pascal_write(term_out,"**");update_terminal;
+@.**@>
+ if (!input_ln(&term_in, true)) /*this shouldn't happen*/
+ {@+write_ln(term_out);
+ pascal_write(term_out,"! End of file on the terminal... why?");
+@.End of file on the terminal@>
+ return false;
+ }
+ loc=first;
+ while ((loc < last)&&(buffer[loc]==' ')) incr(loc);
+ if (loc < last)
+ {@+return true;
+ /*return unless the line was all blank*/
+ }
+ write_ln(term_out,"Please type the name of your input file.");
+ }
+}
+
+@* String handling.
+Control sequence names and diagnostic messages are variable-length strings
+of eight-bit characters. Since \PASCAL\ does not have a well-developed string
+mechanism, \TeX\ does all of its string processing by homegrown methods.
+
+Elaborate facilities for dynamic strings are not needed, so all of the
+necessary operations can be handled with a simple data structure.
+The array |str_pool| contains all of the (eight-bit) ASCII codes in all
+of the strings, and the array |str_start| contains indices of the starting
+points of each string. Strings are referred to by integer numbers, so that
+string number |s| comprises the characters |str_pool[j]| for
+|str_start[s] <= j < str_start[s+1]|. Additional integer variables
+|pool_ptr| and |str_ptr| indicate the number of entries used so far
+in |str_pool| and |str_start|, respectively; locations
+|str_pool[pool_ptr]| and |str_start[str_ptr]| are
+ready for the next string to be allocated.
+
+String numbers 0 to 255 are reserved for strings that correspond to single
+ASCII characters. This is in accordance with the conventions of \.{WEB},
+@.WEB@>
+which converts single-character strings into the ASCII code number of the
+single character involved, while it converts other strings into integers
+and builds a string pool file. Thus, when the string constant \.{"."} appears
+in the program below, \.{WEB} converts it into the integer 46, which is the
+ASCII code for a period, while \.{WEB} will convert a string like \.{"hello"}
+into some integer greater than~255. String number 46 will presumably be the
+single character `\..'; but some ASCII codes have no standard visible
+representation, and \TeX\ sometimes needs to be able to print an arbitrary
+ASCII character, so the first 256 strings are used to specify exactly what
+should be printed for each of the 256 possibilities.
+
+Elements of the |str_pool| array must be ASCII codes that can actually
+be printed; i.e., they must have an |xchr| equivalent in the local
+character set. (This restriction applies only to preloaded strings,
+not to those generated dynamically by the user.)
+
+Some \PASCAL\ compilers won't pack integers into a single byte unless the
+integers lie in the range |-128 dotdot 127|. To accommodate such systems
+we access the string pool only via macros that can easily be redefined.
+@^system dependencies@>
+
+@d si(A) A /*convert from |ASCII_code| to |packed_ASCII_code|*/
+@d so(A) A /*convert from |packed_ASCII_code| to |ASCII_code|*/
+
+@<Types...@>=
+typedef int32_t pool_pointer; /*for variables that point into |str_pool|*/
+typedef int32_t str_number; /*for variables that point into |str_start|*/
+typedef uint8_t packed_ASCII_code; /*elements of |str_pool| array*/
+
+@ @<Glob...@>=
+static packed_ASCII_code @!str_pool[pool_size+1]; /*the characters*/
+static pool_pointer @!str_start[max_strings+1]; /*the starting pointers*/
+static pool_pointer @!pool_ptr; /*first unused position in |str_pool|*/
+static str_number @!str_ptr; /*number of the current string being created*/
+static pool_pointer @!init_pool_ptr; /*the starting value of |pool_ptr|*/
+static str_number @!init_str_ptr; /*the starting value of |str_ptr|*/
+
+@ Several of the elementary string operations are performed using \.{WEB}
+macros instead of \PASCAL\ procedures, because many of the
+operations are done quite frequently and we want to avoid the
+overhead of procedure calls. For example, here is
+a simple macro that computes the length of a string.
+@.WEB@>
+
+@d length(A) (str_start[A+1]-str_start[A]) /*the number of characters
+ in string number \#*/
+
+@ The length of the current string is called |cur_length|:
+
+@d cur_length (pool_ptr-str_start[str_ptr])
+
+@ Strings are created by appending character codes to |str_pool|.
+The |append_char| macro, defined here, does not check to see if the
+value of |pool_ptr| has gotten too high; this test is supposed to be
+made before |append_char| is used. There is also a |flush_char|
+macro, which erases the last character appended.
+
+To test if there is room to append |l| more characters to |str_pool|,
+we shall write |str_room(l)|, which aborts \TeX\ and gives an
+apologetic error message if there isn't enough room.
+
+@d append_char(A) /*put |ASCII_code| \# at the end of |str_pool|*/
+{@+str_pool[pool_ptr]=si(A);incr(pool_ptr);
+}
+@d flush_char decr(pool_ptr) /*forget the last character in the pool*/
+@d str_room(A) /*make sure that the pool hasn't overflowed*/
+ {@+if (pool_ptr+A > pool_size)
+ overflow("pool size", pool_size-init_pool_ptr);
+@:TeX capacity exceeded pool size}{\quad pool size@>
+ }
+
+@ Once a sequence of characters has been appended to |str_pool|, it
+officially becomes a string when the function |make_string| is called.
+This function returns the identification number of the new string as its
+value.
+
+@p static str_number make_string(void) /*current string enters the pool*/
+{@+if (str_ptr==max_strings)
+ overflow("number of strings", max_strings-init_str_ptr);
+@:TeX capacity exceeded number of strings}{\quad number of strings@>
+incr(str_ptr);str_start[str_ptr]=pool_ptr;
+return str_ptr-1;
+}
+
+@ To destroy the most recently made string, we say |flush_string|.
+
+@d flush_string {@+decr(str_ptr);pool_ptr=str_start[str_ptr];
+ }
+
+@ The following subroutine compares string |s| with another string of the
+same length that appears in |buffer| starting at position |k|;
+the result is |true| if and only if the strings are equal.
+Empirical tests indicate that |str_eq_buf| is used in such a way that
+it tends to return |true| about 80 percent of the time.
+
+@p static bool str_eq_buf(str_number @!s, int @!k)
+ /*test equality of strings*/
+{@+ /*loop exit*/
+pool_pointer j; /*running index*/
+bool @!result; /*result of comparison*/
+j=str_start[s];
+while (j < str_start[s+1])
+ {@+if (so(str_pool[j])!=buffer[k])
+ {@+result=false;goto not_found;
+ }
+ incr(j);incr(k);
+ }
+result=true;
+not_found: return result;
+}
+
+@ Here is a similar routine, but it compares two strings in the string pool,
+and it does not assume that they have the same length.
+
+@p static bool str_eq_str(str_number @!s, str_number @!t)
+ /*test equality of strings*/
+{@+ /*loop exit*/
+pool_pointer j, @!k; /*running indices*/
+bool @!result; /*result of comparison*/
+result=false;
+if (length(s)!=length(t)) goto not_found;
+j=str_start[s];k=str_start[t];
+while (j < str_start[s+1])
+ {@+if (str_pool[j]!=str_pool[k]) goto not_found;
+ incr(j);incr(k);
+ }
+result=true;
+not_found: return result;
+}
+@t\4@>@<Declare \Prote\ procedures for strings@>@;
+
+@ The initial values of |str_pool|, |str_start|, |pool_ptr|,
+and |str_ptr| are computed by the \.{INITEX} program, based in part
+on the information that \.{WEB} has output while processing \TeX.
+@.INITEX@>
+@^string pool@>
+
+@p
+static bool get_strings_started(void) /*initializes the string pool*/
+{@+
+int k, @!l; /*small indices or counters*/
+pool_ptr=0;str_ptr=0;str_start[0]=0;
+@<Make the first 256 strings@>;
+@<Add the empty string to the string pool@>;
+return true;
+}
+
+@ @d app_lc_hex(A) l=A;
+ if (l < 10) append_char(l+'0')@;@+else append_char(l-10+'a')
+
+@<Make the first 256...@>=
+for (k=0; k<=255; k++)
+ {@+if ((@<Character |k| cannot be printed@>))
+ {@+append_char('^');append_char('^');
+ if (k < 0100) append_char(k+0100)@;
+ else if (k < 0200) append_char(k-0100)@;
+ else{@+app_lc_hex(k/16);app_lc_hex(k%16);
+ }
+ }
+ else append_char(k);
+ make_string();
+ }
+
+@ The first 128 strings will contain 95 standard ASCII characters, and the
+other 33 characters will be printed in three-symbol form like `\.{\^\^A}'
+unless a system-dependent change is made here. Installations that have
+an extended character set, where for example |xchr[032]==@t\.{\'^^Z\'}@>|,
+would like string 032 to be the single character 032 instead of the
+three characters 0136, 0136, 0132 (\.{\^\^Z}). On the other hand,
+even people with an extended character set will want to represent string
+015 by \.{\^\^M}, since 015 is |carriage_return|; the idea is to
+produce visible strings instead of tabs or line-feeds or carriage-returns
+or bell-rings or characters that are treated anomalously in text files.
+
+Unprintable characters of codes 128--255 are, similarly, rendered
+\.{\^\^80}--\.{\^\^ff}.
+
+The boolean expression defined here should be |true| unless \TeX\
+internal code number~|k| corresponds to a non-troublesome visible
+symbol in the local character set. An appropriate formula for the
+extended character set recommended in {\sl The \TeX book\/} would, for
+example, be `|k in[0, 010 dotdot 012, 014, 015, 033, 0177 dotdot 0377]|'.
+If character |k| cannot be printed, and |k < 0200|, then character |k+0100| or
+|k-0100| must be printable; moreover, ASCII codes |[041 dotdot 046,
+060 dotdot 071, 0136, 0141 dotdot 0146, 0160 dotdot 0171]| must be printable.
+Thus, at least 80 printable characters are needed.
+@:TeXbook}{\sl The \TeX book@>
+@^character set dependencies@>
+@^system dependencies@>
+
+@<Character |k| cannot be printed@>=
+ (k < ' ')||(k > '~')
+
+@ The |pool_file| variable is no longer needed and has been removed.
+
+Instead of reading the other strings from the \.{TEX.POOL} file,
+it is sufficient here to add the empty string.
+@<Add the empty string to the string pool@>=
+make_string();
+
+@ Without a string pool file there is no need for a pool check sum either.
+But this is a convenient place to define the function |s_no| that will
+add literal strings to the string pool at runtime, thereby obtaining their
+string number.
+
+@p static int s_no(const char *str)
+{@+
+ if (str[0]==0) return empty_string;
+ if (str[1]==0) return str[0];
+ str_room(strlen(str));
+ while (*str!=0) append_char(*str++);
+ return make_string();
+}
+
+@ The function |s_no| is used in |initialize| and needs a forward
+declaration.
+
+@<Forward declarations@>=
+static int s_no(const char *str);
+
+@* On-line and off-line printing.
+Messages that are sent to a user's terminal and to the transcript-log file
+are produced by several `|print|' procedures. These procedures will
+direct their output to a variety of places, based on the setting of
+the global variable |selector|, which has the following possible
+values:
+
+\yskip
+\hang |term_and_log|, the normal setting, prints on the terminal and on the
+ transcript file.
+
+\hang |log_only|, prints only on the transcript file.
+
+\hang |term_only|, prints only on the terminal.
+
+\hang |no_print|, doesn't print at all. This is used only in rare cases
+ before the transcript file is open.
+
+\hang |pseudo|, puts output into a cyclic buffer that is used
+ by the |show_context| routine; when we get to that routine we shall discuss
+ the reasoning behind this curious mode.
+
+\hang |new_string|, appends the output to the current string in the
+ string pool.
+
+\hang 0 to 15, prints on one of the sixteen files for \.{\\write} output.
+
+\yskip
+\noindent The symbolic names `|term_and_log|', etc., have been assigned
+numeric codes that satisfy the convenient relations |no_print+1==term_only|,
+|no_print+2==log_only|, |term_only+2==log_only+1==term_and_log|.
+
+Three additional global variables, |tally| and |term_offset| and
+|file_offset|, record the number of characters that have been printed
+since they were most recently cleared to zero. We use |tally| to record
+the length of (possibly very long) stretches of printing; |term_offset|
+and |file_offset|, on the other hand, keep track of how many characters
+have appeared so far on the current line that has been output to the
+terminal or to the transcript file, respectively.
+
+@d no_print 16 /*|selector| setting that makes data disappear*/
+@d term_only 17 /*printing is destined for the terminal only*/
+@d log_only 18 /*printing is destined for the transcript file only*/
+@d term_and_log 19 /*normal |selector| setting*/
+@d pseudo 20 /*special |selector| setting for |show_context|*/
+@d new_string 21 /*printing is deflected to the string pool*/
+@d max_selector 21 /*highest selector setting*/
+
+@<Glob...@>=
+static alpha_file @!log_file; /*transcript of \TeX\ session*/
+static int @!selector; /*where to print a message*/
+static int8_t @!dig[23]; /*digits in a number being output*/
+static int @!tally; /*the number of characters recently printed*/
+static int @!term_offset;
+ /*the number of characters on the current terminal line*/
+static int @!file_offset;
+ /*the number of characters on the current file line*/
+static ASCII_code @!trick_buf[error_line+1]; /*circular buffer for
+ pseudoprinting*/
+static int @!trick_count; /*threshold for pseudoprinting, explained later*/
+static int @!first_count; /*another variable for pseudoprinting*/
+
+@ @<Initialize the output routines@>=
+selector=term_only;tally=0;term_offset=0;file_offset=0;
+
+@ Macro abbreviations for output to the terminal and to the log file are
+defined here for convenience. Some systems need special conventions
+for terminal output, and it is possible to adhere to those conventions
+by changing |wterm|, |wterm_ln|, and |wterm_cr| in this section.
+@^system dependencies@>
+
+@<Basic printing procedures@>=
+#define @[put(F)@] @[fwrite(&((F).d)@],@[sizeof((F).d),1,(F).f)@]@;
+#define @[get(F)@] @[(void)!fread(&((F).d),sizeof((F).d),1,(F).f)@]
+
+#define @[pascal_close(F)@] @[fclose((F).f)@]
+#define @[eof(F)@] @[feof((F).f)@]
+#define @[eoln(F)@] @[((F).d=='\n'||eof(F))@]
+#define @[erstat(F)@] @[((F).f==NULL?-1:ferror((F).f))@]
+
+#define @[pascal_read(F,X)@] @[((X)=(F).d,get(F))@]
+#define @[read_ln(F)@] do get(F); while (!eoln(F))
+
+#define @[pascal_write(F, FMT,...)@] @[fprintf(F.f,FMT,## __VA_ARGS__)@]
+#define @[write_ln(F,...)@] @[pascal_write(F,__VA_ARGS__"\n")@]
+
+#define @[wterm(FMT,...)@] @[pascal_write(term_out,FMT, ## __VA_ARGS__)@]
+#define @[wterm_ln(FMT,...)@] @[wterm(FMT "\n", ## __VA_ARGS__)@]
+#define wterm_cr @[pascal_write(term_out,"\n")@]
+#define @[wlog(FMT, ...)@] @[pascal_write(log_file,FMT, ## __VA_ARGS__)@]
+#define @[wlog_ln(FMT, ...)@] @[wlog(FMT "\n", ## __VA_ARGS__)@]
+#define wlog_cr @[pascal_write(log_file,"\n")@]
+
+@ To end a line of text output, we call |print_ln|.
+
+@<Basic print...@>=
+static void print_ln(void) /*prints an end-of-line*/
+{@+switch (selector) {
+case term_and_log: {@+wterm_cr;wlog_cr;
+ term_offset=0;file_offset=0;
+ } @+break;
+case log_only: {@+wlog_cr;file_offset=0;
+ } @+break;
+case term_only: {@+wterm_cr;term_offset=0;
+ } @+break;
+case no_print: case pseudo: case new_string: do_nothing;@+break;
+default:write_ln(write_file[selector]);
+} @/
+} /*|tally| is not affected*/
+
+@ The |print_char| procedure sends one character to the desired destination,
+using the |xchr| array to map it into an external character compatible with
+|input_ln|. All printing comes through |print_ln| or |print_char|.
+
+@<Basic printing...@>=
+static void print_char(ASCII_code @!s) /*prints a single character*/
+{@+
+if (@<Character |s| is the current new-line character@>)
+ if (selector < pseudo)
+ {@+print_ln();return;
+ }
+switch (selector) {
+case term_and_log: {@+wterm("%c",xchr[s]);wlog("%c",xchr[s]);
+ incr(term_offset);incr(file_offset);
+ if (term_offset==max_print_line)
+ {@+wterm_cr;term_offset=0;
+ }
+ if (file_offset==max_print_line)
+ {@+wlog_cr;file_offset=0;
+ }
+ } @+break;
+case log_only: {@+wlog("%c",xchr[s]);incr(file_offset);
+ if (file_offset==max_print_line) print_ln();
+ } @+break;
+case term_only: {@+wterm("%c",xchr[s]);incr(term_offset);
+ if (term_offset==max_print_line) print_ln();
+ } @+break;
+case no_print: do_nothing;@+break;
+case pseudo: if (tally < trick_count) trick_buf[tally%error_line]=s;@+break;
+case new_string: {@+if (pool_ptr < pool_size) append_char(s);
+ } @+break; /*we drop characters if the string space is full*/
+default:pascal_write(write_file[selector],"%c", xchr[s]);
+} @/
+incr(tally);
+}
+
+@ An entire string is output by calling |print|. Note that if we are outputting
+the single standard ASCII character \.c, we could call |print('c')|, since
+|'c'==99| is the number of a single-character string, as explained above. But
+|print_char('c')| is quicker, so \TeX\ goes directly to the |print_char|
+routine when it knows that this is safe. (The present implementation
+assumes that it is always safe to print a visible ASCII character.)
+@^system dependencies@>
+
+@<Basic print...@>=
+static void print(char *s) /* the simple version */
+{ @+if (s == NULL) s="???"; /*this can't happen*/
+ while (*s!=0) print_char(*s++);@+
+}
+
+static void printn(int @!s) /*prints string |s|*/
+{@+
+pool_pointer j; /*current character code position*/
+int @!nl; /*new-line character to restore*/
+if (s >= str_ptr) {print("???"); return;}/*this can't happen*/
+@.???@>
+else if (s < 256)
+ if (s < 0) { print("???");return; } /*can't happen*/
+ else{@+if (selector > pseudo)
+ {@+print_char(s);return; /*internal strings are not expanded*/
+ }
+ if ((@<Character |s| is the current new-line character@>))
+ if (selector < pseudo)
+ {@+print_ln();return;
+ }
+ nl=new_line_char;new_line_char=-1;
+ /*temporarily disable new-line character*/
+ j=str_start[s];
+ while (j < str_start[s+1])
+ {@+print_char(so(str_pool[j]));incr(j);
+ }
+ new_line_char=nl;return;
+ }
+j=str_start[s];
+while (j < str_start[s+1])
+ {@+print_char(so(str_pool[j]));incr(j);
+ }
+}
+
+@ Control sequence names, file names, and strings constructed with
+\.{\\string} might contain |ASCII_code| values that can't
+be printed using |print_char|. Therefore we use |slow_print| for them:
+
+@<Basic print...@>=
+static void slow_print(int @!s) /*prints string |s|*/
+{@+pool_pointer j; /*current character code position*/
+if ((s >= str_ptr)||(s < 256)) printn(s);
+else{@+j=str_start[s];
+ while (j < str_start[s+1])
+ {@+printn(so(str_pool[j]));incr(j);
+ }
+ }
+}
+
+@ Here is the very first thing that \TeX\ prints: a headline that identifies
+the version number and format package. The |term_offset| variable is temporarily
+incorrect, but the discrepancy is not serious since we assume that this
+part of the program is system dependent.
+@^system dependencies@>
+
+According to the conventions of \TeX\ Live,
+ we print the |dump_name| if no format identifier is known.
+@<Initialize the output...@>=
+wterm("%s",banner);
+if (format_ident==0) wterm_ln(" (preloaded format=%s)", dump_name);
+else{@+slow_print(format_ident);print_ln();
+ }
+update_terminal;
+
+@ The procedure |print_nl| is like |print|, but it makes sure that the
+string appears at the beginning of a new line.
+
+@<Basic print...@>=
+static void print_nl(char *@!s) /*prints string |s| at beginning of line*/
+{@+if (((term_offset > 0)&&(odd(selector)))||@|
+ ((file_offset > 0)&&(selector >= log_only))) print_ln();
+print(s);
+}
+
+@ The procedure |print_esc| prints a string that is preceded by
+the user's escape character (which is usually a backslash).
+
+@<Basic print...@>=
+static void printn_esc(str_number @!s) /*prints escape character, then |s|*/
+{@+int c; /*the escape character code*/
+@<Set variable |c| to the current escape character@>;
+if (c >= 0) if (c < 256) printn(c);
+slow_print(s);
+}
+
+static void print_esc(char *@!s) /*the fast way*/
+{@+int c; /*the escape character code*/
+@<Set variable |c| to the current escape character@>;
+if (c >= 0) if (c < 256) printn(c);
+print(s);
+}
+
+@ An array of digits in the range |0 dotdot 15| is printed by |print_the_digs|.
+
+@<Basic print...@>=
+static void print_the_digs(eight_bits @!k)
+ /*prints |dig[k-1]|$\,\ldots\,$|dig[0]|*/
+{@+while (k > 0)
+ {@+decr(k);
+ if (dig[k] < 10) print_char('0'+dig[k]);
+ else print_char('A'-10+dig[k]);
+ }
+}
+
+@ The following procedure, which prints out the decimal representation of a
+given integer |n|, has been written carefully so that it works properly
+if |n==0| or if |(-n)| would cause overflow. It does not apply |%| or |/|
+to negative arguments, since such operations are not implemented consistently
+by all \PASCAL\ compilers.
+
+@<Basic print...@>=
+static void print_int(int @!n) /*prints an integer in decimal form*/
+{@+int k; /*index to current digit; we assume that $\vert n\vert<10^{23}$*/
+int @!m; /*used to negate |n| in possibly dangerous cases*/
+k=0;
+if (n < 0)
+ {@+print_char('-');
+ if (n > -100000000) negate(n);
+ else{@+m=-1-n;n=m/10;m=(m%10)+1;k=1;
+ if (m < 10) dig[0]=m;
+ else{@+dig[0]=0;incr(n);
+ }
+ }
+ }
+@/do@+{dig[k]=n%10;n=n/10;incr(k);
+}@+ while (!(n==0));
+print_the_digs(k);
+}
+
+@ Here is a trivial procedure to print two digits; it is usually called with
+a parameter in the range |0 <= n <= 99|.
+
+@p static void print_two(int @!n) /*prints two least significant digits*/
+{@+n=abs(n)%100;print_char('0'+(n/10));
+print_char('0'+(n%10));
+}
+
+@ Hexadecimal printing of nonnegative integers is accomplished by |print_hex|.
+
+@p static void print_hex(int @!n)
+ /*prints a positive integer in hexadecimal form*/
+{@+int k; /*index to current digit; we assume that $0\le n<16^{22}$*/
+k=0;print_char('"');
+@/do@+{dig[k]=n%16;n=n/16;incr(k);
+}@+ while (!(n==0));
+print_the_digs(k);
+}
+
+@ Old versions of \TeX\ needed a procedure called |print_ASCII| whose function
+is now subsumed by |print|. We retain the old name here as a possible aid to
+future software arch\ae ologists.
+
+@d print_ASCII printn
+
+@ Roman numerals are produced by the |print_roman_int| routine. Readers
+who like puzzles might enjoy trying to figure out how this tricky code
+works; therefore no explanation will be given. Notice that 1990 yields
+\.{mcmxc}, not \.{mxm}.
+
+@p static void print_roman_int(int @!n)
+{@+
+pool_pointer j, @!k; /*mysterious indices into |mystery|*/
+nonnegative_integer @!u, @!v; /*mysterious numbers*/
+const char mystery[] ="m2d5c2l5x2v5i";
+j=0;v=1000;
+loop@+{@+while (n >= v)
+ {@+print_char(so(mystery[j]));n=n-v;
+ }
+ if (n <= 0) return; /*nonpositive input produces no output*/
+ k=j+2;u=v/(so(mystery[k-1])-'0');
+ if (mystery[k-1]==si('2'))
+ {@+k=k+2;u=u/(so(mystery[k-1])-'0');
+ }
+ if (n+u >= v)
+ {@+print_char(so(mystery[k]));n=n+u;
+ }
+ else{@+j=j+2;v=v/(so(mystery[j-1])-'0');
+ }
+ }
+}
+
+@ The |print| subroutine will not print a string that is still being
+created. The following procedure will.
+
+@p static void print_current_string(void) /*prints a yet-unmade string*/
+{@+pool_pointer j; /*points to current character code*/
+j=str_start[str_ptr];
+while (j < pool_ptr)
+ {@+print_char(so(str_pool[j]));incr(j);
+ }
+}
+
+@ Here is a procedure that asks the user to type a line of input,
+assuming that the |selector| setting is either |term_only| or |term_and_log|.
+The input is placed into locations |first| through |last-1| of the
+|buffer| array, and echoed on the transcript file if appropriate.
+
+This procedure is never called when |interaction < scroll_mode|.
+
+@d prompt_input(A) {@+wake_up_terminal;print(A);term_input();
+ } /*prints a string and gets a line of input*/
+
+@p static void term_input(void) /*gets a line from the terminal*/
+{@+int k; /*index into |buffer|*/
+update_terminal; /*now the user sees the prompt for sure*/
+if (!input_ln(&term_in, true)) fatal_error("End of file on the terminal!");
+@.End of file on the terminal@>
+term_offset=0; /*the user's line ended with \<\rm return>*/
+decr(selector); /*prepare to echo the input*/
+if (last!=first) for (k=first; k<=last-1; k++) printn(buffer[k]);
+print_ln();incr(selector); /*restore previous status*/
+}
+
+@* Reporting errors.
+When something anomalous is detected, \TeX\ typically does something like this:
+$$\vbox{\halign{#\hfil\cr
+|print_err("Something anomalous has been detected");|\cr
+|help3("This is the first line of my offer to help.")|\cr
+|("This is the second line. I'm trying to")|\cr
+|("explain the best way for you to proceed.");|\cr
+|error;|\cr}}$$
+A two-line help message would be given using |help2|, etc.; these informal
+helps should use simple vocabulary that complements the words used in the
+official error message that was printed. (Outside the U.S.A., the help
+messages should preferably be translated into the local vernacular. Each
+line of help is at most 60 characters long, in the present implementation,
+so that |max_print_line| will not be exceeded.)
+
+The |print_err| procedure supplies a `\.!' before the official message,
+and makes sure that the terminal is awake if a stop is going to occur.
+The |error| procedure supplies a `\..' after the official message, then it
+shows the location of the error; and if |interaction==error_stop_mode|,
+it also enters into a dialog with the user, during which time the help
+message may be printed.
+@^system dependencies@>
+
+@<Error handling...@>=
+static void print_err(char *s)
+{@+if (interaction==error_stop_mode) wake_up_terminal;
+ if (filelineerrorstylep) print_file_line(); /* \TeX\ Live */
+ else print_nl("! ");
+ print(s);
+}
+
+@ The global variable |interaction| has four settings, representing increasing
+amounts of user interaction:
+
+@d batch_mode 0 /*omits all stops and omits terminal output*/
+@d nonstop_mode 1 /*omits all stops*/
+@d scroll_mode 2 /*omits error stops*/
+@d error_stop_mode 3 /*stops at every opportunity to interact*/
+
+@<Glob...@>=
+static int @!interaction; /*current level of interaction*/
+
+@ @<Set init...@>=
+if (interaction_option<0) interaction=error_stop_mode;
+else interaction=interaction_option; /* \TeX\ Live */
+
+@ \TeX\ is careful not to call |error| when the print |selector| setting
+might be unusual. The only possible values of |selector| at the time of
+error messages are
+
+\yskip\hang|no_print| (when |interaction==batch_mode|
+ and |log_file| not yet open);
+
+\hang|term_only| (when |interaction > batch_mode| and |log_file| not yet open);
+
+\hang|log_only| (when |interaction==batch_mode| and |log_file| is open);
+
+\hang|term_and_log| (when |interaction > batch_mode| and |log_file| is open).
+
+@<Initialize the print |selector| based on |interaction|@>=
+if (interaction==batch_mode) selector=no_print;@+else selector=term_only
+
+@ A global variable |deletions_allowed| is set |false| if the |get_next|
+routine is active when |error| is called; this ensures that |get_next|
+and related routines like |get_token| will never be called recursively.
+A similar interlock is provided by |set_box_allowed|.
+@^recursion@>
+
+The global variable |history| records the worst level of error that
+has been detected. It has four possible values: |spotless|, |warning_issued|,
+|error_message_issued|, and |fatal_error_stop|.
+
+Another global variable, |error_count|, is increased by one when an
+|error| occurs without an interactive dialog, and it is reset to zero at
+the end of every paragraph. If |error_count| reaches 100, \TeX\ decides
+that there is no point in continuing further.
+
+@d spotless 0 /*|history| value when nothing has been amiss yet*/
+@d warning_issued 1 /*|history| value when |begin_diagnostic| has been called*/
+@d error_message_issued 2 /*|history| value when |error| has been called*/
+@d fatal_error_stop 3 /*|history| value when termination was premature*/
+
+@<Glob...@>=
+static bool @!deletions_allowed; /*is it safe for |error| to call |get_token|?*/
+static bool @!set_box_allowed; /*is it safe to do a \.{\\setbox} assignment?*/
+static int @!history; /*has the source input been clean so far?*/
+static int @!error_count; /*the number of scrolled errors since the
+ last paragraph ended*/
+
+@ The value of |history| is initially |fatal_error_stop|, but it will
+be changed to |spotless| if \TeX\ survives the initialization process.
+
+@<Set init...@>=
+deletions_allowed=true;set_box_allowed=true;
+error_count=0; /*|history| is initialized elsewhere*/
+
+@ Since errors can be detected almost anywhere in \TeX, we want to declare the
+error procedures near the beginning of the program. But the error procedures
+in turn use some other procedures, which need to be declared |forward|
+before we get to |error| itself.
+
+It is possible for |error| to be called recursively if some error arises
+when |get_token| is being used to delete a token, and/or if some fatal error
+occurs while \TeX\ is trying to fix a non-fatal one. But such recursion
+@^recursion@>
+is never more than two levels deep.
+
+@<Error handling...@>=
+static void normalize_selector(void);@/
+static void get_token(void);@/
+static void term_input(void);@/
+static void show_context(void);@/
+static void begin_file_reading(void);@/
+static void open_log_file(void);@/
+static void close_files_and_terminate(void);@/
+static void clear_for_error_prompt(void);@/
+static void give_err_help(void);@/
+#ifdef @!DEBUG
+static void debug_help(void);
+#else
+#define debug_help() do_nothing
+#endif
+
+@ Individual lines of help are recorded in the array |help_line|, which
+contains entries in positions |0 dotdot(help_ptr-1)|. They should be printed
+in reverse order, i.e., with |help_line[0]| appearing last.
+
+@d hlp1(A) help_line[0]=A;@+}
+@d hlp2(A, B) help_line[1]=A;help_line[0]=B;@+}
+@d hlp3(A, B, C) help_line[2]=A;help_line[1]=B;help_line[0]=C;@+}
+@d hlp4(A, B, C, D) help_line[3]=A;help_line[2]=B;help_line[1]=C;help_line[0]=D;@+}
+@d hlp5(A, B, C, D, E) help_line[4]=A;help_line[3]=B;help_line[2]=C;help_line[1]=D;help_line[0]=E;@+}
+@d hlp6(A, B, C, D, E, F) help_line[5]=A;help_line[4]=B;help_line[3]=C;help_line[2]=D;help_line[1]=E;help_line[0]=F;@+}
+@d help0 help_ptr=0 /*sometimes there might be no help*/
+@d help1(A) @+{@+help_ptr=1;hlp1(A) /*use this with one help line*/
+@d help2(A, B) @+{@+help_ptr=2;hlp2(A, B) /*use this with two help lines*/
+@d help3(A, B, C) @+{@+help_ptr=3;hlp3(A, B, C) /*use this with three help lines*/
+@d help4(A, B, C, D) @+{@+help_ptr=4;hlp4(A, B, C, D) /*use this with four help lines*/
+@d help5(A, B, C, D, E) @+{@+help_ptr=5;hlp5(A, B, C, D, E) /*use this with five help lines*/
+@d help6(A, B, C, D, E, F) @+{@+help_ptr=6;hlp6(A, B, C, D, E, F) /*use this with six help lines*/
+
+@<Glob...@>=
+static char *@!help_line[6]; /*helps for the next |error|*/
+static int @!help_ptr; /*the number of help lines present*/
+static bool @!use_err_help; /*should the |err_help| list be shown?*/
+
+@ @<Set init...@>=
+help_ptr=0;use_err_help=false;
+
+@ The |jump_out| procedure just cuts across all active procedure levels and
+goes to |end_of_TEX|. This is the only nontrivial |@!goto| statement in the
+whole program. It is used when there is no recovery from a particular error.
+
+Some \PASCAL\ compilers do not implement non-local |goto| statements.
+@^system dependencies@>
+In such cases the body of |jump_out| should simply be
+`|close_files_and_terminate|;\thinspace' followed by a call on some system
+procedure that quietly terminates the program.
+
+@<Error hand...@>=
+static void jump_out(void)
+{@+ close_files_and_terminate(); exit(0);
+}
+
+@ Here now is the general |error| routine.
+
+@<Error hand...@>=
+static void error(void) /*completes the job of error reporting*/
+{@+
+ASCII_code c; /*what the user types*/
+int @!s1, @!s2, @!s3, @!s4;
+ /*used to save global variables when deleting tokens*/
+if (history < error_message_issued) history=error_message_issued;
+print_char('.');show_context();
+if (interaction==error_stop_mode)
+ @<Get user's advice and |return|@>;
+incr(error_count);
+if (error_count==100)
+ {@+print_nl("(That makes 100 errors; please try again.)");
+@.That makes 100 errors...@>
+ history=fatal_error_stop;jump_out();
+ }
+@<Put help message on the transcript file@>;
+}
+
+@ @<Get user's advice...@>=
+loop@+{@+resume: if (interaction!=error_stop_mode) return;
+ clear_for_error_prompt();prompt_input("? ");
+@.?\relax@>
+ if (last==first) return;
+ c=buffer[first];
+ if (c >= 'a') c=c+'A'-'a'; /*convert to uppercase*/
+ @<Interpret code |c| and |return| if done@>;
+ }
+
+@ It is desirable to provide an `\.E' option here that gives the user
+an easy way to return from \TeX\ to the system editor, with the offending
+line ready to be edited. But such an extension requires some system
+wizardry, so the present implementation simply types out the name of the
+file that should be
+edited and the relevant line number.
+@^system dependencies@>
+
+There is a secret `\.D' option available when the debugging routines haven't
+been commented~out.
+@^debugging@>
+
+@<Interpret code |c| and |return| if done@>=
+switch (c) {
+case '0': case '1': case '2': case '3':
+ case '4': case '5': case '6': case '7':
+ case '8': case '9': if (deletions_allowed)
+ @<Delete \(c)|c-"0"| tokens and |goto resume|@>@;@+break;
+@t\4\4@>@;
+#ifdef @!DEBUG
+case 'D': {@+debug_help();goto resume;@+}
+#endif
+case 'E': if (base_ptr > 0) if (input_stack[base_ptr].name_field >= 256)
+ {@+print_nl("You want to edit file ");
+@.You want to edit file x@>
+ slow_print(input_stack[base_ptr].name_field);
+ print(" at line ");print_int(line);
+ interaction=scroll_mode;jump_out();
+ } @+break;
+case 'H': @<Print the help information and |goto resume|@>@;
+case 'I': @<Introduce new material from the terminal and |return|@>@;
+case 'Q': case 'R': case 'S': @<Change the interaction level and |return|@>@;
+case 'X': {@+interaction=scroll_mode;jump_out();
+ } @+break;
+default:do_nothing;
+} @/
+@<Print the menu of available options@>@;
+
+@ @<Print the menu...@>=
+{@+print("Type <return> to proceed, S to scroll future error messages,");@/
+@.Type <return> to proceed...@>
+print_nl("R to run without stopping, Q to run quietly,");@/
+print_nl("I to insert something, ");
+if (base_ptr > 0) if (input_stack[base_ptr].name_field >= 256)
+ print("E to edit your file,");
+if (deletions_allowed)
+ print_nl("1 or ... or 9 to ignore the next 1 to 9 tokens of input,");
+print_nl("H for help, X to quit.");
+}
+
+@ Here the author of \TeX\ apologizes for making use of the numerical
+relation between |'Q'|, |'R'|, |'S'|, and the desired interaction settings
+|batch_mode|, |nonstop_mode|, |scroll_mode|.
+@^Knuth, Donald Ervin@>
+
+@<Change the interaction...@>=
+{@+error_count=0;interaction=batch_mode+c-'Q';
+print("OK, entering ");
+switch (c) {
+case 'Q': {@+print_esc("batchmode");decr(selector);
+ } @+break;
+case 'R': print_esc("nonstopmode");@+break;
+case 'S': print_esc("scrollmode");
+} /*there are no other cases*/
+print("...");print_ln();update_terminal;return;
+}
+
+@ When the following code is executed, |buffer[(first+1)dotdot(last-1)]| may
+contain the material inserted by the user; otherwise another prompt will
+be given. In order to understand this part of the program fully, you need
+to be familiar with \TeX's input stacks.
+
+@<Introduce new material...@>=
+{@+begin_file_reading(); /*enter a new syntactic level for terminal input*/
+ /*now |state==mid_line|, so an initial blank space will count as a blank*/
+if (last > first+1)
+ {@+loc=first+1;buffer[first]=' ';
+ }
+else{@+prompt_input("insert>");loc=first;
+@.insert>@>
+ }
+first=last;
+cur_input.limit_field=last-1; /*no |end_line_char| ends this line*/
+return;
+}
+
+@ We allow deletion of up to 99 tokens at a time.
+
+@<Delete \(c)|c-"0"| tokens...@>=
+{@+s1=cur_tok;s2=cur_cmd;s3=cur_chr;s4=align_state;
+align_state=1000000;OK_to_interrupt=false;
+if ((last > first+1)&&(buffer[first+1] >= '0')&&(buffer[first+1] <= '9'))
+ c=c*10+buffer[first+1]-'0'*11;
+else c=c-'0';
+while (c > 0)
+ {@+get_token(); /*one-level recursive call of |error| is possible*/
+ decr(c);
+ }
+cur_tok=s1;cur_cmd=s2;cur_chr=s3;align_state=s4;OK_to_interrupt=true;
+help2("I have just deleted some text, as you asked.",@/
+"You can now delete more, or insert, or whatever.");
+show_context();goto resume;
+}
+
+@ @<Print the help info...@>=
+{@+if (use_err_help)
+ {@+give_err_help();use_err_help=false;
+ }
+else{@+if (help_ptr==0)
+ help2("Sorry, I don't know how to help in this situation.",@/
+ @t\kern1em@>"Maybe you should try asking a human?");
+ @/do@+{decr(help_ptr);print(help_line[help_ptr]);print_ln();
+ }@+ while (!(help_ptr==0));
+ }
+help4("Sorry, I already gave what help I could...",@/
+ "Maybe you should try asking a human?",@/
+ "An error might have occurred before I noticed any problems.",@/
+ "``If all else fails, read the instructions.'");@/
+goto resume;
+}
+
+@ @<Put help message on the transcript file@>=
+if (interaction > batch_mode) decr(selector); /*avoid terminal output*/
+if (use_err_help)
+ {@+print_ln();give_err_help();
+ }
+else while (help_ptr > 0)
+ {@+decr(help_ptr);print_nl(help_line[help_ptr]);
+ }
+print_ln();
+if (interaction > batch_mode) incr(selector); /*re-enable terminal output*/
+print_ln()
+
+@ A dozen or so error messages end with a parenthesized integer, so we
+save a teeny bit of program space by declaring the following procedure:
+
+@p static void int_error(int @!n)
+{@+print(" (");print_int(n);print_char(')');error();
+}
+
+@ In anomalous cases, the print selector might be in an unknown state;
+the following subroutine is called to fix things just enough to keep
+running a bit longer.
+
+@p static void normalize_selector(void)
+{@+if (log_opened) selector=term_and_log;
+else selector=term_only;
+if (job_name==0) open_log_file();
+if (interaction==batch_mode) decr(selector);
+}
+
+@ The following procedure prints \TeX's last words before dying.
+
+@d succumb {@+if (interaction==error_stop_mode)
+ interaction=scroll_mode; /*no more interaction*/
+ if (log_opened) error();
+ if (interaction > batch_mode) debug_help();
+ history=fatal_error_stop;jump_out(); /*irrecoverable error*/
+ }
+
+@<Error hand...@>=
+static void fatal_error(char *@!s) /*prints |s|, and that's it*/
+{@+normalize_selector();@/
+print_err("Emergency stop");help1(s);succumb;
+@.Emergency stop@>
+}
+
+@ Here is the most dreaded error message.
+
+@<Error hand...@>=
+static void overflow(char *@!s, int @!n) /*stop due to finiteness*/
+{@+normalize_selector();
+print_err("TeX capacity exceeded, sorry [");
+@.TeX capacity exceeded ...@>
+print(s);print_char('=');print_int(n);print_char(']');
+help2("If you really absolutely need more capacity,",@/
+ "you can ask a wizard to enlarge me.");
+succumb;
+}
+
+@ The program might sometime run completely amok, at which point there is
+no choice but to stop. If no previous error has been detected, that's bad
+news; a message is printed that is really intended for the \TeX\
+maintenance person instead of the user (unless the user has been
+particularly diabolical). The index entries for `this can't happen' may
+help to pinpoint the problem.
+@^dry rot@>
+
+@<Error hand...@>=
+static void confusion(char *@!s)
+ /*consistency check violated; |s| tells where*/
+{@+normalize_selector();
+if (history < error_message_issued)
+ {@+print_err("This can't happen (");print(s);print_char(')');
+@.This can't happen@>
+ help1("I'm broken. Please show this to someone who can fix can fix");
+ }
+else{@+print_err("I can't go on meeting you like this");
+@.I can't go on...@>
+ help2("One of your faux pas seems to have wounded me deeply...",@/
+ "in fact, I'm barely conscious. Please fix it and try again.");
+ }
+succumb;
+}
+
+@ Users occasionally want to interrupt \TeX\ while it's running.
+If the \PASCAL\ runtime system allows this, one can implement
+a routine that sets the global variable |interrupt| to some nonzero value
+when such an interrupt is signalled. Otherwise there is probably at least
+a way to make |interrupt| nonzero using the \PASCAL\ debugger.
+@^system dependencies@>
+@^debugging@>
+
+@d check_interrupt {@+if (interrupt!=0) pause_for_instructions();
+ }
+
+@<Global...@>=
+static int @!interrupt; /*should \TeX\ pause for instructions?*/
+static bool @!OK_to_interrupt; /*should interrupts be observed?*/
+
+@ @<Set init...@>=
+interrupt=0;OK_to_interrupt=true;
+
+@ When an interrupt has been detected, the program goes into its
+highest interaction level and lets the user have nearly the full flexibility of
+the |error| routine. \TeX\ checks for interrupts only at times when it is
+safe to do this.
+
+@p static void pause_for_instructions(void)
+{@+if (OK_to_interrupt)
+ {@+interaction=error_stop_mode;
+ if ((selector==log_only)||(selector==no_print))
+ incr(selector);
+ print_err("Interruption");
+@.Interruption@>
+ help3("You rang?",@/
+ "Try to insert an instruction for me (e.g., `I\\showlists'),",@/
+ "unless you just want to quit by typing `X'.");
+ deletions_allowed=false;error();deletions_allowed=true;
+ interrupt=0;
+ }
+}
+
+@* Arithmetic with scaled dimensions.
+The principal computations performed by \TeX\ are done entirely in terms of
+integers less than $2^{31}$ in magnitude; and divisions are done only when both
+dividend and divisor are nonnegative. Thus, the arithmetic specified in this
+program can be carried out in exactly the same way on a wide variety of
+computers, including some small ones. Why? Because the arithmetic
+calculations need to be spelled out precisely in order to guarantee that
+\TeX\ will produce identical output on different machines. If some
+quantities were rounded differently in different implementations, we would
+find that line breaks and even page breaks might occur in different places.
+Hence the arithmetic of \TeX\ has been designed with care, and systems that
+claim to be implementations of \TeX82 should follow precisely the
+@:TeX82}{\TeX82@>
+calculations as they appear in the present program.
+
+(Actually there are three places where \TeX\ uses |/| with a possibly negative
+numerator. These are harmless; see |/| in the index. Also if the user
+sets the \.{\\time} or the \.{\\year} to a negative value, some diagnostic
+information will involve negative-numerator division. The same remarks
+apply for |%| as well as for |/|.)
+
+@ Here is a routine that calculates half of an integer, using an
+unambiguous convention with respect to signed odd numbers.
+
+@p static int half(int @!x)
+{@+if (odd(x)) return(x+1)/2;
+else return x/2;
+}
+
+@ Fixed-point arithmetic is done on {\sl scaled integers\/} that are multiples
+of $2^{-16}$. In other words, a binary point is assumed to be sixteen bit
+positions from the right end of a binary computer word.
+
+@d unity 0200000 /*$2^{16}$, represents 1.00000*/
+@d two 0400000 /*$2^{17}$, represents 2.00000*/
+
+@<Types...@>=
+typedef int scaled; /*this type is used for scaled integers*/
+typedef int32_t nonnegative_integer; /*$0\le x<2^{31}$*/
+typedef int8_t small_number; /*this type is self-explanatory*/
+
+@ The following function is used to create a scaled integer from a given decimal
+fraction $(.d_0d_1\ldots d_{k-1})$, where |0 <= k <= 17|. The digit $d_i$ is
+given in |dig[i]|, and the calculation produces a correctly rounded result.
+
+@p static scaled round_decimals(small_number @!k)
+ /*converts a decimal fraction*/
+{@+int a; /*the accumulator*/
+a=0;
+while (k > 0)
+ {@+decr(k);a=(a+dig[k]*two)/10;
+ }
+return(a+1)/2;
+}
+
+@ Conversely, here is a procedure analogous to |print_int|. If the output
+of this procedure is subsequently read by \TeX\ and converted by the
+|round_decimals| routine above, it turns out that the original value will
+be reproduced exactly; the ``simplest'' such decimal number is output,
+but there is always at least one digit following the decimal point.
+
+The invariant relation in the \&{repeat} loop is that a sequence of
+decimal digits yet to be printed will yield the original number if and only if
+they form a fraction~$f$ in the range $s-\delta\le 10\cdot2^{16}f<s$.
+We can stop if and only if $f=0$ satisfies this condition; the loop will
+terminate before $s$ can possibly become zero.
+
+@p static void print_scaled(scaled @!s) /*prints scaled real, rounded to five
+ digits*/
+{@+scaled delta; /*amount of allowable inaccuracy*/
+if (s < 0)
+ {@+print_char('-');negate(s); /*print the sign, if negative*/
+ }
+print_int(s/unity); /*print the integer part*/
+print_char('.');
+s=10*(s%unity)+5;delta=10;
+@/do@+{if (delta > unity) s=s+0100000-50000; /*round the last digit*/
+print_char('0'+(s/unity));s=10*(s%unity);delta=delta*10;
+}@+ while (!(s <= delta));
+}
+
+@ Physical sizes that a \TeX\ user specifies for portions of documents are
+represented internally as scaled points. Thus, if we define an `sp' (scaled
+@^sp@>
+point) as a unit equal to $2^{-16}$ printer's points, every dimension
+inside of \TeX\ is an integer number of sp. There are exactly
+4,736,286.72 sp per inch. Users are not allowed to specify dimensions
+larger than $2^{30}-1$ sp, which is a distance of about 18.892 feet (5.7583
+meters); two such quantities can be added without overflow on a 32-bit
+computer.
+
+The present implementation of \TeX\ does not check for overflow when
+@^overflow in arithmetic@>
+dimensions are added or subtracted. This could be done by inserting a
+few dozen tests of the form `\ignorespaces|if (x >= 010000000000)|
+\\{report\_overflow}', but the chance of overflow is so remote that
+such tests do not seem worthwhile.
+
+\TeX\ needs to do only a few arithmetic operations on scaled quantities,
+other than addition and subtraction, and the following subroutines do most of
+the work. A single computation might use several subroutine calls, and it is
+desirable to avoid producing multiple error messages in case of arithmetic
+overflow; so the routines set the global variable |arith_error| to |true|
+instead of reporting errors directly to the user. Another global variable,
+|rem|, holds the remainder after a division.
+
+@<Glob...@>=
+static bool @!arith_error; /*has arithmetic overflow occurred recently?*/
+static scaled @!rem; /*amount subtracted to get an exact division*/
+
+@ The first arithmetical subroutine we need computes $nx+y$, where |x|
+and~|y| are |scaled| and |n| is an integer. We will also use it to
+multiply integers.
+
+@d nx_plus_y(A, B, C) mult_and_add(A, B, C, 07777777777)
+@d mult_integers(A, B) mult_and_add(A, B, 0, 017777777777)
+
+@p static scaled mult_and_add(int @!n, scaled @!x, scaled @!y, scaled @!max_answer)
+{@+if (n < 0)
+ {@+negate(x);negate(n);
+ }
+if (n==0) return y;
+else if (((x <= (max_answer-y)/n)&&(-x <= (max_answer+y)/n)))
+ return n*x+y;
+else{@+arith_error=true;return 0;
+ }
+}
+
+@ We also need to divide scaled dimensions by integers.
+
+@p static scaled x_over_n(scaled @!x, int @!n)
+{@+bool negative; /*should |rem| be negated?*/
+scaled x_over_n;
+negative=false;
+if (n==0)
+ {@+arith_error=true;x_over_n=0;rem=x;
+ }
+else{@+if (n < 0)
+ {@+negate(x);negate(n);negative=true;
+ }
+ if (x >= 0)
+ {@+x_over_n=x/n;rem=x%n;
+ }
+ else{@+x_over_n=-((-x)/n);rem=-((-x)%n);
+ }
+ }
+if (negative) negate(rem);
+return x_over_n;}
+
+@ Then comes the multiplication of a scaled number by a fraction |n/(double)d|,
+where |n| and |d| are nonnegative integers | <= @t$2^{16}$@>| and |d| is
+positive. It would be too dangerous to multiply by~|n| and then divide
+by~|d|, in separate operations, since overflow might well occur; and it
+would be too inaccurate to divide by |d| and then multiply by |n|. Hence
+this subroutine simulates 1.5-precision arithmetic.
+
+@p static scaled xn_over_d(scaled @!x, int @!n, int @!d)
+{@+bool positive; /*was |x >= 0|?*/
+nonnegative_integer @!t, @!u, @!v; /*intermediate quantities*/
+scaled xn_over_d;
+if (x >= 0) positive=true;
+else{@+negate(x);positive=false;
+ }
+t=(x%0100000)*n;
+u=(x/0100000)*n+(t/0100000);
+v=(u%d)*0100000+(t%0100000);
+if (u/d >= 0100000) arith_error=true;
+else u=0100000*(u/d)+(v/d);
+if (positive)
+ {@+xn_over_d=u;rem=v%d;
+ }
+else{@+xn_over_d=-u;rem=-(v%d);
+ }
+return xn_over_d;}
+
+@ The next subroutine is used to compute the ``badness'' of glue, when a
+total~|t| is supposed to be made from amounts that sum to~|s|. According
+to {\sl The \TeX book}, the badness of this situation is $100(t/s)^3$;
+however, badness is simply a heuristic, so we need not squeeze out the
+last drop of accuracy when computing it. All we really want is an
+approximation that has similar properties.
+@:TeXbook}{\sl The \TeX book@>
+
+The actual method used to compute the badness is easier to read from the
+program than to describe in words. It produces an integer value that is a
+reasonably close approximation to $100(t/s)^3$, and all implementations
+of \TeX\ should use precisely this method. Any badness of $2^{13}$ or more is
+treated as infinitely bad, and represented by 10000.
+
+It is not difficult to prove that $$\hbox{|badness(t+1, s) >= badness(t, s)
+ >= badness(t, s+1)|}.$$ The badness function defined here is capable of
+computing at most 1095 distinct values, but that is plenty.
+
+@d inf_bad 10000 /*infinitely bad value*/
+
+@p @<Declare \Prote\ arithmetic routines@>@/
+static halfword badness(scaled @!t, scaled @!s) /*compute badness, given |t >= 0|*/
+{@+int r; /*approximation to $\alpha t/s$, where $\alpha^3\approx
+ 100\cdot2^{18}$*/
+if (t==0) return 0;
+else if (s <= 0) return inf_bad;
+else{@+if (t <= 7230584) r=(t*297)/s; /*$297^3=99.94\times2^{18}$*/
+ else if (s >= 1663497) r=t/(s/297);
+ else r=t;
+ if (r > 1290) return inf_bad; /*$1290^3<2^{31}<1291^3$*/
+ else return(r*r*r+0400000)/01000000;
+ } /*that was $r^3/2^{18}$, rounded to the nearest integer*/
+}
+
+@ When \TeX\ ``packages'' a list into a box, it needs to calculate the
+proportionality ratio by which the glue inside the box should stretch
+or shrink. This calculation does not affect \TeX's decision making,
+so the precise details of rounding, etc., in the glue calculation are not
+of critical importance for the consistency of results on different computers.
+
+We shall use the type |glue_ratio| for such proportionality ratios.
+A glue ratio should take the same amount of memory as an
+|int| (usually 32 bits) if it is to blend smoothly with \TeX's
+other data structures. Thus |glue_ratio| should be equivalent to
+|short_real| in some implementations of \PASCAL. Alternatively,
+it is possible to deal with glue ratios using nothing but fixed-point
+arithmetic; see {\sl TUGboat \bf3},1 (March 1982), 10--27. (But the
+routines cited there must be modified to allow negative glue ratios.)
+@^system dependencies@>
+
+@d set_glue_ratio_zero(A) A=0.0 /*store the representation of zero ratio*/
+@d set_glue_ratio_one(A) A=1.0 /*store the representation of unit ratio*/
+@d unfix(A) ((double)(A)) /*convert from |glue_ratio| to type |double|*/
+@d fix(A) ((glue_ratio)(A)) /*convert from |double| to type |glue_ratio|*/
+@d float_constant(A) ((double)(A)) /*convert |int| constant to |double|*/
+
+@<Types...@>=
+#if __SIZEOF_FLOAT__==4
+typedef float float32_t;
+#else
+#error @=float type must have size 4@>
+#endif
+typedef float @!glue_ratio; /*one-word representation of a glue expansion factor*/
+
+@* Packed data.
+In order to make efficient use of storage space, \TeX\ bases its major data
+structures on a |memory_word|, which contains either a (signed) integer,
+possibly scaled, or a (signed) |glue_ratio|, or a small number of
+fields that are one half or one quarter of the size used for storing
+integers.
+
+If |x| is a variable of type |memory_word|, it contains up to four
+fields that can be referred to as follows:
+$$\vbox{\halign{\hfil#&#\hfil&#\hfil\cr
+|x|&.|i|&(an |int|)\cr
+|x|&.|sc|\qquad&(a |scaled| integer)\cr
+|x|&.|gr|&(a |glue_ratio|)\cr
+|x.hh.lh|, |x.hh|&.|rh|&(two halfword fields)\cr
+|x.hh.b0|, |x.hh.b1|, |x.hh|&.|rh|&(two quarterword fields, one halfword
+ field)\cr
+|x.qqqq.b0|, |x.qqqq.b1|, |x.qqqq|&.|b2|, |x.qqqq.b3|\hskip-100pt
+ &\qquad\qquad\qquad(four quarterword fields)\cr}}$$
+This is somewhat cumbersome to write, and not very readable either, but
+macros will be used to make the notation shorter and more transparent.
+The \PASCAL\ code below gives a formal definition of |memory_word| and
+its subsidiary types, using packed variant records. \TeX\ makes no
+assumptions about the relative positions of the fields within a word.
+
+Since we are assuming 32-bit integers, a halfword must contain at least
+16 bits, and a quarterword must contain at least 8 bits.
+@^system dependencies@>
+But it doesn't hurt to have more bits; for example, with enough 36-bit
+words you might be able to have |mem_max| as large as 262142, which is
+eight times as much memory as anybody had during the first four years of
+\TeX's existence.
+
+N.B.: Valuable memory space will be dreadfully wasted unless \TeX\ is compiled
+by a \PASCAL\ that packs all of the |memory_word| variants into
+the space of a single integer. This means, for example, that |glue_ratio|
+words should be |short_real| instead of |double| on some computers. Some
+\PASCAL\ compilers will pack an integer whose subrange is `|0 dotdot 255|' into
+an eight-bit field, but others insist on allocating space for an additional
+sign bit; on such systems you can get 256 values into a quarterword only
+if the subrange is `|-128 dotdot 127|'.
+
+The present implementation tries to accommodate as many variations as possible,
+so it makes few assumptions. If integers having the subrange
+`|min_quarterword dotdot max_quarterword|' can be packed into a quarterword,
+and if integers having the subrange `|min_halfword dotdot max_halfword|'
+can be packed into a halfword, everything should work satisfactorily.
+
+It is usually most efficient to have |min_quarterword==min_halfword==0|,
+so one should try to achieve this unless it causes a severe problem.
+The values defined here are recommended for most 32-bit computers.
+
+@d min_quarterword 0 /*smallest allowable value in a |quarterword|*/
+@d max_quarterword 65535 /*largest allowable value in a |quarterword|*/
+@d min_halfword 0 /*smallest allowable value in a |halfword|*/
+@d max_halfword 0x3FFFFFFF /*largest allowable value in a |halfword|*/
+
+@ Here are the inequalities that the quarterword and halfword values
+must satisfy (or rather, the inequalities that they mustn't satisfy):
+
+@<Check the ``constant''...@>=
+#ifdef @!INIT
+if ((mem_min!=mem_bot)||(mem_max!=mem_top)) bad=10;
+#endif
+@;@/
+if ((mem_min > mem_bot)||(mem_max < mem_top)) bad=10;
+if ((min_quarterword > 0)||(max_quarterword < 127)) bad=11;
+if ((min_halfword > 0)||(max_halfword < 32767)) bad=12;
+if ((min_quarterword < min_halfword)||@|
+ (max_quarterword > max_halfword)) bad=13;
+if ((mem_min < min_halfword)||(mem_max >= max_halfword)||@|
+ (mem_bot-mem_min > max_halfword+1)) bad=14;
+if ((font_base < min_quarterword)||(font_max > max_quarterword)) bad=15;
+if (font_max > font_base+256) bad=16;
+if ((save_size > max_halfword)||(max_strings > max_halfword)) bad=17;
+if (buf_size > max_halfword) bad=18;
+if (max_quarterword-min_quarterword < 255) bad=19;
+
+@ The operation of adding or subtracting |min_quarterword| occurs quite
+frequently in \TeX, so it is convenient to abbreviate this operation
+by using the macros |qi| and |qo| for input and output to and from
+quarterword format.
+
+The inner loop of \TeX\ will run faster with respect to compilers
+that don't optimize expressions like `|x+0|' and `|x-0|', if these
+macros are simplified in the obvious way when |min_quarterword==0|.
+@^inner loop@>@^system dependencies@>
+
+@d qi(A) A+min_quarterword
+ /*to put an |eight_bits| item into a quarterword*/
+@d qo(A) A-min_quarterword
+ /*to take an |eight_bits| item out of a quarterword*/
+@d hi(A) A+min_halfword
+ /*to put a sixteen-bit item into a halfword*/
+@d ho(A) A-min_halfword
+ /*to take a sixteen-bit item from a halfword*/
+
+@ The reader should study the following definitions closely:
+@^system dependencies@>
+
+@d sc i /*|scaled| data is equivalent to |int|*/
+
+@<Types...@>=
+typedef uint16_t quarterword; /*1/4 of a word*/
+typedef int32_t halfword; /*1/2 of a word*/
+typedef int8_t two_choices; /*used when there are two variants in a record*/
+typedef int8_t four_choices; /*used when there are four variants in a record*/
+typedef uint64_t fullword;
+typedef struct { @;@/
+ halfword @!rh;
+ union {
+ halfword @!lh;
+ struct { quarterword @!b0;quarterword @!b1;} ;
+ };} two_halves;
+typedef struct { @;@/
+ quarterword @!b0;
+ quarterword @!b1;
+ quarterword @!b2;
+ quarterword @!b3;
+ } four_quarters;
+typedef struct { @;@/
+ union {
+ fullword @!w;
+ int @!i;
+ glue_ratio @!gr;
+ two_halves @!hh;
+ four_quarters @!qqqq;
+ };} memory_word;
+typedef struct {@+FILE *f;@+memory_word@,d;@+} word_file;
+
+@ When debugging, we may want to print a |memory_word| without knowing
+what type it is; so we print it in all modes.
+@^dirty \PASCAL@>@^debugging@>
+
+@p
+#ifdef @!DEBUG
+static void print_word(memory_word @!w)
+ /*prints |w| in all ways*/
+{@+print_int(w.i);print_char(' ');@/
+print_scaled(w.sc);print_char(' ');@/
+print_scaled(round(unity*unfix(w.gr)));print_ln();@/
+@^real multiplication@>
+print_int(w.hh.lh);print_char('=');print_int(w.hh.b0);print_char(':');
+print_int(w.hh.b1);print_char(';');print_int(w.hh.rh);print_char(' ');@/
+print_int(w.qqqq.b0);print_char(':');print_int(w.qqqq.b1);print_char(':');
+print_int(w.qqqq.b2);print_char(':');print_int(w.qqqq.b3);
+}
+#endif
+
+@* Dynamic memory allocation.
+The \TeX\ system does nearly all of its own memory allocation, so that it
+can readily be transported into environments that do not have automatic
+facilities for strings, garbage collection, etc., and so that it can be in
+control of what error messages the user receives. The dynamic storage
+requirements of \TeX\ are handled by providing a large array |mem| in
+which consecutive blocks of words are used as nodes by the \TeX\ routines.
+
+Pointer variables are indices into this array, or into another array
+called |eqtb| that will be explained later. A pointer variable might
+also be a special flag that lies outside the bounds of |mem|, so we
+allow pointers to assume any |halfword| value. The minimum halfword
+value represents a null pointer. \TeX\ does not assume that |mem[null]| exists.
+
+@s pointer int
+@d pointer halfword /*a flag or a location in |mem| or |eqtb|*/
+@d null min_halfword /*the null pointer*/
+
+@<Glob...@>=
+static pointer @!temp_ptr; /*a pointer variable for occasional emergency use*/
+
+@ The |mem| array is divided into two regions that are allocated separately,
+but the dividing line between these two regions is not fixed; they grow
+together until finding their ``natural'' size in a particular job.
+Locations less than or equal to |lo_mem_max| are used for storing
+variable-length records consisting of two or more words each. This region
+is maintained using an algorithm similar to the one described in exercise
+2.5--19 of {\sl The Art of Computer Programming}. However, no size field
+appears in the allocated nodes; the program is responsible for knowing the
+relevant size when a node is freed. Locations greater than or equal to
+|hi_mem_min| are used for storing one-word records; a conventional
+\.{AVAIL} stack is used for allocation in this region.
+
+Locations of |mem| between |mem_bot| and |mem_top| may be dumped as part
+of preloaded format files, by the \.{INITEX} preprocessor.
+@.INITEX@>
+Production versions of \TeX\ may extend the memory at both ends in order to
+provide more space; locations between |mem_min| and |mem_bot| are always
+used for variable-size nodes, and locations between |mem_top| and |mem_max|
+are always used for single-word nodes.
+
+The key pointers that govern |mem| allocation have a prescribed order:
+$$\advance\thickmuskip-2mu
+\hbox{|null <= mem_min <= mem_bot < lo_mem_max <
+ hi_mem_min < mem_top <= mem_end <= mem_max|.}$$
+
+Empirical tests show that the present implementation of \TeX\ tends to
+spend about 9\pct! of its running time allocating nodes, and about 6\pct!
+deallocating them after their use.
+
+@<Glob...@>=
+static memory_word @!mem0[mem_max-mem_min+1],
+ *const @!mem = @!mem0-mem_min; /*the big dynamic storage area*/
+static pointer @!lo_mem_max; /*the largest location of variable-size memory in use*/
+static pointer @!hi_mem_min; /*the smallest location of one-word memory in use*/
+
+@ In order to study the memory requirements of particular applications, it
+is possible to prepare a version of \TeX\ that keeps track of current and
+maximum memory usage. When code between the delimiters |
+#ifdef @!STAT
+| $\ldots$
+|@t\#\&{endif}@>| is not ``commented out,'' \TeX\ will run a bit slower but it will
+report these statistics when |tracing_stats| is sufficiently large.
+
+@<Glob...@>=
+static int @!var_used, @!dyn_used; /*how much memory is in use*/
+#ifdef @!STAT
+#define incr_dyn_used @[incr(dyn_used)@]
+#define decr_dyn_used @[decr(dyn_used)@]
+#else
+#define incr_dyn_used
+#define decr_dyn_used
+#endif
+
+@ Let's consider the one-word memory region first, since it's the
+simplest. The pointer variable |mem_end| holds the highest-numbered location
+of |mem| that has ever been used. The free locations of |mem| that
+occur between |hi_mem_min| and |mem_end|, inclusive, are of type
+|two_halves|, and we write |info(p)| and |link(p)| for the |lh|
+and |rh| fields of |mem[p]| when it is of this type. The single-word
+free locations form a linked list
+$$|avail|,\;\hbox{|link(avail)|},\;\hbox{|link(link(avail))|},\;\ldots$$
+terminated by |null|.
+
+@d link(A) mem[A].hh.rh /*the |link| field of a memory word*/
+@d info(A) mem[A].hh.lh /*the |info| field of a memory word*/
+
+@<Glob...@>=
+static pointer @!avail; /*head of the list of available one-word nodes*/
+static pointer @!mem_end; /*the last one-word node used in |mem|*/
+
+@ If memory is exhausted, it might mean that the user has forgotten
+a right brace. We will define some procedures later that try to help
+pinpoint the trouble.
+
+@p @<Declare the procedure called |show_token_list|@>@/
+@<Declare the procedure called |runaway|@>@;
+
+@ The function |get_avail| returns a pointer to a new one-word node whose
+|link| field is null. However, \TeX\ will halt if there is no more room left.
+@^inner loop@>
+
+If the available-space list is empty, i.e., if |avail==null|,
+we try first to increase |mem_end|. If that cannot be done, i.e., if
+|mem_end==mem_max|, we try to decrease |hi_mem_min|. If that cannot be
+done, i.e., if |hi_mem_min==lo_mem_max+1|, we have to quit.
+
+@p static pointer get_avail(void) /*single-word node allocation*/
+{@+pointer p; /*the new node being got*/
+p=avail; /*get top location in the |avail| stack*/
+if (p!=null) avail=link(avail); /*and pop it off*/
+else if (mem_end < mem_max) /*or go into virgin territory*/
+ {@+incr(mem_end);p=mem_end;
+ }
+else{@+decr(hi_mem_min);p=hi_mem_min;
+ if (hi_mem_min <= lo_mem_max)
+ {@+runaway(); /*if memory is exhausted, display possible runaway text*/
+ overflow("main memory size", mem_max+1-mem_min);
+ /*quit; all one-word nodes are busy*/
+@:TeX capacity exceeded main memory size}{\quad main memory size@>
+ }
+ }
+link(p)=null; /*provide an oft-desired initialization of the new node*/
+#ifdef @!STAT
+incr(dyn_used);
+#endif
+@; /*maintain statistics*/
+return p;
+}
+
+@ Conversely, a one-word node is recycled by calling |free_avail|.
+This routine is part of \TeX's ``inner loop,'' so we want it to be fast.
+@^inner loop@>
+
+@d free_avail(A) /*single-word node liberation*/
+ {@+link(A)=avail;avail=A;
+ decr_dyn_used;
+ }
+
+@ There's also a |fast_get_avail| routine, which saves the procedure-call
+overhead at the expense of extra programming. This routine is used in
+the places that would otherwise account for the most calls of |get_avail|.
+@^inner loop@>
+
+@d fast_get_avail(A) @t@>@;@/
+ {@+A=avail; /*avoid |get_avail| if possible, to save time*/
+ if (A==null) A=get_avail();
+ else{@+avail=link(A);link(A)=null;
+ incr_dyn_used;
+ }
+ }
+
+@ The procedure |flush_list(p)| frees an entire linked list of
+one-word nodes that starts at position |p|.
+@^inner loop@>
+
+@p static void flush_list(pointer @!p) /*makes list of single-word nodes
+ available*/
+{@+pointer @!q, @!r; /*list traversers*/
+if (p!=null)
+ {@+r=p;
+ @/do@+{q=r;r=link(r);
+#ifdef @!STAT
+decr(dyn_used);
+#endif
+ }@+ while (!(r==null)); /*now |q| is the last node on the list*/
+ link(q)=avail;avail=p;
+ }
+}
+
+@ The available-space list that keeps track of the variable-size portion
+of |mem| is a nonempty, doubly-linked circular list of empty nodes,
+pointed to by the roving pointer |rover|.
+
+Each empty node has size 2 or more; the first word contains the special
+value |max_halfword| in its |link| field and the size in its |info| field;
+the second word contains the two pointers for double linking.
+
+Each nonempty node also has size 2 or more. Its first word is of type
+|two_halves|\kern-1pt, and its |link| field is never equal to |max_halfword|.
+Otherwise there is complete flexibility with respect to the contents
+of its other fields and its other words.
+
+(We require |mem_max < max_halfword| because terrible things can happen
+when |max_halfword| appears in the |link| field of a nonempty node.)
+
+@d empty_flag max_halfword /*the |link| of an empty variable-size node*/
+@d is_empty(A) (link(A)==empty_flag) /*tests for empty node*/
+@d node_size(A) info(A) /*the size field in empty variable-size nodes*/
+@d llink(A) info(A+1) /*left link in doubly-linked list of empty nodes*/
+@d rlink(A) link(A+1) /*right link in doubly-linked list of empty nodes*/
+
+@<Glob...@>=
+static pointer @!rover; /*points to some node in the list of empties*/
+
+@ A call to |get_node| with argument |s| returns a pointer to a new node
+of size~|s|, which must be 2~or more. The |link| field of the first word
+of this new node is set to null. An overflow stop occurs if no suitable
+space exists.
+
+If |get_node| is called with $s=2^{30}$, it simply merges adjacent free
+areas and returns the value |max_halfword|.
+
+@p static pointer get_node(int @!s) /*variable-size node allocation*/
+{@+
+pointer p; /*the node currently under inspection*/
+pointer @!q; /*the node physically after node |p|*/
+int @!r; /*the newly allocated node, or a candidate for this honor*/
+int @!t; /*temporary register*/
+restart: p=rover; /*start at some free node in the ring*/
+@/do@+{@<Try to allocate within node |p| and its physical successors, and
+|goto found| if allocation was possible@>;
+@^inner loop@>
+p=rlink(p); /*move to the next node in the ring*/
+}@+ while (!(p==rover)); /*repeat until the whole list has been traversed*/
+if (s==010000000000)
+ {@+return max_halfword;
+ }
+if (lo_mem_max+2 < hi_mem_min) if (lo_mem_max+2 <= mem_bot+max_halfword)
+ @<Grow more variable-size memory and |goto restart|@>;
+overflow("main memory size", mem_max+1-mem_min);
+ /*sorry, nothing satisfactory is left*/
+@:TeX capacity exceeded main memory size}{\quad main memory size@>
+found: link(r)=null; /*this node is now nonempty*/
+#ifdef @!STAT
+var_used=var_used+s; /*maintain usage statistics*/
+#endif
+@;@/
+return r;
+}
+
+@ The lower part of |mem| grows by 1000 words at a time, unless
+we are very close to going under. When it grows, we simply link
+a new node into the available-space list. This method of controlled
+growth helps to keep the |mem| usage consecutive when \TeX\ is
+implemented on ``virtual memory'' systems.
+@^virtual memory@>
+
+@<Grow more variable-size memory and |goto restart|@>=
+{@+if (hi_mem_min-lo_mem_max >= 1998) t=lo_mem_max+1000;
+else t=lo_mem_max+1+(hi_mem_min-lo_mem_max)/2;
+ /*|lo_mem_max+2 <= t < hi_mem_min|*/
+p=llink(rover);q=lo_mem_max;rlink(p)=q;llink(rover)=q;@/
+if (t > mem_bot+max_halfword) t=mem_bot+max_halfword;
+rlink(q)=rover;llink(q)=p;link(q)=empty_flag;node_size(q)=t-lo_mem_max;@/
+lo_mem_max=t;link(lo_mem_max)=null;info(lo_mem_max)=null;
+rover=q;goto restart;
+}
+
+@ Empirical tests show that the routine in this section performs a
+node-merging operation about 0.75 times per allocation, on the average,
+after which it finds that |r > p+1| about 95\pct! of the time.
+
+@<Try to allocate...@>=
+q=p+node_size(p); /*find the physical successor*/
+@^inner loop@>
+while (is_empty(q)) /*merge node |p| with node |q|*/
+ {@+t=rlink(q);
+ if (q==rover) rover=t;
+ llink(t)=llink(q);rlink(llink(q))=t;@/
+ q=q+node_size(q);
+ }
+r=q-s;
+if (r > p+1) @<Allocate from the top of node |p| and |goto found|@>;
+if (r==p) if (rlink(p)!=p)
+ @<Allocate entire node |p| and |goto found|@>;
+node_size(p)=q-p /*reset the size in case it grew*/
+
+@ @<Allocate from the top...@>=
+{@+node_size(p)=r-p; /*store the remaining size*/
+@^inner loop@>
+rover=p; /*start searching here next time*/
+goto found;
+}
+
+@ Here we delete node |p| from the ring, and let |rover| rove around.
+
+@<Allocate entire...@>=
+{@+rover=rlink(p);t=llink(p);
+llink(rover)=t;rlink(t)=rover;
+goto found;
+}
+
+@ Conversely, when some variable-size node |p| of size |s| is no longer needed,
+the operation |free_node(p, s)| will make its words available, by inserting
+|p| as a new empty node just before where |rover| now points.
+@^inner loop@>
+
+@p static void free_node(pointer @!p, halfword @!s) /*variable-size node
+ liberation*/
+{@+pointer q; /*|llink(rover)|*/
+node_size(p)=s;link(p)=empty_flag;
+q=llink(rover);llink(p)=q;rlink(p)=rover; /*set both links*/
+llink(rover)=p;rlink(q)=p; /*insert |p| into the ring*/
+#ifdef @!STAT
+var_used=var_used-s;
+#endif
+@; /*maintain statistics*/
+}
+
+@ Just before \.{INITEX} writes out the memory, it sorts the doubly linked
+available space list. The list is probably very short at such times, so a
+simple insertion sort is used. The smallest available location will be
+pointed to by |rover|, the next-smallest by |rlink(rover)|, etc.
+
+@p
+#ifdef @!INIT
+static void sort_avail(void) /*sorts the available variable-size nodes
+ by location*/
+{@+pointer p, @!q, @!r; /*indices into |mem|*/
+pointer @!old_rover; /*initial |rover| setting*/
+p=get_node(010000000000); /*merge adjacent free areas*/
+p=rlink(rover);rlink(rover)=max_halfword;old_rover=rover;
+while (p!=old_rover) @<Sort \(p)|p| into the list starting at |rover| and
+advance |p| to |rlink(p)|@>;
+p=rover;
+while (rlink(p)!=max_halfword)
+ {@+llink(rlink(p))=p;p=rlink(p);
+ }
+rlink(p)=rover;llink(rover)=p;
+}
+#endif
+
+@ The following |while | loop is guaranteed to
+terminate, since the list that starts at
+|rover| ends with |max_halfword| during the sorting procedure.
+
+@<Sort \(p)|p|...@>=
+if (p < rover)
+ {@+q=p;p=rlink(q);rlink(q)=rover;rover=q;
+ }
+else{@+q=rover;
+ while (rlink(q) < p) q=rlink(q);
+ r=rlink(p);rlink(p)=rlink(q);rlink(q)=p;p=r;
+ }
+
+@* Data structures for boxes and their friends.
+From the computer's standpoint, \TeX's chief mission is to create
+horizontal and vertical lists. We shall now investigate how the elements
+of these lists are represented internally as nodes in the dynamic memory.
+
+A horizontal or vertical list is linked together by |link| fields in
+the first word of each node. Individual nodes represent boxes, glue,
+penalties, or special things like discretionary hyphens; because of this
+variety, some nodes are longer than others, and we must distinguish different
+kinds of nodes. We do this by putting a `|type|' field in the first word,
+together with the link and an optional `|subtype|'.
+
+@d type(A) mem[A].hh.b0 /*identifies what kind of node this is*/
+@d subtype(A) mem[A].hh.b1 /*secondary identification in some cases*/
+
+@ A |@!char_node|, which represents a single character, is the most important
+kind of node because it accounts for the vast majority of all boxes.
+Special precautions are therefore taken to ensure that a |char_node| does
+not take up much memory space. Every such node is one word long, and in fact
+it is identifiable by this property, since other kinds of nodes have at least
+two words, and they appear in |mem| locations less than |hi_mem_min|.
+This makes it possible to omit the |type| field in a |char_node|, leaving
+us room for two bytes that identify a |font| and a |character| within
+that font.
+
+Note that the format of a |char_node| allows for up to 256 different
+fonts and up to 256 characters per font; but most implementations will
+probably limit the total number of fonts to fewer than 75 per job,
+and most fonts will stick to characters whose codes are
+less than 128 (since higher codes
+are more difficult to access on most keyboards).
+
+Extensions of \TeX\ intended for oriental languages will need even more
+than $256\times256$ possible characters, when we consider different sizes
+@^oriental characters@>@^Chinese characters@>@^Japanese characters@>
+and styles of type. It is suggested that Chinese and Japanese fonts be
+handled by representing such characters in two consecutive |char_node|
+entries: The first of these has |font==font_base|, and its |link| points
+to the second;
+the second identifies the font and the character dimensions.
+The saving feature about oriental characters is that most of them have
+the same box dimensions. The |character| field of the first |char_node|
+is a ``\\{charext}'' that distinguishes between graphic symbols whose
+dimensions are identical for typesetting purposes. (See the \MF\ manual.)
+Such an extension of \TeX\ would not be difficult; further details are
+left to the reader.
+
+In order to make sure that the |character| code fits in a quarterword,
+\TeX\ adds the quantity |min_quarterword| to the actual code.
+
+Character nodes appear only in horizontal lists, never in vertical lists.
+
+@d is_char_node(A) (A >= hi_mem_min)
+ /*does the argument point to a |char_node|?*/
+@d font(A) type(A) /*the font code in a |char_node|*/
+@d character(A) subtype(A) /*the character code in a |char_node|*/
+
+@ An |hlist_node| stands for a box that was made from a horizontal list.
+Each |hlist_node| is seven words long, and contains the following fields
+(in addition to the mandatory |type| and |link|, which we shall not
+mention explicitly when discussing the other node types): The |height| and
+|width| and |depth| are scaled integers denoting the dimensions of the
+box. There is also a |shift_amount| field, a scaled integer indicating
+how much this box should be lowered (if it appears in a horizontal list),
+or how much it should be moved to the right (if it appears in a vertical
+list). There is a |list_ptr| field, which points to the beginning of the
+list from which this box was fabricated; if |list_ptr| is |null|, the box
+is empty. Finally, there are three fields that represent the setting of
+the glue: |glue_set(p)| is a word of type |glue_ratio| that represents
+the proportionality constant for glue setting; |glue_sign(p)| is
+|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.
+
+@d hlist_node 0 /*|type| of hlist nodes*/
+@d box_node_size 7 /*number of words to allocate for a box node*/
+@d width_offset 1 /*position of |width| field in a box node*/
+@d depth_offset 2 /*position of |depth| field in a box node*/
+@d height_offset 3 /*position of |height| field in a box node*/
+@d width(A) mem[A+width_offset].sc /*width of the box, in sp*/
+@d depth(A) mem[A+depth_offset].sc /*depth of the box, in sp*/
+@d height(A) mem[A+height_offset].sc /*height of the box, in sp*/
+@d shift_amount(A) mem[A+4].sc /*repositioning distance, in sp*/
+@d list_offset 5 /*position of |list_ptr| field in a box node*/
+@d list_ptr(A) link(A+list_offset) /*beginning of the list inside the box*/
+@d glue_order(A) subtype(A+list_offset) /*applicable order of infinity*/
+@d glue_sign(A) type(A+list_offset) /*stretching or shrinking*/
+@d normal 0 /*the most common case when several cases are named*/
+@d stretching 1 /*glue setting applies to the stretch components*/
+@d shrinking 2 /*glue setting applies to the shrink components*/
+@d glue_offset 6 /*position of |glue_set| in a box node*/
+@d glue_set(A) mem[A+glue_offset].gr
+ /*a word of type |glue_ratio| for glue setting*/
+
+@ The |new_null_box| function returns a pointer to an |hlist_node| in
+which all subfields have the values corresponding to `\.{\\hbox\{\}}'.
+(The |subtype| field is set to |min_quarterword|, for historic reasons
+that are no longer relevant.)
+
+@p static pointer new_null_box(void) /*creates a new box node*/
+{@+pointer p; /*the new node*/
+p=get_node(box_node_size);type(p)=hlist_node;
+subtype(p)=min_quarterword;
+width(p)=0;depth(p)=0;height(p)=0;shift_amount(p)=0;list_ptr(p)=null;
+glue_sign(p)=normal;glue_order(p)=normal;set_glue_ratio_zero(glue_set(p));
+return p;
+}
+
+@ A |vlist_node| is like an |hlist_node| in all respects except that it
+contains a vertical list.
+
+@d vlist_node 1 /*|type| of vlist nodes*/
+
+@ A |rule_node| stands for a solid black rectangle; it has |width|,
+|depth|, and |height| fields just as in an |hlist_node|. However, if
+any of these dimensions is $-2^{30}$, the actual value will be determined
+by running the rule up to the boundary of the innermost enclosing box.
+This is called a ``running dimension.'' The |width| is never running in
+an hlist; the |height| and |depth| are never running in a~vlist.
+
+@d rule_node 2 /*|type| of rule nodes*/
+@d rule_node_size 4 /*number of words to allocate for a rule node*/
+@d null_flag -010000000000 /*$-2^{30}$, signifies a missing item*/
+@d is_running(A) (A==null_flag) /*tests for a running dimension*/
+
+@ A new rule node is delivered by the |new_rule| function. It
+makes all the dimensions ``running,'' so you have to change the
+ones that are not allowed to run.
+
+@p static pointer new_rule(void)
+{@+pointer p; /*the new node*/
+p=get_node(rule_node_size);type(p)=rule_node;
+subtype(p)=0; /*the |subtype| is not used*/
+width(p)=null_flag;depth(p)=null_flag;height(p)=null_flag;
+return p;
+}
+
+@ Insertions are represented by |ins_node| records, where the |subtype|
+indicates the corresponding box number. For example, `\.{\\insert 250}'
+leads to an |ins_node| whose |subtype| is |250+min_quarterword|.
+The |height| field of an |ins_node| is slightly misnamed; it actually holds
+the natural height plus depth of the vertical list being inserted.
+The |depth| field holds the |split_max_depth| to be used in case this
+insertion is split, and the |split_top_ptr| points to the corresponding
+|split_top_skip|. The |float_cost| field holds the |floating_penalty| that
+will be used if this insertion floats to a subsequent page after a
+split insertion of the same class. There is one more field, the
+|ins_ptr|, which points to the beginning of the vlist for the insertion.
+
+@d ins_node 3 /*|type| of insertion nodes*/
+@d ins_node_size 5 /*number of words to allocate for an insertion*/
+@d float_cost(A) mem[A+1].i /*the |floating_penalty| to be used*/
+@d ins_ptr(A) info(A+4) /*the vertical list to be inserted*/
+@d split_top_ptr(A) link(A+4) /*the |split_top_skip| to be used*/
+
+@ A |mark_node| has a |mark_ptr| field that points to the reference count
+of a token list that contains the user's \.{\\mark} text.
+In addition there is a |mark_class| field that contains the mark class.
+
+@d mark_node 4 /*|type| of a mark node*/
+@d small_node_size 2 /*number of words to allocate for most node types*/
+@d mark_ptr(A) link(A+1) /*head of the token list for a mark*/
+@d mark_class(A) info(A+1) /*the mark class*/
+
+@ An |adjust_node|, which occurs only in horizontal lists,
+specifies material that will be moved out into the surrounding
+vertical list; i.e., it is used to implement \TeX's `\.{\\vadjust}'
+operation. The |adjust_ptr| field points to the vlist containing this
+material.
+
+@d adjust_node 5 /*|type| of an adjust node*/
+@d adjust_ptr(A) mem[A+1].i
+ /*vertical list to be moved out of horizontal list*/
+
+@ A |ligature_node|, which occurs only in horizontal lists, specifies
+a character that was fabricated from the interaction of two or more
+actual characters. The second word of the node, which is called the
+|lig_char| word, contains |font| and |character| fields just as in a
+|char_node|. The characters that generated the ligature have not been
+forgotten, since they are needed for diagnostic messages and for
+hyphenation; the |lig_ptr| field points to a linked list of character
+nodes for all original characters that have been deleted. (This list
+might be empty if the characters that generated the ligature were
+retained in other nodes.)
+
+The |subtype| field is 0, plus 2 and/or 1 if the original source of the
+ligature included implicit left and/or right boundaries.
+
+@d ligature_node 6 /*|type| of a ligature node*/
+@d lig_char(A) A+1 /*the word where the ligature is to be found*/
+@d lig_ptr(A) link(lig_char(A)) /*the list of characters*/
+
+@ The |new_ligature| function creates a ligature node having given
+contents of the |font|, |character|, and |lig_ptr| fields. We also have
+a |new_lig_item| function, which returns a two-word node having a given
+|character| field. Such nodes are used for temporary processing as ligatures
+are being created.
+
+@p static pointer new_ligature(quarterword @!f, quarterword @!c, pointer @!q)
+{@+pointer p; /*the new node*/
+p=get_node(small_node_size);type(p)=ligature_node;
+font(lig_char(p))=f;character(lig_char(p))=c;lig_ptr(p)=q;
+subtype(p)=0;return p;
+}
+@#
+static pointer new_lig_item(quarterword @!c)
+{@+pointer p; /*the new node*/
+p=get_node(small_node_size);character(p)=c;lig_ptr(p)=null;
+return p;
+}
+
+@ A |disc_node|, which occurs only in horizontal lists, specifies a
+``dis\-cretion\-ary'' line break. If such a break occurs at node |p|, the text
+that starts at |pre_break(p)| will precede the break, the text that starts at
+|post_break(p)| will follow the break, and text that appears in the next
+|replace_count(p)| nodes will be ignored. For example, an ordinary
+discretionary hyphen, indicated by `\.{\\-}', yields a |disc_node| with
+|pre_break| pointing to a |char_node| containing a hyphen, |post_break==null|,
+and |replace_count==0|. All three of the discretionary texts must be
+lists that consist entirely of character, kern, box, rule, and ligature nodes.
+
+If |pre_break(p)==null|, the |ex_hyphen_penalty| will be charged for this
+break. Otherwise the |hyphen_penalty| will be charged. The texts will
+actually be substituted into the list by the line-breaking algorithm if it
+decides to make the break, and the discretionary node will disappear at
+that time; thus, the output routine sees only discretionaries that were
+not chosen.
+
+@d disc_node 7 /*|type| of a discretionary node*/
+@d replace_count(A) subtype(A) /*how many subsequent nodes to replace*/
+@d pre_break(A) llink(A) /*text that precedes a discretionary break*/
+@d post_break(A) rlink(A) /*text that follows a discretionary break*/
+
+@p static pointer new_disc(void) /*creates an empty |disc_node|*/
+{@+pointer p; /*the new node*/
+p=get_node(small_node_size);type(p)=disc_node;
+replace_count(p)=0;pre_break(p)=null;post_break(p)=null;
+return p;
+}
+
+@ A |whatsit_node| is a wild card reserved for extensions to \TeX. The
+|subtype| field in its first word says what `\\{whatsit}' it is, and
+implicitly determines the node size (which must be 2 or more) and the
+format of the remaining words. When a |whatsit_node| is encountered
+in a list, special actions are invoked; knowledgeable people who are
+careful not to mess up the rest of \TeX\ are able to make \TeX\ do new
+things by adding code at the end of the program. For example, there
+might be a `\TeX nicolor' extension to specify different colors of ink,
+@^extensions to \TeX@>
+and the whatsit node might contain the desired parameters.
+
+The present implementation of \TeX\ treats the features associated with
+`\.{\\write}' and `\.{\\special}' as if they were extensions, in order to
+illustrate how such routines might be coded. We shall defer further
+discussion of extensions until the end of this program.
+
+@d whatsit_node 8 /*|type| of special extension nodes*/
+
+@ A |math_node|, which occurs only in horizontal lists, appears before and
+after mathematical formulas. The |subtype| field is |before| before the
+formula and |after| after it. There is a |width| field, which represents
+the amount of surrounding space inserted by \.{\\mathsurround}.
+
+@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*/
+
+@p static pointer new_math(scaled @!w, small_number @!s)
+{@+pointer p; /*the new node*/
+p=get_node(small_node_size);type(p)=math_node;
+subtype(p)=s;width(p)=w;return p;
+}
+
+@ \TeX\ makes use of the fact that |hlist_node|, |vlist_node|,
+|rule_node|, |ins_node|, |mark_node|, |adjust_node|, |ligature_node|,
+|disc_node|, |whatsit_node|, and |math_node| are at the low end of the
+type codes, by permitting a break at glue in a list if and only if the
+|type| of the previous node is less than |math_node|. Furthermore, a
+node is discarded after a break if its type is |math_node| or~more.
+
+@d precedes_break(A) (type(A) < math_node)
+@d non_discardable(A) (type(A) < math_node)
+
+@ A |glue_node| represents glue in a list. However, it is really only
+a pointer to a separate glue specification, since \TeX\ makes use of the
+fact that many essentially identical nodes of glue are usually present.
+If |p| points to a |glue_node|, |glue_ptr(p)| points to
+another packet of words that specify the stretch and shrink components, etc.
+
+Glue nodes also serve to represent leaders; the |subtype| is used to
+distinguish between ordinary glue (which is called |normal|) and the three
+kinds of leaders (which are called |a_leaders|, |c_leaders|, and |x_leaders|).
+The |leader_ptr| field points to a rule node or to a box node containing the
+leaders; it is set to |null| in ordinary glue nodes.
+
+Many kinds of glue are computed from \TeX's ``skip'' parameters, and
+it is helpful to know which parameter has led to a particular glue node.
+Therefore the |subtype| is set to indicate the source of glue, whenever
+it originated as a parameter. We will be defining symbolic names for the
+parameter numbers later (e.g., |line_skip_code==0|, |baseline_skip_code==1|,
+etc.); it suffices for now to say that the |subtype| of parametric glue
+will be the same as the parameter number, plus~one.
+
+In math formulas there are two more possibilities for the |subtype| in a
+glue node: |mu_glue| denotes an \.{\\mskip} (where the units are scaled \.{mu}
+instead of scaled \.{pt}); and |cond_math_glue| denotes the `\.{\\nonscript}'
+feature that cancels the glue node immediately following if it appears
+in a subscript.
+
+@d glue_node 10 /*|type| of node that points to a glue specification*/
+@d cond_math_glue 98 /*special |subtype| to suppress glue in the next node*/
+@d mu_glue 99 /*|subtype| for math glue*/
+@d a_leaders 100 /*|subtype| for aligned leaders*/
+@d c_leaders 101 /*|subtype| for centered leaders*/
+@d x_leaders 102 /*|subtype| for expanded leaders*/
+@d glue_ptr(A) llink(A) /*pointer to a glue specification*/
+@d leader_ptr(A) rlink(A) /*pointer to box or rule node for leaders*/
+
+@ A glue specification has a halfword reference count in its first word,
+@^reference counts@>
+representing |null| plus the number of glue nodes that point to it (less one).
+Note that the reference count appears in the same position as
+the |link| field in list nodes; this is the field that is initialized
+to |null| when a node is allocated, and it is also the field that is flagged
+by |empty_flag| in empty nodes.
+
+Glue specifications also contain three |scaled| fields, for the |width|,
+|stretch|, and |shrink| dimensions. Finally, there are two one-byte
+fields called |stretch_order| and |shrink_order|; these contain the
+orders of infinity (|normal|, |fil|, |fill|, or |filll|)
+corresponding to the stretch and shrink values.
+
+@d glue_spec_size 4 /*number of words to allocate for a glue specification*/
+@d glue_ref_count(A) link(A) /*reference count of a glue specification*/
+@d stretch(A) mem[A+2].sc /*the stretchability of this glob of glue*/
+@d shrink(A) mem[A+3].sc /*the shrinkability of this glob of glue*/
+@d stretch_order(A) type(A) /*order of infinity for stretching*/
+@d shrink_order(A) subtype(A) /*order of infinity for shrinking*/
+@d fil 1 /*first-order infinity*/
+@d fill 2 /*second-order infinity*/
+@d filll 3 /*third-order infinity*/
+
+@<Types...@>=
+typedef int8_t glue_ord; /*infinity to the 0, 1, 2, or 3 power*/
+
+@ Here is a function that returns a pointer to a copy of a glue spec.
+The reference count in the copy is |null|, because there is assumed
+to be exactly one reference to the new specification.
+
+@p static pointer new_spec(pointer @!p) /*duplicates a glue specification*/
+{@+pointer q; /*the new spec*/
+q=get_node(glue_spec_size);@/
+mem[q]=mem[p];glue_ref_count(q)=null;@/
+width(q)=width(p);stretch(q)=stretch(p);shrink(q)=shrink(p);
+return q;
+}
+
+@ And here's a function that creates a glue node for a given parameter
+identified by its code number; for example,
+|new_param_glue(line_skip_code)| returns a pointer to a glue node for the
+current \.{\\lineskip}.
+
+@p static pointer new_param_glue(small_number @!n)
+{@+pointer p; /*the new node*/
+pointer @!q; /*the glue specification*/
+p=get_node(small_node_size);type(p)=glue_node;subtype(p)=n+1;
+leader_ptr(p)=null;@/
+q=@<Current |mem| equivalent of glue parameter number |n|@>@t@>;
+glue_ptr(p)=q;incr(glue_ref_count(q));
+return p;
+}
+
+@ Glue nodes that are more or less anonymous are created by |new_glue|,
+whose argument points to a glue specification.
+
+@p static pointer new_glue(pointer @!q)
+{@+pointer p; /*the new node*/
+p=get_node(small_node_size);type(p)=glue_node;subtype(p)=normal;
+leader_ptr(p)=null;glue_ptr(p)=q;incr(glue_ref_count(q));
+return p;
+}
+
+@ Still another subroutine is needed: This one is sort of a combination
+of |new_param_glue| and |new_glue|. It creates a glue node for one of
+the current glue parameters, but it makes a fresh copy of the glue
+specification, since that specification will probably be subject to change,
+while the parameter will stay put. The global variable |temp_ptr| is
+set to the address of the new spec.
+
+@p static pointer new_skip_param(small_number @!n)
+{@+pointer p; /*the new node*/
+temp_ptr=new_spec(@<Current |mem| equivalent of glue parameter...@>);
+p=new_glue(temp_ptr);glue_ref_count(temp_ptr)=null;subtype(p)=n+1;
+return p;
+}
+
+@ A |kern_node| has a |width| field to specify a (normally negative)
+amount of spacing. This spacing correction appears in horizontal lists
+between letters like A and V when the font designer said that it looks
+better to move them closer together or further apart. A kern node can
+also appear in a vertical list, when its `|width|' denotes additional
+spacing in the vertical direction. The |subtype| is either |normal| (for
+kerns inserted from font information or math mode calculations) or |explicit|
+(for kerns inserted from \.{\\kern} and \.{\\/} commands) or |acc_kern|
+(for kerns inserted from non-math accents) or |mu_glue| (for kerns
+inserted from \.{\\mkern} specifications in math formulas).
+
+@d kern_node 11 /*|type| of a kern node*/
+@d explicit 1 /*|subtype| of kern nodes from \.{\\kern} and \.{\\/}*/
+@d acc_kern 2 /*|subtype| of kern nodes from accents*/
+
+@ The |new_kern| function creates a kern node having a given width.
+
+@p static pointer new_kern(scaled @!w)
+{@+pointer p; /*the new node*/
+p=get_node(small_node_size);type(p)=kern_node;
+subtype(p)=normal;
+width(p)=w;
+return p;
+}
+
+@ A |penalty_node| specifies the penalty associated with line or page
+breaking, in its |penalty| field. This field is a fullword integer, but
+the full range of integer values is not used: Any penalty | >= 10000| is
+treated as infinity, and no break will be allowed for such high values.
+Similarly, any penalty | <= -10000| is treated as negative infinity, and a
+break will be forced.
+
+@d penalty_node 12 /*|type| of a penalty node*/
+@d inf_penalty inf_bad /*``infinite'' penalty value*/
+@d eject_penalty (-inf_penalty) /*``negatively infinite'' penalty value*/
+@d penalty(A) mem[A+1].i /*the added cost of breaking a list here*/
+
+@ Anyone who has been reading the last few sections of the program will
+be able to guess what comes next.
+
+@p static pointer new_penalty(int @!m)
+{@+pointer p; /*the new node*/
+p=get_node(small_node_size);type(p)=penalty_node;
+subtype(p)=0; /*the |subtype| is not used*/
+penalty(p)=m;return p;
+}
+
+@ You might think that we have introduced enough node types by now. Well,
+almost, but there is one more: An |unset_node| has nearly the same format
+as an |hlist_node| or |vlist_node|; it is used for entries in \.{\\halign}
+or \.{\\valign} that are not yet in their final form, since the box
+dimensions are their ``natural'' sizes before any glue adjustment has been
+made. The |glue_set| word is not present; instead, we have a |glue_stretch|
+field, which contains the total stretch of order |glue_order| that is
+present in the hlist or vlist being boxed.
+Similarly, the |shift_amount| field is replaced by a |glue_shrink| field,
+containing the total shrink of order |glue_sign| that is present.
+The |subtype| field is called |span_count|; an unset box typically
+contains the data for |qo(span_count)+1| columns.
+Unset nodes will be changed to box nodes when alignment is completed.
+
+@d unset_node 13 /*|type| for an unset node*/
+@d glue_stretch(A) mem[A+glue_offset].sc /*total stretch in an unset node*/
+@d glue_shrink(A) shift_amount(A) /*total shrink in an unset node*/
+@d span_count(A) subtype(A) /*indicates the number of spanned columns*/
+
+@ In fact, there are still more types coming. When we get to math formula
+processing we will see that a |style_node| has |type==14|; and a number
+of larger type codes will also be defined, for use in math mode only.
+
+@ Warning: If any changes are made to these data structure layouts, such as
+changing any of the node sizes or even reordering the words of nodes,
+the |copy_node_list| procedure and the memory initialization code
+below may have to be changed. Such potentially dangerous parts of the
+program are listed in the index under `data structure assumptions'.
+@!@^data structure assumptions@>
+However, other references to the nodes are made symbolically in terms of
+the \.{WEB} macro definitions above, so that format changes will leave
+\TeX's other algorithms intact.
+@^system dependencies@>
+
+@* Memory layout.
+Some areas of |mem| are dedicated to fixed usage, since static allocation is
+more efficient than dynamic allocation when we can get away with it. For
+example, locations |mem_bot| to |mem_bot+3| are always used to store the
+specification for glue that is `\.{0pt plus 0pt minus 0pt}'. The
+following macro definitions accomplish the static allocation by giving
+symbolic names to the fixed positions. Static variable-size nodes appear
+in locations |mem_bot| through |lo_mem_stat_max|, and static single-word nodes
+appear in locations |hi_mem_stat_min| through |mem_top|, inclusive. It is
+harmless to let |lig_trick| and |garbage| share the same location of |mem|.
+
+@d zero_glue mem_bot /*specification for \.{0pt plus 0pt minus 0pt}*/
+@d fil_glue zero_glue+glue_spec_size /*\.{0pt plus 1fil minus 0pt}*/
+@d fill_glue fil_glue+glue_spec_size /*\.{0pt plus 1fill minus 0pt}*/
+@d ss_glue fill_glue+glue_spec_size /*\.{0pt plus 1fil minus 1fil}*/
+@d fil_neg_glue ss_glue+glue_spec_size /*\.{0pt plus -1fil minus 0pt}*/
+@d lo_mem_stat_max fil_neg_glue+glue_spec_size-1 /*largest statically
+ allocated word in the variable-size |mem|*/
+@#
+@d page_ins_head mem_top /*list of insertion data for current page*/
+@d contrib_head mem_top-1 /*vlist of items not yet on current page*/
+@d page_head mem_top-2 /*vlist for current page*/
+@d temp_head mem_top-3 /*head of a temporary list of some kind*/
+@d hold_head mem_top-4 /*head of a temporary list of another kind*/
+@d adjust_head mem_top-5 /*head of adjustment list returned by |hpack|*/
+@d active mem_top-7 /*head of active list in |line_break|, needs two words*/
+@d align_head mem_top-8 /*head of preamble list for alignments*/
+@d end_span mem_top-9 /*tail of spanned-width lists*/
+@d omit_template mem_top-10 /*a constant token list*/
+@d null_list mem_top-11 /*permanently empty list*/
+@d lig_trick mem_top-12 /*a ligature masquerading as a |char_node|*/
+@d garbage mem_top-12 /*used for scrap information*/
+@d backup_head mem_top-13 /*head of token list built by |scan_keyword|*/
+@d hi_mem_stat_min mem_top-13 /*smallest statically allocated word in
+ the one-word |mem|*/
+@d hi_mem_stat_usage 14 /*the number of one-word nodes always present*/
+
+@ The following code gets |mem| off to a good start, when \TeX\ is
+initializing itself the slow~way.
+
+@<Local variables for init...@>=
+int @!k; /*index into |mem|, |eqtb|, etc.*/
+
+@ @<Initialize table entries...@>=
+for (k=mem_bot+1; k<=lo_mem_stat_max; k++) mem[k].sc=0;
+ /*all glue dimensions are zeroed*/
+@^data structure assumptions@>
+k=mem_bot;@+while (k <= lo_mem_stat_max)
+ /*set first words of glue specifications*/
+ {@+glue_ref_count(k)=null+1;
+ stretch_order(k)=normal;shrink_order(k)=normal;
+ k=k+glue_spec_size;
+ }
+stretch(fil_glue)=unity;stretch_order(fil_glue)=fil;@/
+stretch(fill_glue)=unity;stretch_order(fill_glue)=fill;@/
+stretch(ss_glue)=unity;stretch_order(ss_glue)=fil;@/
+shrink(ss_glue)=unity;shrink_order(ss_glue)=fil;@/
+stretch(fil_neg_glue)=-unity;stretch_order(fil_neg_glue)=fil;@/
+rover=lo_mem_stat_max+1;
+link(rover)=empty_flag; /*now initialize the dynamic memory*/
+node_size(rover)=1000; /*which is a 1000-word available node*/
+llink(rover)=rover;rlink(rover)=rover;@/
+lo_mem_max=rover+1000;link(lo_mem_max)=null;info(lo_mem_max)=null;@/
+for (k=hi_mem_stat_min; k<=mem_top; k++)
+ mem[k]=mem[lo_mem_max]; /*clear list heads*/
+@<Initialize the special list heads and constant nodes@>;
+avail=null;mem_end=mem_top;
+hi_mem_min=hi_mem_stat_min; /*initialize the one-word memory*/
+var_used=lo_mem_stat_max+1-mem_bot;dyn_used=hi_mem_stat_usage;
+ /*initialize statistics*/
+
+@ If \TeX\ is extended improperly, the |mem| array might get screwed up.
+For example, some pointers might be wrong, or some ``dead'' nodes might not
+have been freed when the last reference to them disappeared. Procedures
+|check_mem| and |search_mem| are available to help diagnose such
+problems. These procedures make use of two arrays called |is_free| and
+|was_free| that are present only if \TeX's debugging routines have
+been included. (You may want to decrease the size of |mem| while you
+@^debugging@>
+are debugging.)
+
+@<Glob...@>=
+#ifdef @!DEBUG
+static bool @!is_free0[mem_max-mem_min+1],
+ *const @!is_free = @!is_free0-mem_min; /*free cells*/
+@t\hskip10pt@>static bool @!was_free0[mem_max-mem_min+1],
+ *const @!was_free = @!was_free0-mem_min;
+ /*previously free cells*/
+@t\hskip10pt@>static pointer @!was_mem_end, @!was_lo_max, @!was_hi_min;
+ /*previous |mem_end|, |lo_mem_max|, and |hi_mem_min|*/
+@t\hskip10pt@>static bool @!panicking; /*do we want to check memory constantly?*/
+#endif
+
+@ @<Set initial...@>=
+#ifdef @!DEBUG
+was_mem_end=mem_min; /*indicate that everything was previously free*/
+was_lo_max=mem_min;was_hi_min=mem_max;
+panicking=false;
+#endif
+
+@ Procedure |check_mem| makes sure that the available space lists of
+|mem| are well formed, and it optionally prints out all locations
+that are reserved now but were free the last time this procedure was called.
+
+@p
+#ifdef @!DEBUG
+static void check_mem(bool @!print_locs)
+{@+ /*loop exits*/
+int p, @!q; /*current locations of interest in |mem|*/
+bool @!clobbered; /*is something amiss?*/
+for (p=mem_min; p<=lo_mem_max; p++) is_free[p]=false; /*you can probably
+ do this faster*/
+for (p=hi_mem_min; p<=mem_end; p++) is_free[p]=false; /*ditto*/
+@<Check single-word |avail| list@>;
+@<Check variable-size |avail| list@>;
+@<Check flags of unavailable nodes@>;
+if (print_locs) @<Print newly busy locations@>;
+for (p=mem_min; p<=lo_mem_max; p++) was_free[p]=is_free[p];
+for (p=hi_mem_min; p<=mem_end; p++) was_free[p]=is_free[p];
+ /*|was_free=is_free| might be faster*/
+was_mem_end=mem_end;was_lo_max=lo_mem_max;was_hi_min=hi_mem_min;
+}
+#endif
+
+@ @<Check single-word...@>=
+p=avail;q=null;clobbered=false;
+while (p!=null)
+ {@+if ((p > mem_end)||(p < hi_mem_min)) clobbered=true;
+ else if (is_free[p]) clobbered=true;
+ if (clobbered)
+ {@+print_nl("AVAIL list clobbered at ");
+@.AVAIL list clobbered...@>
+ print_int(q);goto done1;
+ }
+ is_free[p]=true;q=p;p=link(q);
+ }
+done1:
+
+@ @<Check variable-size...@>=
+p=rover;q=null;clobbered=false;
+@/do@+{if ((p >= lo_mem_max)||(p < mem_min)) clobbered=true;
+ else if ((rlink(p) >= lo_mem_max)||(rlink(p) < mem_min)) clobbered=true;
+ else if (!(is_empty(p))||(node_size(p) < 2)||@|
+ (p+node_size(p) > lo_mem_max)||@|(llink(rlink(p))!=p)) clobbered=true;
+ if (clobbered)
+ {@+print_nl("Double-AVAIL list clobbered at ");
+ print_int(q);goto done2;
+ }
+for (q=p; q<=p+node_size(p)-1; q++) /*mark all locations free*/
+ {@+if (is_free[q])
+ {@+print_nl("Doubly free location at ");
+@.Doubly free location...@>
+ print_int(q);goto done2;
+ }
+ is_free[q]=true;
+ }
+q=p;p=rlink(p);
+}@+ while (!(p==rover));
+done2:
+
+@ @<Check flags...@>=
+p=mem_min;
+while (p <= lo_mem_max) /*node |p| should not be empty*/
+ {@+if (is_empty(p))
+ {@+print_nl("Bad flag at ");print_int(p);
+@.Bad flag...@>
+ }
+ while ((p <= lo_mem_max)&&!is_free[p]) incr(p);
+ while ((p <= lo_mem_max)&&is_free[p]) incr(p);
+ }
+
+@ @<Print newly busy...@>=
+{@+print_nl("New busy locs:");
+for (p=mem_min; p<=lo_mem_max; p++)
+ if (!is_free[p]&&((p > was_lo_max)||was_free[p]))
+ {@+print_char(' ');print_int(p);
+ }
+for (p=hi_mem_min; p<=mem_end; p++)
+ if (!is_free[p]&&
+ ((p < was_hi_min)||(p > was_mem_end)||was_free[p]))
+ {@+print_char(' ');print_int(p);
+ }
+}
+
+@ The |search_mem| procedure attempts to answer the question ``Who points
+to node~|p|?'' In doing so, it fetches |link| and |info| fields of |mem|
+that might not be of type |two_halves|. Strictly speaking, this is
+@^dirty \PASCAL@>
+undefined in \PASCAL, and it can lead to ``false drops'' (words that seem to
+point to |p| purely by coincidence). But for debugging purposes, we want
+to rule out the places that do {\sl not\/} point to |p|, so a few false
+drops are tolerable.
+
+@p
+#ifdef @!DEBUG
+static void search_mem(pointer @!p) /*look for pointers to |p|*/
+{@+int q; /*current position being searched*/
+for (q=mem_min; q<=lo_mem_max; q++)
+ {@+if (link(q)==p)
+ {@+print_nl("LINK(");print_int(q);print_char(')');
+ }
+ if (info(q)==p)
+ {@+print_nl("INFO(");print_int(q);print_char(')');
+ }
+ }
+for (q=hi_mem_min; q<=mem_end; q++)
+ {@+if (link(q)==p)
+ {@+print_nl("LINK(");print_int(q);print_char(')');
+ }
+ if (info(q)==p)
+ {@+print_nl("INFO(");print_int(q);print_char(')');
+ }
+ }
+@<Search |eqtb| for equivalents equal to |p|@>;
+@<Search |save_stack| for equivalents that point to |p|@>;
+@<Search |hyph_list| for pointers to |p|@>;
+}
+#endif
+
+@* Displaying boxes.
+We can reinforce our knowledge of the data structures just introduced
+by considering two procedures that display a list in symbolic form.
+The first of these, called |short_display|, is used in ``overfull box''
+messages to give the top-level description of a list. The other one,
+called |show_node_list|, prints a detailed description of exactly what
+is in the data structure.
+
+The philosophy of |short_display| is to ignore the fine points about exactly
+what is inside boxes, except that ligatures and discretionary breaks are
+expanded. As a result, |short_display| is a recursive procedure, but the
+recursion is never more than one level deep.
+@^recursion@>
+
+A global variable |font_in_short_display| keeps track of the font code that
+is assumed to be present when |short_display| begins; deviations from this
+font will be printed.
+
+@<Glob...@>=
+static int @!font_in_short_display; /*an internal font number*/
+
+@ Boxes, rules, inserts, whatsits, marks, and things in general that are
+sort of ``complicated'' are indicated only by printing `\.{[]}'.
+
+@p static void short_display(int @!p) /*prints highlights of list |p|*/
+{@+int n; /*for replacement counts*/
+while (p > mem_min)
+ {@+if (is_char_node(p))
+ {@+if (p <= mem_end)
+ {@+if (font(p)!=font_in_short_display)
+ {@+if ((font(p) < font_base)||(font(p) > font_max))
+ print_char('*');
+@.*\relax@>
+ else@<Print the font identifier for |font(p)|@>;
+ print_char(' ');font_in_short_display=font(p);
+ }
+ print_ASCII(qo(character(p)));
+ }
+ }
+ else@<Print a short indication of the contents of node |p|@>;
+ p=link(p);
+ }
+}
+
+@ @<Print a short indication of the contents of node |p|@>=
+switch (type(p)) {
+case hlist_node: case vlist_node: case ins_node:
+ case whatsit_node: case mark_node: case adjust_node:
+ case unset_node: print("[]");@+break;
+case rule_node: print_char('|');@+break;
+case glue_node: if (glue_ptr(p)!=zero_glue) print_char(' ');@+break;
+case math_node: print_char('$');@+break;
+case ligature_node: short_display(lig_ptr(p));@+break;
+case disc_node: {@+short_display(pre_break(p));
+ short_display(post_break(p));@/
+ n=replace_count(p);
+ while (n > 0)
+ {@+if (link(p)!=null) p=link(p);
+ decr(n);
+ }
+ } @+break;
+default:do_nothing;
+}
+
+@ The |show_node_list| routine requires some auxiliary subroutines: one to
+print a font-and-character combination, one to print a token list without
+its reference count, and one to print a rule dimension.
+
+@p static void print_font_and_char(int @!p) /*prints |char_node| data*/
+{@+if (p > mem_end) print_esc("CLOBBERED.");
+else{@+if ((font(p) < font_base)||(font(p) > font_max)) print_char('*');
+@.*\relax@>
+ else@<Print the font identifier for |font(p)|@>;
+ print_char(' ');print_ASCII(qo(character(p)));
+ }
+}
+@#
+static void print_mark(int @!p) /*prints token list data in braces*/
+{@+print_char('{');
+if ((p < hi_mem_min)||(p > mem_end)) print_esc("CLOBBERED.");
+else show_token_list(link(p), null, max_print_line-10);
+print_char('}');
+}
+@#
+static void print_rule_dimen(scaled @!d) /*prints dimension in rule node*/
+{@+if (is_running(d)) print_char('*');else print_scaled(d);
+@.*\relax@>
+}
+
+@ Then there is a subroutine that prints glue stretch and shrink, possibly
+followed by the name of finite units:
+
+@p static void print_glue(scaled @!d, int @!order, char *@!s)
+ /*prints a glue component*/
+{@+print_scaled(d);
+if ((order < normal)||(order > filll)) print("foul");
+else if (order > normal)
+ {@+print("fil");
+ while (order > fil)
+ {@+print_char('l');decr(order);
+ }
+ }
+else if (s!=0) print(s);
+}
+
+@ The next subroutine prints a whole glue specification.
+
+@p static void print_spec(int @!p, char *@!s)
+ /*prints a glue specification*/
+{@+if ((p < mem_min)||(p >= lo_mem_max)) print_char('*');
+@.*\relax@>
+else{@+print_scaled(width(p));
+ if (s!=0) print(s);
+ if (stretch(p)!=0)
+ {@+print(" plus ");print_glue(stretch(p), stretch_order(p), s);
+ }
+ if (shrink(p)!=0)
+ {@+print(" minus ");print_glue(shrink(p), shrink_order(p), s);
+ }
+ }
+}
+
+@ We also need to declare some procedures that appear later in this
+documentation.
+
+@p @<Declare procedures needed for displaying the elements of mlists@>@;
+@<Declare the procedure called |print_skip_param|@>@;
+
+@ Since boxes can be inside of boxes, |show_node_list| is inherently recursive,
+@^recursion@>
+up to a given maximum number of levels. The history of nesting is indicated
+by the current string, which will be printed at the beginning of each line;
+the length of this string, namely |cur_length|, is the depth of nesting.
+
+Recursive calls on |show_node_list| therefore use the following pattern:
+
+@d node_list_display(A)
+ {@+append_char('.');show_node_list(A);flush_char;
+ } /*|str_room| need not be checked; see |show_box| below*/
+
+@ A global variable called |depth_threshold| is used to record the maximum
+depth of nesting for which |show_node_list| will show information. If we
+have |depth_threshold==0|, for example, only the top level information will
+be given and no sublists will be traversed. Another global variable, called
+|breadth_max|, tells the maximum number of items to show at each level;
+|breadth_max| had better be positive, or you won't see anything.
+
+@<Glob...@>=
+static int @!depth_threshold; /*maximum nesting depth in box displays*/
+static int @!breadth_max; /*maximum number of items shown at the same list level*/
+
+@ Now we are ready for |show_node_list| itself. This procedure has been
+written to be ``extra robust'' in the sense that it should not crash or get
+into a loop even if the data structures have been messed up by bugs in
+the rest of the program. You can safely call its parent routine
+|show_box(p)| for arbitrary values of |p| when you are debugging \TeX.
+However, in the presence of bad data, the procedure may
+@^dirty \PASCAL@>@^debugging@>
+fetch a |memory_word| whose variant is different from the way it was stored;
+for example, it might try to read |mem[p].hh| when |mem[p]|
+contains a scaled integer, if |p| is a pointer that has been
+clobbered or chosen at random.
+
+@p static void show_node_list(int @!p) /*prints a node list symbolically*/
+{@+
+int n; /*the number of items already printed at this level*/
+double @!g; /*a glue ratio, as a floating point number*/
+if (cur_length > depth_threshold)
+ {@+if (p > null) print(" []");
+ /*indicate that there's been some truncation*/
+ return;
+ }
+n=0;
+while (p > mem_min)
+ {@+print_ln();print_current_string(); /*display the nesting history*/
+ if (p > mem_end) /*pointer out of range*/
+ {@+print("Bad link, display aborted.");return;
+@.Bad link...@>
+ }
+ incr(n);if (n > breadth_max) /*time to stop*/
+ {@+print("etc.");return;
+@.etc@>
+ }
+ @<Display node |p|@>;
+ p=link(p);
+ }
+
+}
+
+@ @<Display node |p|@>=
+if (is_char_node(p)) print_font_and_char(p);
+else switch (type(p)) {
+ case hlist_node: case vlist_node: case unset_node: @<Display box |p|@>@;@+break;
+ case rule_node: @<Display rule |p|@>@;@+break;
+ case ins_node: @<Display insertion |p|@>@;@+break;
+ case whatsit_node: @<Display the whatsit node |p|@>@;@+break;
+ case glue_node: @<Display glue |p|@>@;@+break;
+ case kern_node: @<Display kern |p|@>@;@+break;
+ case math_node: @<Display math node |p|@>@;@+break;
+ case ligature_node: @<Display ligature |p|@>@;@+break;
+ case penalty_node: @<Display penalty |p|@>@;@+break;
+ case disc_node: @<Display discretionary |p|@>@;@+break;
+ case mark_node: @<Display mark |p|@>@;@+break;
+ case adjust_node: @<Display adjustment |p|@>@;@+break;
+ @t\4@>@<Cases of |show_node_list| that arise in mlists only@>@;
+ default:print("Unknown node type!");
+ }
+
+@ @<Display box |p|@>=
+{@+if (type(p)==hlist_node) print_esc("h");
+else if (type(p)==vlist_node) print_esc("v");
+else print_esc("unset");
+print("box(");print_scaled(height(p));print_char('+');
+print_scaled(depth(p));print(")x");print_scaled(width(p));
+if (type(p)==unset_node)
+ @<Display special fields of the unset node |p|@>@;
+else{@+@<Display the value of |glue_set(p)|@>;
+ if (shift_amount(p)!=0)
+ {@+print(", shifted ");print_scaled(shift_amount(p));
+ }
+ }
+node_list_display(list_ptr(p)); /*recursive call*/
+}
+
+@ @<Display special fields of the unset node |p|@>=
+{@+if (span_count(p)!=min_quarterword)
+ {@+print(" (");print_int(qo(span_count(p))+1);
+ print(" columns)");
+ }
+if (glue_stretch(p)!=0)
+ {@+print(", stretch ");print_glue(glue_stretch(p), glue_order(p), 0);
+ }
+if (glue_shrink(p)!=0)
+ {@+print(", shrink ");print_glue(glue_shrink(p), glue_sign(p), 0);
+ }
+}
+
+@ The code will have to change in this place if |glue_ratio| is
+a structured type instead of an ordinary |double|. Note that this routine
+should avoid arithmetic errors even if the |glue_set| field holds an
+arbitrary random value. The following code assumes that a properly
+formed nonzero |double| number has absolute value $2^{20}$ or more when
+it is regarded as an integer; this precaution was adequate to prevent
+floating point underflow on the author's computer.
+@^system dependencies@>
+@^dirty \PASCAL@>
+
+@<Display the value of |glue_set(p)|@>=
+g=unfix(glue_set(p));
+if ((g!=float_constant(0))&&(glue_sign(p)!=normal))
+ {@+print(", glue set ");
+ if (glue_sign(p)==shrinking) print("- ");
+ if (abs(mem[p+glue_offset].i) < 04000000) print("?.?");
+ else if (abs(g) > float_constant(20000))
+ {@+if (g > float_constant(0)) print_char('>');
+ else print("< -");
+ print_glue(20000*unity, glue_order(p), 0);
+ }
+ else print_glue(round(unity*g), glue_order(p), 0);
+@^real multiplication@>
+ }
+
+@ @<Display rule |p|@>=
+{@+print_esc("rule(");print_rule_dimen(height(p));print_char('+');
+print_rule_dimen(depth(p));print(")x");print_rule_dimen(width(p));
+}
+
+@ @<Display insertion |p|@>=
+{@+print_esc("insert");print_int(qo(subtype(p)));
+print(", natural size ");print_scaled(height(p));
+print("; split(");print_spec(split_top_ptr(p), 0);
+print_char(',');print_scaled(depth(p));
+print("); float cost ");print_int(float_cost(p));
+node_list_display(ins_ptr(p)); /*recursive call*/
+}
+
+@ @<Display glue |p|@>=
+if (subtype(p) >= a_leaders) @<Display leaders |p|@>@;
+else{@+print_esc("glue");
+ if (subtype(p)!=normal)
+ {@+print_char('(');
+ if (subtype(p) < cond_math_glue)
+ print_skip_param(subtype(p)-1);
+ else if (subtype(p)==cond_math_glue) print_esc("nonscript");
+ else print_esc("mskip");
+ print_char(')');
+ }
+ if (subtype(p)!=cond_math_glue)
+ {@+print_char(' ');
+ if (subtype(p) < cond_math_glue) print_spec(glue_ptr(p), 0);
+ else print_spec(glue_ptr(p),"mu");
+ }
+ }
+
+@ @<Display leaders |p|@>=
+{@+print_esc("");
+if (subtype(p)==c_leaders) print_char('c');
+else if (subtype(p)==x_leaders) print_char('x');
+print("leaders ");print_spec(glue_ptr(p), 0);
+node_list_display(leader_ptr(p)); /*recursive call*/
+}
+
+@ An ``explicit'' kern value is indicated implicitly by an explicit space.
+
+@<Display kern |p|@>=
+if (subtype(p)!=mu_glue)
+ {@+print_esc("kern");
+ if (subtype(p)!=normal) print_char(' ');
+ print_scaled(width(p));
+ if (subtype(p)==acc_kern) print(" (for accent)");
+@.for accent@>
+ }
+else{@+print_esc("mkern");print_scaled(width(p));print("mu");
+ }
+
+@ @<Display math node |p|@>=
+{@+print_esc("math");
+if (subtype(p)==before) print("on");
+else print("off");
+if (width(p)!=0)
+ {@+print(", surrounded ");print_scaled(width(p));
+ }
+}
+
+@ @<Display ligature |p|@>=
+{@+print_font_and_char(lig_char(p));print(" (ligature ");
+if (subtype(p) > 1) print_char('|');
+font_in_short_display=font(lig_char(p));short_display(lig_ptr(p));
+if (odd(subtype(p))) print_char('|');
+print_char(')');
+}
+
+@ @<Display penalty |p|@>=
+{@+print_esc("penalty ");print_int(penalty(p));
+}
+
+@ The |post_break| list of a discretionary node is indicated by a prefixed
+`\.{\char'174}' instead of the `\..' before the |pre_break| list.
+
+@<Display discretionary |p|@>=
+{@+print_esc("discretionary");
+if (replace_count(p) > 0)
+ {@+print(" replacing ");print_int(replace_count(p));
+ }
+node_list_display(pre_break(p)); /*recursive call*/
+append_char('|');show_node_list(post_break(p));flush_char; /*recursive call*/
+}
+
+@ @<Display mark |p|@>=
+{@+print_esc("mark");
+if (mark_class(p)!=0)
+ {@+print_char('s');print_int(mark_class(p));
+ }
+print_mark(mark_ptr(p));
+}
+
+@ @<Display adjustment |p|@>=
+{@+print_esc("vadjust");node_list_display(adjust_ptr(p)); /*recursive call*/
+}
+
+@ The recursive machinery is started by calling |show_box|.
+@^recursion@>
+
+@p static void show_box(pointer @!p)
+{@+@<Assign the values |depth_threshold:=show_box_depth| and |breadth_max:=show_box_breadth|@>;
+if (breadth_max <= 0) breadth_max=5;
+if (pool_ptr+depth_threshold >= pool_size)
+ depth_threshold=pool_size-pool_ptr-1;
+ /*now there's enough room for prefix string*/
+show_node_list(p); /*the show starts at |p|*/
+print_ln();
+}
+
+@* Destroying boxes.
+When we are done with a node list, we are obliged to return it to free
+storage, including all of its sublists. The recursive procedure
+|flush_node_list| does this for us.
+
+@ First, however, we shall consider two non-recursive procedures that do
+simpler tasks. The first of these, |delete_token_ref|, is called when
+a pointer to a token list's reference count is being removed. This means
+that the token list should disappear if the reference count was |null|,
+otherwise the count should be decreased by one.
+@^reference counts@>
+
+@d token_ref_count(A) info(A) /*reference count preceding a token list*/
+
+@p static void delete_token_ref(pointer @!p) /*|p| points to the reference count
+ of a token list that is losing one reference*/
+{@+if (token_ref_count(p)==null) flush_list(p);
+else decr(token_ref_count(p));
+}
+
+@ Similarly, |delete_glue_ref| is called when a pointer to a glue
+specification is being withdrawn.
+@^reference counts@>
+@d fast_delete_glue_ref(A) @t@>@;@/
+ {@+if (glue_ref_count(A)==null) free_node(A, glue_spec_size);
+ else decr(glue_ref_count(A));
+ }
+
+@p static void delete_glue_ref(pointer @!p) /*|p| points to a glue specification*/
+fast_delete_glue_ref(p)
+
+@ Now we are ready to delete any node list, recursively.
+In practice, the nodes deleted are usually charnodes (about 2/3 of the time),
+and they are glue nodes in about half of the remaining cases.
+@^recursion@>
+
+@p static void flush_node_list(pointer @!p) /*erase list of nodes starting at |p|*/
+{@+ /*go here when node |p| has been freed*/
+pointer q; /*successor to node |p|*/
+while (p!=null)
+@^inner loop@>
+ {@+q=link(p);
+ if (is_char_node(p)) free_avail(p)@;
+ else{@+switch (type(p)) {
+ case hlist_node: case vlist_node:
+ case unset_node: {@+flush_node_list(list_ptr(p));
+ free_node(p, box_node_size);goto done;
+ }
+ case rule_node: {@+free_node(p, rule_node_size);goto done;
+ }
+ case ins_node: {@+flush_node_list(ins_ptr(p));
+ delete_glue_ref(split_top_ptr(p));
+ free_node(p, ins_node_size);goto done;
+ }
+ case whatsit_node: @<Wipe out the whatsit node |p| and |goto done|@>@;
+ case glue_node: {@+fast_delete_glue_ref(glue_ptr(p));
+ if (leader_ptr(p)!=null) flush_node_list(leader_ptr(p));
+ } @+break;
+ case kern_node: case math_node: case penalty_node: do_nothing;@+break;
+ case ligature_node: flush_node_list(lig_ptr(p));@+break;
+ case mark_node: delete_token_ref(mark_ptr(p));@+break;
+ case disc_node: {@+flush_node_list(pre_break(p));
+ flush_node_list(post_break(p));
+ } @+break;
+ case adjust_node: flush_node_list(adjust_ptr(p));@+break;
+ @t\4@>@<Cases of |flush_node_list| that arise in mlists only@>@;
+ default:confusion("flushing");
+@:this can't happen flushing}{\quad flushing@>
+ } @/
+ free_node(p, small_node_size);
+ done: ;}
+ p=q;
+ }
+}
+
+@* Copying boxes.
+Another recursive operation that acts on boxes is sometimes needed: The
+procedure |copy_node_list| returns a pointer to another node list that has
+the same structure and meaning as the original. Note that since glue
+specifications and token lists have reference counts, we need not make
+copies of them. Reference counts can never get too large to fit in a
+halfword, since each pointer to a node is in a different memory address,
+and the total number of memory addresses fits in a halfword.
+@^recursion@>
+@^reference counts@>
+
+(Well, there actually are also references from outside |mem|; if the
+|save_stack| is made arbitrarily large, it would theoretically be possible
+to break \TeX\ by overflowing a reference count. But who would want to do that?)
+
+@d add_token_ref(A) incr(token_ref_count(A)) /*new reference to a token list*/
+@d add_glue_ref(A) incr(glue_ref_count(A)) /*new reference to a glue spec*/
+
+@ The copying procedure copies words en masse without bothering
+to look at their individual fields. If the node format changes---for
+example, if the size is altered, or if some link field is moved to another
+relative position---then this code may need to be changed too.
+@^data structure assumptions@>
+
+@p static pointer copy_node_list(pointer @!p) /*makes a duplicate of the
+ node list that starts at |p| and returns a pointer to the new list*/
+{@+pointer h; /*temporary head of copied list*/
+pointer @!q; /*previous position in new list*/
+pointer @!r; /*current node being fabricated for new list*/
+int @!words; /*number of words remaining to be copied*/
+h=get_avail();q=h;
+while (p!=null)
+ {@+@<Make a copy of node |p| in node |r|@>;
+ link(q)=r;q=r;p=link(p);
+ }
+link(q)=null;q=link(h);free_avail(h);
+return q;
+}
+
+@ @<Make a copy of node |p|...@>=
+words=1; /*this setting occurs in more branches than any other*/
+if (is_char_node(p)) r=get_avail();
+else@<Case statement to copy different types and set |words| to the number
+of initial words not yet copied@>;
+while (words > 0)
+ {@+decr(words);mem[r+words]=mem[p+words];
+ }
+
+@ @<Case statement to copy...@>=
+switch (type(p)) {
+case hlist_node: case vlist_node: case unset_node: {@+r=get_node(box_node_size);
+ mem[r+6]=mem[p+6];mem[r+5]=mem[p+5]; /*copy the last two words*/
+ list_ptr(r)=copy_node_list(list_ptr(p)); /*this affects |mem[r+5]|*/
+ words=5;
+ } @+break;
+case rule_node: {@+r=get_node(rule_node_size);words=rule_node_size;
+ } @+break;
+case ins_node: {@+r=get_node(ins_node_size);mem[r+4]=mem[p+4];
+ add_glue_ref(split_top_ptr(p));
+ ins_ptr(r)=copy_node_list(ins_ptr(p)); /*this affects |mem[r+4]|*/
+ words=ins_node_size-1;
+ } @+break;
+case whatsit_node: @<Make a partial copy of the whatsit node |p| and make
+|r| point to it; set |words| to the number of initial words not yet copied@>@;@+break;
+case glue_node: {@+r=get_node(small_node_size);add_glue_ref(glue_ptr(p));
+ glue_ptr(r)=glue_ptr(p);leader_ptr(r)=copy_node_list(leader_ptr(p));
+ } @+break;
+case kern_node: case math_node: case penalty_node: {@+r=get_node(small_node_size);
+ words=small_node_size;
+ } @+break;
+case ligature_node: {@+r=get_node(small_node_size);
+ mem[lig_char(r)]=mem[lig_char(p)]; /*copy |font| and |character|*/
+ lig_ptr(r)=copy_node_list(lig_ptr(p));
+ } @+break;
+case disc_node: {@+r=get_node(small_node_size);
+ pre_break(r)=copy_node_list(pre_break(p));
+ post_break(r)=copy_node_list(post_break(p));
+ } @+break;
+case mark_node: {@+r=get_node(small_node_size);add_token_ref(mark_ptr(p));
+ words=small_node_size;
+ } @+break;
+case adjust_node: {@+r=get_node(small_node_size);
+ adjust_ptr(r)=copy_node_list(adjust_ptr(p));
+ } @+break; /*|words==1==small_node_size-1|*/
+default:confusion("copying");
+@:this can't happen copying}{\quad copying@>
+}
+
+@* The command codes.
+Before we can go any further, we need to define symbolic names for the internal
+code numbers that represent the various commands obeyed by \TeX. These codes
+are somewhat arbitrary, but not completely so. For example, the command
+codes for character types are fixed by the language, since a user says,
+e.g., `\.{\\catcode \`\\\${} = 3}' to make \.{\char'44} a math delimiter,
+and the command code |math_shift| is equal to~3. Some other codes have
+been made adjacent so that |case| statements in the program need not consider
+cases that are widely spaced, or so that |case| statements can be replaced
+by |if| statements.
+
+At any rate, here is the list, for future reference. First come the
+``catcode'' commands, several of which share their numeric codes with
+ordinary commands when the catcode cannot emerge from \TeX's scanning routine.
+
+@d escape 0 /*escape delimiter (called \.\\ in {\sl The \TeX book\/})*/
+@:TeXbook}{\sl The \TeX book@>
+@d relax 0 /*do nothing ( \.{\\relax} )*/
+@d left_brace 1 /*beginning of a group ( \.\{ )*/
+@d right_brace 2 /*ending of a group ( \.\} )*/
+@d math_shift 3 /*mathematics shift character ( \.\$ )*/
+@d tab_mark 4 /*alignment delimiter ( \.\&, \.{\\span} )*/
+@d car_ret 5 /*end of line ( |carriage_return|, \.{\\cr}, \.{\\crcr} )*/
+@d out_param 5 /*output a macro parameter*/
+@d mac_param 6 /*macro parameter symbol ( \.\# )*/
+@d sup_mark 7 /*superscript ( \.{\char'136} )*/
+@d sub_mark 8 /*subscript ( \.{\char'137} )*/
+@d ignore 9 /*characters to ignore ( \.{\^\^@@} )*/
+@d endv 9 /*end of \<v_j> list in alignment template*/
+@d spacer 10 /*characters equivalent to blank space ( \.{\ } )*/
+@d letter 11 /*characters regarded as letters ( \.{A..Z}, \.{a..z} )*/
+@d other_char 12 /*none of the special character types*/
+@d active_char 13 /*characters that invoke macros ( \.{\char`\~} )*/
+@d par_end 13 /*end of paragraph ( \.{\\par} )*/
+@d match 13 /*match a macro parameter*/
+@d comment 14 /*characters that introduce comments ( \.\% )*/
+@d end_match 14 /*end of parameters to macro*/
+@d stop 14 /*end of job ( \.{\\end}, \.{\\dump} )*/
+@d invalid_char 15 /*characters that shouldn't appear ( \.{\^\^?} )*/
+@d delim_num 15 /*specify delimiter numerically ( \.{\\delimiter} )*/
+@d max_char_code 15 /*largest catcode for individual characters*/
+
+@ Next are the ordinary run-of-the-mill command codes. Codes that are
+|min_internal| or more represent internal quantities that might be
+expanded by `\.{\\the}'.
+
+@d char_num 16 /*character specified numerically ( \.{\\char} )*/
+@d math_char_num 17 /*explicit math code ( \.{\\mathchar} )*/
+@d mark 18 /*mark definition ( \.{\\mark} )*/
+@d xray 19 /*peek inside of \TeX\ ( \.{\\show}, \.{\\showbox}, etc.~)*/
+@d make_box 20 /*make a box ( \.{\\box}, \.{\\copy}, \.{\\hbox}, etc.~)*/
+@d hmove 21 /*horizontal motion ( \.{\\moveleft}, \.{\\moveright} )*/
+@d vmove 22 /*vertical motion ( \.{\\raise}, \.{\\lower} )*/
+@d un_hbox 23 /*unglue a box ( \.{\\unhbox}, \.{\\unhcopy} )*/
+@d un_vbox 24 /*unglue a box ( \.{\\unvbox}, \.{\\unvcopy} )*/
+ /*( or \.{\\pagediscards}, \.{\\splitdiscards} )*/
+@d remove_item 25 /*nullify last item ( \.{\\unpenalty},
+ \.{\\unkern}, \.{\\unskip} )*/
+@d hskip 26 /*horizontal glue ( \.{\\hskip}, \.{\\hfil}, etc.~)*/
+@d vskip 27 /*vertical glue ( \.{\\vskip}, \.{\\vfil}, etc.~)*/
+@d mskip 28 /*math glue ( \.{\\mskip} )*/
+@d kern 29 /*fixed space ( \.{\\kern} )*/
+@d mkern 30 /*math kern ( \.{\\mkern} )*/
+@d leader_ship 31 /*use a box ( \.{\\shipout}, \.{\\leaders}, etc.~)*/
+@d halign 32 /*horizontal table alignment ( \.{\\halign} )*/
+@d valign 33 /*vertical table alignment ( \.{\\valign} )*/
+@d no_align 34 /*temporary escape from alignment ( \.{\\noalign} )*/
+@d vrule 35 /*vertical rule ( \.{\\vrule} )*/
+@d hrule 36 /*horizontal rule ( \.{\\hrule} )*/
+@d insert 37 /*vlist inserted in box ( \.{\\insert} )*/
+@d vadjust 38 /*vlist inserted in enclosing paragraph ( \.{\\vadjust} )*/
+@d ignore_spaces 39 /*gobble |spacer| tokens ( \.{\\ignorespaces} )*/
+@d after_assignment 40 /*save till assignment is done ( \.{\\afterassignment} )*/
+@d after_group 41 /*save till group is done ( \.{\\aftergroup} )*/
+@d break_penalty 42 /*additional badness ( \.{\\penalty} )*/
+@d start_par 43 /*begin paragraph ( \.{\\indent}, \.{\\noindent} )*/
+@d ital_corr 44 /*italic correction ( \.{\\/} )*/
+@d accent 45 /*attach accent in text ( \.{\\accent} )*/
+@d math_accent 46 /*attach accent in math ( \.{\\mathaccent} )*/
+@d discretionary 47 /*discretionary texts ( \.{\\-}, \.{\\discretionary} )*/
+@d eq_no 48 /*equation number ( \.{\\eqno}, \.{\\leqno} )*/
+@d left_right 49 /*variable delimiter ( \.{\\left}, \.{\\right} )*/
+ /*( or \.{\\middle} )*/
+@d math_comp 50 /*component of formula ( \.{\\mathbin}, etc.~)*/
+@d limit_switch 51 /*diddle limit conventions ( \.{\\displaylimits}, etc.~)*/
+@d above 52 /*generalized fraction ( \.{\\above}, \.{\\atop}, etc.~)*/
+@d math_style 53 /*style specification ( \.{\\displaystyle}, etc.~)*/
+@d math_choice 54 /*choice specification ( \.{\\mathchoice} )*/
+@d non_script 55 /*conditional math glue ( \.{\\nonscript} )*/
+@d vcenter 56 /*vertically center a vbox ( \.{\\vcenter} )*/
+@d case_shift 57 /*force specific case ( \.{\\lowercase}, \.{\\uppercase}~)*/
+@d message 58 /*send to user ( \.{\\message}, \.{\\errmessage} )*/
+@d extension 59 /*extensions to \TeX\ ( \.{\\write}, \.{\\special}, etc.~)*/
+@d in_stream 60 /*files for reading ( \.{\\openin}, \.{\\closein} )*/
+@d begin_group 61 /*begin local grouping ( \.{\\begingroup} )*/
+@d end_group 62 /*end local grouping ( \.{\\endgroup} )*/
+@d omit 63 /*omit alignment template ( \.{\\omit} )*/
+@d ex_space 64 /*explicit space ( \.{\\\ } )*/
+@d no_boundary 65 /*suppress boundary ligatures ( \.{\\noboundary} )*/
+@d radical 66 /*square root and similar signs ( \.{\\radical} )*/
+@d end_cs_name 67 /*end control sequence ( \.{\\endcsname} )*/
+@d min_internal 68 /*the smallest code that can follow \.{\\the}*/
+@d char_given 68 /*character code defined by \.{\\chardef}*/
+@d math_given 69 /*math code defined by \.{\\mathchardef}*/
+@d last_item 70 /*most recent item ( \.{\\lastpenalty},
+ \.{\\lastkern}, \.{\\lastskip} )*/
+@d max_non_prefixed_command 70 /*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 71 /*token list register ( \.{\\toks} )*/
+@d assign_toks 72 /*special token list ( \.{\\output}, \.{\\everypar}, etc.~)*/
+@d assign_int 73 /*user-defined integer ( \.{\\tolerance}, \.{\\day}, etc.~)*/
+@d assign_dimen 74 /*user-defined length ( \.{\\hsize}, etc.~)*/
+@d assign_glue 75 /*user-defined glue ( \.{\\baselineskip}, etc.~)*/
+@d assign_mu_glue 76 /*user-defined muglue ( \.{\\thinmuskip}, etc.~)*/
+@d assign_font_dimen 77 /*user-defined font dimension ( \.{\\fontdimen} )*/
+@d assign_font_int 78 /*user-defined font integer ( \.{\\hyphenchar},
+ \.{\\skewchar} )*/
+@d set_aux 79 /*specify state info ( \.{\\spacefactor}, \.{\\prevdepth} )*/
+@d set_prev_graf 80 /*specify state info ( \.{\\prevgraf} )*/
+@d set_page_dimen 81 /*specify state info ( \.{\\pagegoal}, etc.~)*/
+@d set_page_int 82 /*specify state info ( \.{\\deadcycles},
+ \.{\\insertpenalties} )*/
+ /*( or \.{\\interactionmode} )*/
+@d set_box_dimen 83 /*change dimension of box ( \.{\\wd}, \.{\\ht}, \.{\\dp} )*/
+@d set_shape 84 /*specify fancy paragraph shape ( \.{\\parshape} )*/
+ /*(or \.{\\interlinepenalties}, etc.~)*/
+@d def_code 85 /*define a character code ( \.{\\catcode}, etc.~)*/
+@d def_family 86 /*declare math fonts ( \.{\\textfont}, etc.~)*/
+@d set_font 87 /*set current font ( font identifiers )*/
+@d def_font 88 /*define a font file ( \.{\\font} )*/
+@d internal_register 89 /*internal register ( \.{\\count}, \.{\\dimen}, etc.~)*/
+@d max_internal 89 /*the largest code that can follow \.{\\the}*/
+@d advance 90 /*advance a register or parameter ( \.{\\advance} )*/
+@d multiply 91 /*multiply a register or parameter ( \.{\\multiply} )*/
+@d divide 92 /*divide a register or parameter ( \.{\\divide} )*/
+@d prefix 93 /*qualify a definition ( \.{\\global}, \.{\\long}, \.{\\outer} )*/
+ /*( or \.{\\protected} )*/
+@d let 94 /*assign a command code ( \.{\\let}, \.{\\futurelet} )*/
+@d shorthand_def 95 /*code definition ( \.{\\chardef}, \.{\\countdef}, etc.~)*/
+@d read_to_cs 96 /*read into a control sequence ( \.{\\read} )*/
+ /*( or \.{\\readline} )*/
+@d def 97 /*macro definition ( \.{\\def}, \.{\\gdef}, \.{\\xdef}, \.{\\edef} )*/
+@d set_box 98 /*set a box ( \.{\\setbox} )*/
+@d hyph_data 99 /*hyphenation data ( \.{\\hyphenation}, \.{\\patterns} )*/
+@d set_interaction 100 /*define level of interaction ( \.{\\batchmode}, etc.~)*/
+@d max_command 100 /*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
+than |max_command| so that their special nature is easily discernible.
+The ``expandable'' commands come first.
+
+@d undefined_cs (max_command+1) /*initial state of most |eq_type| fields*/
+@d expand_after (max_command+2) /*special expansion ( \.{\\expandafter} )*/
+@d no_expand (max_command+3) /*special nonexpansion ( \.{\\noexpand} )*/
+@d input (max_command+4) /*input a source file ( \.{\\input}, \.{\\endinput} )*/
+ /*( or \.{\\scantokens} )*/
+@d if_test (max_command+5) /*conditional text ( \.{\\if}, \.{\\ifcase}, etc.~)*/
+@d fi_or_else (max_command+6) /*delimiters for conditionals ( \.{\\else}, etc.~)*/
+@d cs_name (max_command+7) /*make a control sequence from tokens ( \.{\\csname} )*/
+@d convert (max_command+8) /*convert to text ( \.{\\number}, \.{\\string}, etc.~)*/
+@d the (max_command+9) /*expand an internal quantity ( \.{\\the} )*/
+ /*( or \.{\\unexpanded}, \.{\\detokenize} )*/
+@d top_bot_mark (max_command+10) /*inserted mark ( \.{\\topmark}, etc.~)*/
+@d call (max_command+11) /*non-long, non-outer control sequence*/
+@d long_call (max_command+12) /*long, non-outer control sequence*/
+@d outer_call (max_command+13) /*non-long, outer control sequence*/
+@d long_outer_call (max_command+14) /*long, outer control sequence*/
+@d end_template (max_command+15) /*end of an alignment template*/
+@d dont_expand (max_command+16) /*the following token was marked by \.{\\noexpand}*/
+@d glue_ref (max_command+17) /*the equivalent points to a glue specification*/
+@d shape_ref (max_command+18) /*the equivalent points to a parshape specification*/
+@d box_ref (max_command+19) /*the equivalent points to a box node, or is |null|*/
+@d data (max_command+20) /*the equivalent is simply a halfword number*/
+
+@* The semantic nest.
+\TeX\ is typically in the midst of building many lists at once. For example,
+when a math formula is being processed, \TeX\ is in math mode and
+working on an mlist; this formula has temporarily interrupted \TeX\ from
+being in horizontal mode and building the hlist of a paragraph; and this
+paragraph has temporarily interrupted \TeX\ from being in vertical mode
+and building the vlist for the next page of a document. Similarly, when a
+\.{\\vbox} occurs inside of an \.{\\hbox}, \TeX\ is temporarily
+interrupted from working in restricted horizontal mode, and it enters
+internal vertical mode. The ``semantic nest'' is a stack that
+keeps track of what lists and modes are currently suspended.
+
+At each level of processing we are in one of six modes:
+
+\yskip\hang|vmode| stands for vertical mode (the page builder);
+
+\hang|hmode| stands for horizontal mode (the paragraph builder);
+
+\hang|mmode| stands for displayed formula mode;
+
+\hang|-vmode| stands for internal vertical mode (e.g., in a \.{\\vbox});
+
+\hang|-hmode| stands for restricted horizontal mode (e.g., in an \.{\\hbox});
+
+\hang|-mmode| stands for math formula mode (not displayed).
+
+\yskip\noindent The mode is temporarily set to zero while processing \.{\\write}
+texts.
+
+Numeric values are assigned to |vmode|, |hmode|, and |mmode| so that
+\TeX's ``big semantic switch'' can select the appropriate thing to
+do by computing the value |abs(mode)+cur_cmd|, where |mode| is the current
+mode and |cur_cmd| is the current command code.
+
+@d vmode 1 /*vertical mode*/
+@d hmode (vmode+max_command+1) /*horizontal mode*/
+@d mmode (hmode+max_command+1) /*math mode*/
+
+@p static void print_mode(int @!m) /*prints the mode represented by |m|*/
+{@+if (m > 0)
+ switch (m/(max_command+1)) {
+ case 0: print("vertical");@+break;
+ case 1: print("horizontal");@+break;
+ case 2: print("display math");
+ }
+else if (m==0) print("no");
+else switch ((-m)/(max_command+1)) {
+ case 0: print("internal vertical");@+break;
+ case 1: print("restricted horizontal");@+break;
+ case 2: print("math");
+ }
+print(" mode");
+}
+
+@ The state of affairs at any semantic level can be represented by
+five values:
+
+\yskip\hang|mode| is the number representing the semantic mode, as
+just explained.
+
+\yskip\hang|head| is a |pointer| to a list head for the list being built;
+|link(head)| therefore points to the first element of the list, or
+to |null| if the list is empty.
+
+\yskip\hang|tail| is a |pointer| to the final node of the list being
+built; thus, |tail==head| if and only if the list is empty.
+
+\yskip\hang|prev_graf| is the number of lines of the current paragraph that
+have already been put into the present vertical list.
+
+\yskip\hang|aux| is an auxiliary |memory_word| that gives further information
+that is needed to characterize the situation.
+
+\yskip\noindent
+In vertical mode, |aux| is also known as |prev_depth|; it is the scaled
+value representing the depth of the previous box, for use in baseline
+calculations, or it is | <= -1000|pt if the next box on the vertical list is to
+be exempt from baseline calculations. In horizontal mode, |aux| is also
+known as |space_factor| and |clang|; it holds the current space factor used in
+spacing calculations, and the current language used for hyphenation.
+(The value of |clang| is undefined in restricted horizontal mode.)
+In math mode, |aux| is also known as |incompleat_noad|; if
+not |null|, it points to a record that represents the numerator of a
+generalized fraction for which the denominator is currently being formed
+in the current list.
+
+There is also a sixth quantity, |mode_line|, which correlates
+the semantic nest with the user's input; |mode_line| contains the source
+line number at which the current level of nesting was entered. The negative
+of this line number is the |mode_line| at the level of the
+user's output routine.
+
+A seventh quantity, |eTeX_aux|, is used by the extended features \eTeX.
+In vertical modes it is known as |LR_save| and holds the LR stack when a
+paragraph is interrupted by a displayed formula. In display math mode
+it is known as |LR_box| and holds a pointer to a prototype box for the
+display. In math mode it is known as |delim_ptr| and points to the most
+recent |left_noad| or |middle_noad| of a |math_left_group|.
+
+In horizontal mode, the |prev_graf| field is used for initial language data.
+
+The semantic nest is an array called |nest| that holds the |mode|, |head|,
+|tail|, |prev_graf|, |aux|, and |mode_line| values for all semantic levels
+below the currently active one. Information about the currently active
+level is kept in the global quantities |mode|, |head|, |tail|, |prev_graf|,
+|aux|, and |mode_line|, which live in a \PASCAL\ record that is ready to
+be pushed onto |nest| if necessary.
+
+@d ignore_depth -65536000 /*|prev_depth| value that is ignored*/
+
+@<Types...@>=
+typedef struct { int16_t @!mode_field;@+
+ pointer @!head_field, @!tail_field;
+ pointer @!eTeX_aux_field;
+ int @!pg_field, @!ml_field;@+
+ memory_word @!aux_field;
+ } list_state_record;
+
+@ @d mode cur_list.mode_field /*current mode*/
+@d head cur_list.head_field /*header node of current list*/
+@d tail cur_list.tail_field /*final node on current list*/
+@d eTeX_aux cur_list.eTeX_aux_field /*auxiliary data for \eTeX*/
+@d LR_save eTeX_aux /*LR stack when a paragraph is interrupted*/
+@d LR_box eTeX_aux /*prototype box for display*/
+@d delim_ptr eTeX_aux /*most recent left or right noad of a math left group*/
+@d prev_graf cur_list.pg_field /*number of paragraph lines accumulated*/
+@d aux cur_list.aux_field /*auxiliary data about the current list*/
+@d prev_depth aux.sc /*the name of |aux| in vertical mode*/
+@d space_factor aux.hh.lh /*part of |aux| in horizontal mode*/
+@d clang aux.hh.rh /*the other part of |aux| in horizontal mode*/
+@d incompleat_noad aux.i /*the name of |aux| in math mode*/
+@d mode_line cur_list.ml_field /*source file line number at beginning of list*/
+
+@<Glob...@>=
+static list_state_record @!nest[nest_size+1];
+static int @!nest_ptr; /*first unused location of |nest|*/
+static int @!max_nest_stack; /*maximum of |nest_ptr| when pushing*/
+static list_state_record @!cur_list; /*the ``top'' semantic state*/
+static int @!shown_mode; /*most recent mode shown by \.{\\tracingcommands}*/
+
+@ Here is a common way to make the current list grow:
+
+@d tail_append(A) {@+link(tail)=A;tail=link(tail);
+ }
+
+@ We will see later that the vertical list at the bottom semantic level is split
+into two parts; the ``current page'' runs from |page_head| to |page_tail|,
+and the ``contribution list'' runs from |contrib_head| to |tail| of
+semantic level zero. The idea is that contributions are first formed in
+vertical mode, then ``contributed'' to the current page (during which time
+the page-breaking decisions are made). For now, we don't need to know
+any more details about the page-building process.
+
+@<Set init...@>=
+nest_ptr=0;max_nest_stack=0;
+mode=vmode;head=contrib_head;tail=contrib_head;
+eTeX_aux=null;
+prev_depth=ignore_depth;mode_line=0;
+prev_graf=0;shown_mode=0;
+@<Start a new current page@>;
+
+@ When \TeX's work on one level is interrupted, the state is saved by
+calling |push_nest|. This routine changes |head| and |tail| so that
+a new (empty) list is begun; it does not change |mode| or |aux|.
+
+@p static void push_nest(void) /*enter a new semantic level, save the old*/
+{@+if (nest_ptr > max_nest_stack)
+ {@+max_nest_stack=nest_ptr;
+ if (nest_ptr==nest_size) overflow("semantic nest size", nest_size);
+@:TeX capacity exceeded semantic nest size}{\quad semantic nest size@>
+ }
+nest[nest_ptr]=cur_list; /*stack the record*/
+incr(nest_ptr);head=get_avail();tail=head;prev_graf=0;mode_line=line;
+eTeX_aux=null;
+}
+
+@ Conversely, when \TeX\ is finished on the current level, the former
+state is restored by calling |pop_nest|. This routine will never be
+called at the lowest semantic level, nor will it be called unless |head|
+is a node that should be returned to free memory.
+
+@p static void pop_nest(void) /*leave a semantic level, re-enter the old*/
+{@+free_avail(head);decr(nest_ptr);cur_list=nest[nest_ptr];
+}
+
+@ Here is a procedure that displays what \TeX\ is working on, at all levels.
+
+@p static void print_totals(void);
+static void show_activities(void)
+{@+int p; /*index into |nest|*/
+int @!m; /*mode*/
+memory_word @!a; /*auxiliary*/
+pointer @!q, @!r; /*for showing the current page*/
+int @!t; /*ditto*/
+nest[nest_ptr]=cur_list; /*put the top level into the array*/
+print_nl("");print_ln();
+for (p=nest_ptr; p>=0; p--)
+ {@+m=nest[p].mode_field;a=nest[p].aux_field;
+ print_nl("### ");print_mode(m);
+ print(" entered at line ");print_int(abs(nest[p].ml_field));
+ if (m==hmode) if (nest[p].pg_field!=040600000)
+ {@+print(" (language");print_int(nest[p].pg_field%0200000);
+ print(":hyphenmin");print_int(nest[p].pg_field/020000000);
+ print_char(',');print_int((nest[p].pg_field/0200000)%0100);
+ print_char(')');
+ }
+ if (nest[p].ml_field < 0) print(" (\\output routine)");
+ if (p==0)
+ {@+@<Show the status of the current page@>;
+ if (link(contrib_head)!=null)
+ print_nl("### recent contributions:");
+ }
+ show_box(link(nest[p].head_field));
+ @<Show the auxiliary field, |a|@>;
+ }
+}
+
+@ @<Show the auxiliary...@>=
+switch (abs(m)/(max_command+1)) {
+case 0: {@+print_nl("prevdepth ");
+ if (a.sc <= ignore_depth) print("ignored");
+ else print_scaled(a.sc);
+ if (nest[p].pg_field!=0)
+ {@+print(", prevgraf ");
+ print_int(nest[p].pg_field);print(" line");
+ if (nest[p].pg_field!=1) print_char('s');
+ }
+ } @+break;
+case 1: {@+print_nl("spacefactor ");print_int(a.hh.lh);
+ if (m > 0) @+if (a.hh.rh > 0)
+ {@+print(", current language ");print_int(a.hh.rh);@+
+ }
+ } @+break;
+case 2: if (a.i!=null)
+ {@+print("this will begin denominator of:");show_box(a.i);@+
+ }
+} /*there are no other cases*/
+
+@* The table of equivalents.
+Now that we have studied the data structures for \TeX's semantic routines,
+we ought to consider the data structures used by its syntactic routines. In
+other words, our next concern will be
+the tables that \TeX\ looks at when it is scanning
+what the user has written.
+
+The biggest and most important such table is called |eqtb|. It holds the
+current ``equivalents'' of things; i.e., it explains what things mean
+or what their current values are, for all quantities that are subject to
+the nesting structure provided by \TeX's grouping mechanism. There are six
+parts to |eqtb|:
+
+\yskip\hangg 1) |eqtb[active_base dotdot(hash_base-1)]| holds the current
+equivalents of single-character control sequences.
+
+\yskip\hangg 2) |eqtb[hash_base dotdot(glue_base-1)]| holds the current
+equivalents of multiletter control sequences.
+
+\yskip\hangg 3) |eqtb[glue_base dotdot(local_base-1)]| holds the current
+equivalents of glue parameters like the current baselineskip.
+
+\yskip\hangg 4) |eqtb[local_base dotdot(int_base-1)]| holds the current
+equivalents of local halfword quantities like the current box registers,
+the current ``catcodes,'' the current font, and a pointer to the current
+paragraph shape.
+
+\yskip\hangg 5) |eqtb[int_base dotdot(dimen_base-1)]| holds the current
+equivalents of fullword integer parameters like the current hyphenation
+penalty.
+
+\yskip\hangg 6) |eqtb[dimen_base dotdot eqtb_size]| holds the current equivalents
+of fullword dimension parameters like the current hsize or amount of
+hanging indentation.
+
+\yskip\noindent Note that, for example, the current amount of
+baselineskip glue is determined by the setting of a particular location
+in region~3 of |eqtb|, while the current meaning of the control sequence
+`\.{\\baselineskip}' (which might have been changed by \.{\\def} or
+\.{\\let}) appears in region~2.
+
+@ Each entry in |eqtb| is a |memory_word|. Most of these words are of type
+|two_halves|, and subdivided into three fields:
+
+\yskip\hangg 1) The |eq_level| (a quarterword) is the level of grouping at
+which this equivalent was defined. If the level is |level_zero|, the
+equivalent has never been defined; |level_one| refers to the outer level
+(outside of all groups), and this level is also used for global
+definitions that never go away. Higher levels are for equivalents that
+will disappear at the end of their group. @^global definitions@>
+
+\yskip\hangg 2) The |eq_type| (another quarterword) specifies what kind of
+entry this is. There are many types, since each \TeX\ primitive like
+\.{\\hbox}, \.{\\def}, etc., has its own special code. The list of
+command codes above includes all possible settings of the |eq_type| field.
+
+\yskip\hangg 3) The |equiv| (a halfword) is the current equivalent value.
+This may be a font number, a pointer into |mem|, or a variety of other
+things.
+
+@d eq_level_field(A) A.hh.b1
+@d eq_type_field(A) A.hh.b0
+@d equiv_field(A) A.hh.rh
+@d eq_level(A) eq_level_field(eqtb[A]) /*level of definition*/
+@d eq_type(A) eq_type_field(eqtb[A]) /*command code for equivalent*/
+@d equiv(A) equiv_field(eqtb[A]) /*equivalent value*/
+@d level_zero min_quarterword /*level for undefined quantities*/
+@d level_one (level_zero+1) /*outermost level for defined quantities*/
+
+@ Many locations in |eqtb| have symbolic names. The purpose of the next
+paragraphs is to define these names, and to set up the initial values of the
+equivalents.
+
+In the first region we have 256 equivalents for ``active characters'' that
+act as control sequences, followed by 256 equivalents for single-character
+control sequences.
+
+Then comes region~2, which corresponds to the hash table that we will
+define later. The maximum address in this region is used for a dummy
+control sequence that is perpetually undefined. There also are several
+locations for control sequences that are perpetually defined
+(since they are used in error recovery).
+
+@d active_base 1 /*beginning of region 1, for active character equivalents*/
+@d single_base (active_base+256) /*equivalents of one-character control sequences*/
+@d null_cs (single_base+256) /*equivalent of \.{\\csname\\endcsname}*/
+@d hash_base (null_cs+1) /*beginning of region 2, for the hash table*/
+@d frozen_control_sequence (hash_base+hash_size) /*for error recovery*/
+@d frozen_protection frozen_control_sequence /*inaccessible but definable*/
+@d frozen_cr (frozen_control_sequence+1) /*permanent `\.{\\cr}'*/
+@d frozen_end_group (frozen_control_sequence+2) /*permanent `\.{\\endgroup}'*/
+@d frozen_right (frozen_control_sequence+3) /*permanent `\.{\\right}'*/
+@d frozen_fi (frozen_control_sequence+4) /*permanent `\.{\\fi}'*/
+@d frozen_end_template (frozen_control_sequence+5) /*permanent `\.{\\endtemplate}'*/
+@d frozen_endv (frozen_control_sequence+6) /*second permanent `\.{\\endtemplate}'*/
+@d frozen_relax (frozen_control_sequence+7) /*permanent `\.{\\relax}'*/
+@d end_write (frozen_control_sequence+8) /*permanent `\.{\\endwrite}'*/
+@d frozen_dont_expand (frozen_control_sequence+9)
+ /*permanent `\.{\\notexpanded:}'*/
+@d frozen_primitive (frozen_control_sequence+10)
+ /*permanent `\.{\\primitive:}'*/
+@d frozen_null_font (frozen_control_sequence+11)
+ /*permanent `\.{\\nullfont}'*/
+@d font_id_base (frozen_null_font-font_base)
+ /*begins table of 257 permanent font identifiers*/
+@d undefined_control_sequence (frozen_null_font+257) /*dummy location*/
+@d glue_base (undefined_control_sequence+1) /*beginning of region 3*/
+
+@<Initialize table entries...@>=
+eq_type(undefined_control_sequence)=undefined_cs;
+equiv(undefined_control_sequence)=null;
+eq_level(undefined_control_sequence)=level_zero;
+for (k=active_base; k<=undefined_control_sequence-1; k++)
+ eqtb[k]=eqtb[undefined_control_sequence];
+
+@ Here is a routine that displays the current meaning of an |eqtb| entry
+in region 1 or~2. (Similar routines for the other regions will appear
+below.)
+
+@<Show equivalent |n|, in region 1 or 2@>=
+{@+sprint_cs(n);print_char('=');print_cmd_chr(eq_type(n), equiv(n));
+if (eq_type(n) >= call)
+ {@+print_char(':');show_token_list(link(equiv(n)), null, 32);
+ }
+}
+
+@ Region 3 of |eqtb| contains the 256 \.{\\skip} registers, as well as the
+glue parameters defined here. It is important that the ``muskip''
+parameters have larger numbers than the others.
+
+@d line_skip_code 0 /*interline glue if |baseline_skip| is infeasible*/
+@d baseline_skip_code 1 /*desired glue between baselines*/
+@d par_skip_code 2 /*extra glue just above a paragraph*/
+@d above_display_skip_code 3 /*extra glue just above displayed math*/
+@d below_display_skip_code 4 /*extra glue just below displayed math*/
+@d above_display_short_skip_code 5
+ /*glue above displayed math following short lines*/
+@d below_display_short_skip_code 6
+ /*glue below displayed math following short lines*/
+@d left_skip_code 7 /*glue at left of justified lines*/
+@d right_skip_code 8 /*glue at right of justified lines*/
+@d top_skip_code 9 /*glue at top of main pages*/
+@d split_top_skip_code 10 /*glue at top of split pages*/
+@d tab_skip_code 11 /*glue between aligned entries*/
+@d space_skip_code 12 /*glue between words (if not |zero_glue|)*/
+@d xspace_skip_code 13 /*glue after sentences (if not |zero_glue|)*/
+@d par_fill_skip_code 14 /*glue on last line of paragraph*/
+@d thin_mu_skip_code 15 /*thin space in math formula*/
+@d med_mu_skip_code 16 /*medium space in math formula*/
+@d thick_mu_skip_code 17 /*thick space in math formula*/
+@d glue_pars 18 /*total number of glue parameters*/
+@d skip_base (glue_base+glue_pars) /*table of 256 ``skip'' registers*/
+@d mu_skip_base (skip_base+256) /*table of 256 ``muskip'' registers*/
+@d local_base (mu_skip_base+256) /*beginning of region 4*/
+@#
+@d skip(A) equiv(skip_base+A) /*|mem| location of glue specification*/
+@d mu_skip(A) equiv(mu_skip_base+A) /*|mem| location of math glue spec*/
+@d glue_par(A) equiv(glue_base+A) /*|mem| location of glue specification*/
+@d line_skip glue_par(line_skip_code)
+@d baseline_skip glue_par(baseline_skip_code)
+@d par_skip glue_par(par_skip_code)
+@d above_display_skip glue_par(above_display_skip_code)
+@d below_display_skip glue_par(below_display_skip_code)
+@d above_display_short_skip glue_par(above_display_short_skip_code)
+@d below_display_short_skip glue_par(below_display_short_skip_code)
+@d left_skip glue_par(left_skip_code)
+@d right_skip glue_par(right_skip_code)
+@d top_skip glue_par(top_skip_code)
+@d split_top_skip glue_par(split_top_skip_code)
+@d tab_skip glue_par(tab_skip_code)
+@d space_skip glue_par(space_skip_code)
+@d xspace_skip glue_par(xspace_skip_code)
+@d par_fill_skip glue_par(par_fill_skip_code)
+@d thin_mu_skip glue_par(thin_mu_skip_code)
+@d med_mu_skip glue_par(med_mu_skip_code)
+@d thick_mu_skip glue_par(thick_mu_skip_code)
+
+@<Current |mem| equivalent of glue parameter number |n|@>=glue_par(n)
+
+@ Sometimes we need to convert \TeX's internal code numbers into symbolic
+form. The |print_skip_param| routine gives the symbolic name of a glue
+parameter.
+
+@<Declare the procedure called |print_skip_param|@>=
+static void print_skip_param(int @!n)
+{@+switch (n) {
+case line_skip_code: print_esc("lineskip");@+break;
+case baseline_skip_code: print_esc("baselineskip");@+break;
+case par_skip_code: print_esc("parskip");@+break;
+case above_display_skip_code: print_esc("abovedisplayskip");@+break;
+case below_display_skip_code: print_esc("belowdisplayskip");@+break;
+case above_display_short_skip_code: print_esc("abovedisplayshortskip");@+break;
+case below_display_short_skip_code: print_esc("belowdisplayshortskip");@+break;
+case left_skip_code: print_esc("leftskip");@+break;
+case right_skip_code: print_esc("rightskip");@+break;
+case top_skip_code: print_esc("topskip");@+break;
+case split_top_skip_code: print_esc("splittopskip");@+break;
+case tab_skip_code: print_esc("tabskip");@+break;
+case space_skip_code: print_esc("spaceskip");@+break;
+case xspace_skip_code: print_esc("xspaceskip");@+break;
+case par_fill_skip_code: print_esc("parfillskip");@+break;
+case thin_mu_skip_code: print_esc("thinmuskip");@+break;
+case med_mu_skip_code: print_esc("medmuskip");@+break;
+case thick_mu_skip_code: print_esc("thickmuskip");@+break;
+default:print("[unknown glue parameter!]");
+}
+}
+
+@ The symbolic names for glue parameters are put into \TeX's hash table
+by using the routine called |primitive|, defined below. Let us enter them
+now, so that we don't have to list all those parameter names anywhere else.
+
+@<Put each of \TeX's primitives into the hash table@>=
+primitive("lineskip", assign_glue, glue_base+line_skip_code);@/
+@!@:line\_skip\_}{\.{\\lineskip} primitive@>
+primitive("baselineskip", assign_glue, glue_base+baseline_skip_code);@/
+@!@:baseline\_skip\_}{\.{\\baselineskip} primitive@>
+primitive("parskip", assign_glue, glue_base+par_skip_code);@/
+@!@:par\_skip\_}{\.{\\parskip} primitive@>
+primitive("abovedisplayskip", assign_glue, glue_base+above_display_skip_code);@/
+@!@:above\_display\_skip\_}{\.{\\abovedisplayskip} primitive@>
+primitive("belowdisplayskip", assign_glue, glue_base+below_display_skip_code);@/
+@!@:below\_display\_skip\_}{\.{\\belowdisplayskip} primitive@>
+primitive("abovedisplayshortskip",
+ assign_glue, glue_base+above_display_short_skip_code);@/
+@!@:above\_display\_short\_skip\_}{\.{\\abovedisplayshortskip} primitive@>
+primitive("belowdisplayshortskip",
+ assign_glue, glue_base+below_display_short_skip_code);@/
+@!@:below\_display\_short\_skip\_}{\.{\\belowdisplayshortskip} primitive@>
+primitive("leftskip", assign_glue, glue_base+left_skip_code);@/
+@!@:left\_skip\_}{\.{\\leftskip} primitive@>
+primitive("rightskip", assign_glue, glue_base+right_skip_code);@/
+@!@:right\_skip\_}{\.{\\rightskip} primitive@>
+primitive("topskip", assign_glue, glue_base+top_skip_code);@/
+@!@:top\_skip\_}{\.{\\topskip} primitive@>
+primitive("splittopskip", assign_glue, glue_base+split_top_skip_code);@/
+@!@:split\_top\_skip\_}{\.{\\splittopskip} primitive@>
+primitive("tabskip", assign_glue, glue_base+tab_skip_code);@/
+@!@:tab\_skip\_}{\.{\\tabskip} primitive@>
+primitive("spaceskip", assign_glue, glue_base+space_skip_code);@/
+@!@:space\_skip\_}{\.{\\spaceskip} primitive@>
+primitive("xspaceskip", assign_glue, glue_base+xspace_skip_code);@/
+@!@:xspace\_skip\_}{\.{\\xspaceskip} primitive@>
+primitive("parfillskip", assign_glue, glue_base+par_fill_skip_code);@/
+@!@:par\_fill\_skip\_}{\.{\\parfillskip} primitive@>
+primitive("thinmuskip", assign_mu_glue, glue_base+thin_mu_skip_code);@/
+@!@:thin\_mu\_skip\_}{\.{\\thinmuskip} primitive@>
+primitive("medmuskip", assign_mu_glue, glue_base+med_mu_skip_code);@/
+@!@:med\_mu\_skip\_}{\.{\\medmuskip} primitive@>
+primitive("thickmuskip", assign_mu_glue, glue_base+thick_mu_skip_code);@/
+@!@:thick\_mu\_skip\_}{\.{\\thickmuskip} primitive@>
+
+@ @<Cases of |print_cmd_chr| for symbolic printing of primitives@>=
+case assign_glue: case assign_mu_glue: if (chr_code < skip_base)
+ print_skip_param(chr_code-glue_base);
+ else if (chr_code < mu_skip_base)
+ {@+print_esc("skip");print_int(chr_code-skip_base);
+ }
+ else{@+print_esc("muskip");print_int(chr_code-mu_skip_base);
+ } @+break;
+
+@ All glue parameters and registers are initially `\.{0pt plus0pt minus0pt}'.
+
+@<Initialize table entries...@>=
+equiv(glue_base)=zero_glue;eq_level(glue_base)=level_one;
+eq_type(glue_base)=glue_ref;
+for (k=glue_base+1; k<=local_base-1; k++) eqtb[k]=eqtb[glue_base];
+glue_ref_count(zero_glue)=glue_ref_count(zero_glue)+local_base-glue_base;
+
+@ @<Show equivalent |n|, in region 3@>=
+if (n < skip_base)
+ {@+print_skip_param(n-glue_base);print_char('=');
+ if (n < glue_base+thin_mu_skip_code) print_spec(equiv(n),"pt");
+ else print_spec(equiv(n),"mu");
+ }
+else if (n < mu_skip_base)
+ {@+print_esc("skip");print_int(n-skip_base);print_char('=');
+ print_spec(equiv(n),"pt");
+ }
+else{@+print_esc("muskip");print_int(n-mu_skip_base);print_char('=');
+ print_spec(equiv(n),"mu");
+ }
+
+@ Region 4 of |eqtb| contains the local quantities defined here. The
+bulk of this region is taken up by five tables that are indexed by eight-bit
+characters; these tables are important to both the syntactic and semantic
+portions of \TeX. There are also a bunch of special things like font and
+token parameters, as well as the tables of \.{\\toks} and \.{\\box}
+registers.
+
+@d par_shape_loc local_base /*specifies paragraph shape*/
+@d output_routine_loc (local_base+1) /*points to token list for \.{\\output}*/
+@d every_par_loc (local_base+2) /*points to token list for \.{\\everypar}*/
+@d every_math_loc (local_base+3) /*points to token list for \.{\\everymath}*/
+@d every_display_loc (local_base+4) /*points to token list for \.{\\everydisplay}*/
+@d every_hbox_loc (local_base+5) /*points to token list for \.{\\everyhbox}*/
+@d every_vbox_loc (local_base+6) /*points to token list for \.{\\everyvbox}*/
+@d every_job_loc (local_base+7) /*points to token list for \.{\\everyjob}*/
+@d every_cr_loc (local_base+8) /*points to token list for \.{\\everycr}*/
+@d err_help_loc (local_base+9) /*points to token list for \.{\\errhelp}*/
+@d tex_toks (local_base+10) /*end of \TeX's token list parameters*/
+@#
+@d etex_toks_base pdf_toks /*base for \eTeX's token list parameters*/
+@d every_eof_loc etex_toks_base /*points to token list for \.{\\everyeof}*/
+@d etex_toks (etex_toks_base+1) /*end of \eTeX's token list parameters*/
+@#
+@d toks_base etex_toks /*table of 256 token list registers*/
+@#
+@d etex_pen_base (toks_base+256) /*start of table of \eTeX's penalties*/
+@d inter_line_penalties_loc etex_pen_base /*additional penalties between lines*/
+@d club_penalties_loc (etex_pen_base+1) /*penalties for creating club lines*/
+@d widow_penalties_loc (etex_pen_base+2) /*penalties for creating widow lines*/
+@d display_widow_penalties_loc (etex_pen_base+3) /*ditto, just before a display*/
+@d etex_pens (etex_pen_base+4) /*end of table of \eTeX's penalties*/
+@#
+@d box_base etex_pens /*table of 256 box registers*/
+@d cur_font_loc (box_base+256) /*internal font number outside math mode*/
+@d math_font_base (cur_font_loc+1) /*table of 48 math font numbers*/
+@d cat_code_base (math_font_base+48)
+ /*table of 256 command codes (the ``catcodes'')*/
+@d lc_code_base (cat_code_base+256) /*table of 256 lowercase mappings*/
+@d uc_code_base (lc_code_base+256) /*table of 256 uppercase mappings*/
+@d sf_code_base (uc_code_base+256) /*table of 256 spacefactor mappings*/
+@d math_code_base (sf_code_base+256) /*table of 256 math mode mappings*/
+@d int_base (math_code_base+256) /*beginning of region 5*/
+@#
+@d par_shape_ptr equiv(par_shape_loc)
+@d output_routine equiv(output_routine_loc)
+@d every_par equiv(every_par_loc)
+@d every_math equiv(every_math_loc)
+@d every_display equiv(every_display_loc)
+@d every_hbox equiv(every_hbox_loc)
+@d every_vbox equiv(every_vbox_loc)
+@d every_job equiv(every_job_loc)
+@d every_cr equiv(every_cr_loc)
+@d err_help equiv(err_help_loc)
+@d toks(X) equiv(toks_base+X)
+@d box(A) equiv(box_base+A)
+@d cur_font equiv(cur_font_loc)
+@d fam_fnt(A) equiv(math_font_base+A)
+@d cat_code(A) equiv(cat_code_base+A)
+@d lc_code(A) equiv(lc_code_base+A)
+@d uc_code(A) equiv(uc_code_base+A)
+@d sf_code(A) equiv(sf_code_base+A)
+@d math_code(A) equiv(math_code_base+A)
+ /*Note: |math_code(c)| is the true math code plus |min_halfword|*/
+
+@<Put each...@>=
+primitive("output", assign_toks, output_routine_loc);
+@!@:output\_}{\.{\\output} primitive@>
+primitive("everypar", assign_toks, every_par_loc);
+@!@:every\_par\_}{\.{\\everypar} primitive@>
+primitive("everymath", assign_toks, every_math_loc);
+@!@:every\_math\_}{\.{\\everymath} primitive@>
+primitive("everydisplay", assign_toks, every_display_loc);
+@!@:every\_display\_}{\.{\\everydisplay} primitive@>
+primitive("everyhbox", assign_toks, every_hbox_loc);
+@!@:every\_hbox\_}{\.{\\everyhbox} primitive@>
+primitive("everyvbox", assign_toks, every_vbox_loc);
+@!@:every\_vbox\_}{\.{\\everyvbox} primitive@>
+primitive("everyjob", assign_toks, every_job_loc);
+@!@:every\_job\_}{\.{\\everyjob} primitive@>
+primitive("everycr", assign_toks, every_cr_loc);
+@!@:every\_cr\_}{\.{\\everycr} primitive@>
+primitive("errhelp", assign_toks, err_help_loc);
+@!@:err\_help\_}{\.{\\errhelp} primitive@>
+
+@ @<Cases of |print_cmd_chr|...@>=
+case assign_toks: if (chr_code >= toks_base)
+ {@+print_esc("toks");print_int(chr_code-toks_base);
+ }
+else switch (chr_code) {
+ case output_routine_loc: print_esc("output");@+break;
+ case every_par_loc: print_esc("everypar");@+break;
+ case every_math_loc: print_esc("everymath");@+break;
+ case every_display_loc: print_esc("everydisplay");@+break;
+ case every_hbox_loc: print_esc("everyhbox");@+break;
+ case every_vbox_loc: print_esc("everyvbox");@+break;
+ case every_job_loc: print_esc("everyjob");@+break;
+ case every_cr_loc: print_esc("everycr");@+break;
+ @/@<Cases of |assign_toks| for |print_cmd_chr|@>@/
+ default:print_esc("errhelp");
+ } @+break;
+
+@ We initialize most things to null or undefined values. An undefined font
+is represented by the internal code |font_base|.
+
+However, the character code tables are given initial values based on the
+conventional interpretation of ASCII code. These initial values should
+not be changed when \TeX\ is adapted for use with non-English languages;
+all changes to the initialization conventions should be made in format
+packages, not in \TeX\ itself, so that global interchange of formats is
+possible.
+
+@d null_font font_base
+@d var_code 070000 /*math code meaning ``use the current family''*/
+
+@<Initialize table entries...@>=
+par_shape_ptr=null;eq_type(par_shape_loc)=shape_ref;
+eq_level(par_shape_loc)=level_one;@/
+for (k=etex_pen_base; k<=etex_pens-1; k++)
+ eqtb[k]=eqtb[par_shape_loc];
+for (k=output_routine_loc; k<=toks_base+255; k++)
+ eqtb[k]=eqtb[undefined_control_sequence];
+box(0)=null;eq_type(box_base)=box_ref;eq_level(box_base)=level_one;
+for (k=box_base+1; k<=box_base+255; k++) eqtb[k]=eqtb[box_base];
+cur_font=null_font;eq_type(cur_font_loc)=data;
+eq_level(cur_font_loc)=level_one;@/
+for (k=math_font_base; k<=math_font_base+47; k++) eqtb[k]=eqtb[cur_font_loc];
+equiv(cat_code_base)=0;eq_type(cat_code_base)=data;
+eq_level(cat_code_base)=level_one;@/
+for (k=cat_code_base+1; k<=int_base-1; k++) eqtb[k]=eqtb[cat_code_base];
+for (k=0; k<=255; k++)
+ {@+cat_code(k)=other_char;math_code(k)=hi(k);sf_code(k)=1000;
+ }
+cat_code(carriage_return)=car_ret;cat_code(' ')=spacer;
+cat_code('\\')=escape;cat_code('%')=comment;
+cat_code(invalid_code)=invalid_char;cat_code(null_code)=ignore;
+for (k='0'; k<='9'; k++) math_code(k)=hi(k+var_code);
+for (k='A'; k<='Z'; k++)
+ {@+cat_code(k)=letter;cat_code(k+'a'-'A')=letter;@/
+ math_code(k)=hi(k+var_code+0x100);
+ math_code(k+'a'-'A')=hi(k+'a'-'A'+var_code+0x100);@/
+ lc_code(k)=k+'a'-'A';lc_code(k+'a'-'A')=k+'a'-'A';@/
+ uc_code(k)=k;uc_code(k+'a'-'A')=k;@/
+ sf_code(k)=999;
+ }
+
+@ @<Show equivalent |n|, in region 4@>=
+if ((n==par_shape_loc)||((n >= etex_pen_base)&&(n < etex_pens)))
+ {@+print_cmd_chr(set_shape, n);print_char('=');
+ if (equiv(n)==null) print_char('0');
+ else if (n > par_shape_loc)
+ {@+print_int(penalty(equiv(n)));print_char(' ');
+ print_int(penalty(equiv(n)+1));
+ if (penalty(equiv(n)) > 1) print_esc("ETC.");
+ }
+ else print_int(info(par_shape_ptr));
+ }
+else if (n < toks_base)
+ {@+print_cmd_chr(assign_toks, n);print_char('=');
+ if (equiv(n)!=null) show_token_list(link(equiv(n)), null, 32);
+ }
+else if (n < box_base)
+ {@+print_esc("toks");print_int(n-toks_base);print_char('=');
+ if (equiv(n)!=null) show_token_list(link(equiv(n)), null, 32);
+ }
+else if (n < cur_font_loc)
+ {@+print_esc("box");print_int(n-box_base);print_char('=');
+ if (equiv(n)==null) print("void");
+ else{@+depth_threshold=0;breadth_max=1;show_node_list(equiv(n));
+ }
+ }
+else if (n < cat_code_base) @<Show the font identifier in |eqtb[n]|@>@;
+else@<Show the halfword code in |eqtb[n]|@>@;
+
+@ @<Show the font identifier in |eqtb[n]|@>=
+{@+if (n==cur_font_loc) print("current font");
+else if (n < math_font_base+16)
+ {@+print_esc("textfont");print_int(n-math_font_base);
+ }
+else if (n < math_font_base+32)
+ {@+print_esc("scriptfont");print_int(n-math_font_base-16);
+ }
+else{@+print_esc("scriptscriptfont");print_int(n-math_font_base-32);
+ }
+print_char('=');@/
+printn_esc(hash[font_id_base+equiv(n)].rh);
+ /*that's |font_id_text(equiv(n))|*/
+}
+
+@ @<Show the halfword code in |eqtb[n]|@>=
+if (n < math_code_base)
+ {@+if (n < lc_code_base)
+ {@+print_esc("catcode");print_int(n-cat_code_base);
+ }
+ else if (n < uc_code_base)
+ {@+print_esc("lccode");print_int(n-lc_code_base);
+ }
+ else if (n < sf_code_base)
+ {@+print_esc("uccode");print_int(n-uc_code_base);
+ }
+ else{@+print_esc("sfcode");print_int(n-sf_code_base);
+ }
+ print_char('=');print_int(equiv(n));
+ }
+else{@+print_esc("mathcode");print_int(n-math_code_base);
+ print_char('=');print_int(ho(equiv(n)));
+ }
+
+@ Region 5 of |eqtb| contains the integer parameters and registers defined
+here, as well as the |del_code| table. The latter table differs from the
+|cat_code dotdot math_code| tables that precede it, since delimiter codes are
+fullword integers while the other kinds of codes occupy at most a
+halfword. This is what makes region~5 different from region~4. We will
+store the |eq_level| information in an auxiliary array of quarterwords
+that will be defined later.
+
+@d pretolerance_code 0 /*badness tolerance before hyphenation*/
+@d tolerance_code 1 /*badness tolerance after hyphenation*/
+@d line_penalty_code 2 /*added to the badness of every line*/
+@d hyphen_penalty_code 3 /*penalty for break after discretionary hyphen*/
+@d ex_hyphen_penalty_code 4 /*penalty for break after explicit hyphen*/
+@d club_penalty_code 5 /*penalty for creating a club line*/
+@d widow_penalty_code 6 /*penalty for creating a widow line*/
+@d display_widow_penalty_code 7 /*ditto, just before a display*/
+@d broken_penalty_code 8 /*penalty for breaking a page at a broken line*/
+@d bin_op_penalty_code 9 /*penalty for breaking after a binary operation*/
+@d rel_penalty_code 10 /*penalty for breaking after a relation*/
+@d pre_display_penalty_code 11
+ /*penalty for breaking just before a displayed formula*/
+@d post_display_penalty_code 12
+ /*penalty for breaking just after a displayed formula*/
+@d inter_line_penalty_code 13 /*additional penalty between lines*/
+@d double_hyphen_demerits_code 14 /*demerits for double hyphen break*/
+@d final_hyphen_demerits_code 15 /*demerits for final hyphen break*/
+@d adj_demerits_code 16 /*demerits for adjacent incompatible lines*/
+@d mag_code 17 /*magnification ratio*/
+@d delimiter_factor_code 18 /*ratio for variable-size delimiters*/
+@d looseness_code 19 /*change in number of lines for a paragraph*/
+@d time_code 20 /*current time of day*/
+@d day_code 21 /*current day of the month*/
+@d month_code 22 /*current month of the year*/
+@d year_code 23 /*current year of our Lord*/
+@d show_box_breadth_code 24 /*nodes per level in |show_box|*/
+@d show_box_depth_code 25 /*maximum level in |show_box|*/
+@d hbadness_code 26 /*hboxes exceeding this badness will be shown by |hpack|*/
+@d vbadness_code 27 /*vboxes exceeding this badness will be shown by |vpack|*/
+@d pausing_code 28 /*pause after each line is read from a file*/
+@d tracing_online_code 29 /*show diagnostic output on terminal*/
+@d tracing_macros_code 30 /*show macros as they are being expanded*/
+@d tracing_stats_code 31 /*show memory usage if \TeX\ knows it*/
+@d tracing_paragraphs_code 32 /*show line-break calculations*/
+@d tracing_pages_code 33 /*show page-break calculations*/
+@d tracing_output_code 34 /*show boxes when they are shipped out*/
+@d tracing_lost_chars_code 35 /*show characters that aren't in the font*/
+@d tracing_commands_code 36 /*show command codes at |big_switch|*/
+@d tracing_restores_code 37 /*show equivalents when they are restored*/
+@d uc_hyph_code 38 /*hyphenate words beginning with a capital letter*/
+@d output_penalty_code 39 /*penalty found at current page break*/
+@d max_dead_cycles_code 40 /*bound on consecutive dead cycles of output*/
+@d hang_after_code 41 /*hanging indentation changes after this many lines*/
+@d floating_penalty_code 42 /*penalty for insertions held over after a split*/
+@d global_defs_code 43 /*override \.{\\global} specifications*/
+@d cur_fam_code 44 /*current family*/
+@d escape_char_code 45 /*escape character for token output*/
+@d default_hyphen_char_code 46 /*value of \.{\\hyphenchar} when a font is loaded*/
+@d default_skew_char_code 47 /*value of \.{\\skewchar} when a font is loaded*/
+@d end_line_char_code 48 /*character placed at the right end of the buffer*/
+@d new_line_char_code 49 /*character that prints as |print_ln|*/
+@d language_code 50 /*current hyphenation table*/
+@d left_hyphen_min_code 51 /*minimum left hyphenation fragment size*/
+@d right_hyphen_min_code 52 /*minimum right hyphenation fragment size*/
+@d holding_inserts_code 53 /*do not remove insertion nodes from \.{\\box255}*/
+@d error_context_lines_code 54 /*maximum intermediate line pairs shown*/
+@d tex_int_pars 55 /*total number of \TeX's integer parameters*/
+@#
+@d etex_int_base pdf_int_pars /*base for \eTeX's integer parameters*/
+@d tracing_assigns_code etex_int_base /*show assignments*/
+@d tracing_groups_code (etex_int_base+1) /*show save/restore groups*/
+@d tracing_ifs_code (etex_int_base+2) /*show conditionals*/
+@d tracing_scan_tokens_code (etex_int_base+3) /*show pseudo file open and close*/
+@d tracing_nesting_code (etex_int_base+4) /*show incomplete groups and ifs within files*/
+@d saving_vdiscards_code (etex_int_base+5) /*save items discarded from vlists*/
+@d saving_hyph_codes_code (etex_int_base+6) /*save hyphenation codes for languages*/
+@d expand_depth_code (etex_int_base+7) /*maximum depth for expansion---\eTeX*/
+@d eTeX_state_code (etex_int_base+8) /*\eTeX\ state variables*/
+@d etex_int_pars (eTeX_state_code+eTeX_states) /*total number of \eTeX's integer parameters*/
+@#
+@d int_pars etex_int_pars /*total number of integer parameters*/
+@d count_base (int_base+int_pars) /*256 user \.{\\count} registers*/
+@d del_code_base (count_base+256) /*256 delimiter code mappings*/
+@d dimen_base (del_code_base+256) /*beginning of region 6*/
+@#
+@d del_code(A) eqtb[del_code_base+A].i
+@d count(A) eqtb[count_base+A].i
+@d int_par(A) eqtb[int_base+A].i /*an integer parameter*/
+@d pretolerance int_par(pretolerance_code)
+@d tolerance int_par(tolerance_code)
+@d line_penalty int_par(line_penalty_code)
+@d hyphen_penalty int_par(hyphen_penalty_code)
+@d ex_hyphen_penalty int_par(ex_hyphen_penalty_code)
+@d club_penalty int_par(club_penalty_code)
+@d widow_penalty int_par(widow_penalty_code)
+@d display_widow_penalty int_par(display_widow_penalty_code)
+@d broken_penalty int_par(broken_penalty_code)
+@d bin_op_penalty int_par(bin_op_penalty_code)
+@d rel_penalty int_par(rel_penalty_code)
+@d pre_display_penalty int_par(pre_display_penalty_code)
+@d post_display_penalty int_par(post_display_penalty_code)
+@d inter_line_penalty int_par(inter_line_penalty_code)
+@d double_hyphen_demerits int_par(double_hyphen_demerits_code)
+@d final_hyphen_demerits int_par(final_hyphen_demerits_code)
+@d adj_demerits int_par(adj_demerits_code)
+@d mag int_par(mag_code)
+@d delimiter_factor int_par(delimiter_factor_code)
+@d looseness int_par(looseness_code)
+@d time int_par(time_code)
+@d day int_par(day_code)
+@d month int_par(month_code)
+@d year int_par(year_code)
+@d show_box_breadth int_par(show_box_breadth_code)
+@d show_box_depth int_par(show_box_depth_code)
+@d hbadness int_par(hbadness_code)
+@d vbadness int_par(vbadness_code)
+@d pausing int_par(pausing_code)
+@d tracing_online int_par(tracing_online_code)
+@d tracing_macros int_par(tracing_macros_code)
+@d tracing_stats int_par(tracing_stats_code)
+@d tracing_paragraphs int_par(tracing_paragraphs_code)
+@d tracing_pages int_par(tracing_pages_code)
+@d tracing_output int_par(tracing_output_code)
+@d tracing_lost_chars int_par(tracing_lost_chars_code)
+@d tracing_commands int_par(tracing_commands_code)
+@d tracing_restores int_par(tracing_restores_code)
+@d uc_hyph int_par(uc_hyph_code)
+@d output_penalty int_par(output_penalty_code)
+@d max_dead_cycles int_par(max_dead_cycles_code)
+@d hang_after int_par(hang_after_code)
+@d floating_penalty int_par(floating_penalty_code)
+@d global_defs int_par(global_defs_code)
+@d cur_fam int_par(cur_fam_code)
+@d escape_char int_par(escape_char_code)
+@d default_hyphen_char int_par(default_hyphen_char_code)
+@d default_skew_char int_par(default_skew_char_code)
+@d end_line_char int_par(end_line_char_code)
+@d new_line_char int_par(new_line_char_code)
+@d language int_par(language_code)
+@d left_hyphen_min int_par(left_hyphen_min_code)
+@d right_hyphen_min int_par(right_hyphen_min_code)
+@d holding_inserts int_par(holding_inserts_code)
+@d error_context_lines int_par(error_context_lines_code)
+@#
+@d tracing_assigns int_par(tracing_assigns_code)
+@d tracing_groups int_par(tracing_groups_code)
+@d tracing_ifs int_par(tracing_ifs_code)
+@d tracing_scan_tokens int_par(tracing_scan_tokens_code)
+@d tracing_nesting int_par(tracing_nesting_code)
+@d saving_vdiscards int_par(saving_vdiscards_code)
+@d saving_hyph_codes int_par(saving_hyph_codes_code)
+@d expand_depth int_par(expand_depth_code)
+
+@<Assign the values |depth_threshold:=show_box_depth|...@>=
+depth_threshold=show_box_depth;
+breadth_max=show_box_breadth
+
+@ We can print the symbolic name of an integer parameter as follows.
+
+@p static void print_param(int @!n)
+{@+switch (n) {
+case pretolerance_code: print_esc("pretolerance");@+break;
+case tolerance_code: print_esc("tolerance");@+break;
+case line_penalty_code: print_esc("linepenalty");@+break;
+case hyphen_penalty_code: print_esc("hyphenpenalty");@+break;
+case ex_hyphen_penalty_code: print_esc("exhyphenpenalty");@+break;
+case club_penalty_code: print_esc("clubpenalty");@+break;
+case widow_penalty_code: print_esc("widowpenalty");@+break;
+case display_widow_penalty_code: print_esc("displaywidowpenalty");@+break;
+case broken_penalty_code: print_esc("brokenpenalty");@+break;
+case bin_op_penalty_code: print_esc("binoppenalty");@+break;
+case rel_penalty_code: print_esc("relpenalty");@+break;
+case pre_display_penalty_code: print_esc("predisplaypenalty");@+break;
+case post_display_penalty_code: print_esc("postdisplaypenalty");@+break;
+case inter_line_penalty_code: print_esc("interlinepenalty");@+break;
+case double_hyphen_demerits_code: print_esc("doublehyphendemerits");@+break;
+case final_hyphen_demerits_code: print_esc("finalhyphendemerits");@+break;
+case adj_demerits_code: print_esc("adjdemerits");@+break;
+case mag_code: print_esc("mag");@+break;
+case delimiter_factor_code: print_esc("delimiterfactor");@+break;
+case looseness_code: print_esc("looseness");@+break;
+case time_code: print_esc("time");@+break;
+case day_code: print_esc("day");@+break;
+case month_code: print_esc("month");@+break;
+case year_code: print_esc("year");@+break;
+case show_box_breadth_code: print_esc("showboxbreadth");@+break;
+case show_box_depth_code: print_esc("showboxdepth");@+break;
+case hbadness_code: print_esc("hbadness");@+break;
+case vbadness_code: print_esc("vbadness");@+break;
+case pausing_code: print_esc("pausing");@+break;
+case tracing_online_code: print_esc("tracingonline");@+break;
+case tracing_macros_code: print_esc("tracingmacros");@+break;
+case tracing_stats_code: print_esc("tracingstats");@+break;
+case tracing_paragraphs_code: print_esc("tracingparagraphs");@+break;
+case tracing_pages_code: print_esc("tracingpages");@+break;
+case tracing_output_code: print_esc("tracingoutput");@+break;
+case tracing_lost_chars_code: print_esc("tracinglostchars");@+break;
+case tracing_commands_code: print_esc("tracingcommands");@+break;
+case tracing_restores_code: print_esc("tracingrestores");@+break;
+case uc_hyph_code: print_esc("uchyph");@+break;
+case output_penalty_code: print_esc("outputpenalty");@+break;
+case max_dead_cycles_code: print_esc("maxdeadcycles");@+break;
+case hang_after_code: print_esc("hangafter");@+break;
+case floating_penalty_code: print_esc("floatingpenalty");@+break;
+case global_defs_code: print_esc("globaldefs");@+break;
+case cur_fam_code: print_esc("fam");@+break;
+case escape_char_code: print_esc("escapechar");@+break;
+case default_hyphen_char_code: print_esc("defaulthyphenchar");@+break;
+case default_skew_char_code: print_esc("defaultskewchar");@+break;
+case end_line_char_code: print_esc("endlinechar");@+break;
+case new_line_char_code: print_esc("newlinechar");@+break;
+case language_code: print_esc("language");@+break;
+case left_hyphen_min_code: print_esc("lefthyphenmin");@+break;
+case right_hyphen_min_code: print_esc("righthyphenmin");@+break;
+case holding_inserts_code: print_esc("holdinginserts");@+break;
+case error_context_lines_code: print_esc("errorcontextlines");@+break;
+@/@<Cases for |print_param|@>@/
+default:print("[unknown integer parameter!]");
+}
+}
+
+@ The integer parameter names must be entered into the hash table.
+
+@<Put each...@>=
+primitive("pretolerance", assign_int, int_base+pretolerance_code);@/
+@!@:pretolerance\_}{\.{\\pretolerance} primitive@>
+primitive("tolerance", assign_int, int_base+tolerance_code);@/
+@!@:tolerance\_}{\.{\\tolerance} primitive@>
+primitive("linepenalty", assign_int, int_base+line_penalty_code);@/
+@!@:line\_penalty\_}{\.{\\linepenalty} primitive@>
+primitive("hyphenpenalty", assign_int, int_base+hyphen_penalty_code);@/
+@!@:hyphen\_penalty\_}{\.{\\hyphenpenalty} primitive@>
+primitive("exhyphenpenalty", assign_int, int_base+ex_hyphen_penalty_code);@/
+@!@:ex\_hyphen\_penalty\_}{\.{\\exhyphenpenalty} primitive@>
+primitive("clubpenalty", assign_int, int_base+club_penalty_code);@/
+@!@:club\_penalty\_}{\.{\\clubpenalty} primitive@>
+primitive("widowpenalty", assign_int, int_base+widow_penalty_code);@/
+@!@:widow\_penalty\_}{\.{\\widowpenalty} primitive@>
+primitive("displaywidowpenalty",
+ assign_int, int_base+display_widow_penalty_code);@/
+@!@:display\_widow\_penalty\_}{\.{\\displaywidowpenalty} primitive@>
+primitive("brokenpenalty", assign_int, int_base+broken_penalty_code);@/
+@!@:broken\_penalty\_}{\.{\\brokenpenalty} primitive@>
+primitive("binoppenalty", assign_int, int_base+bin_op_penalty_code);@/
+@!@:bin\_op\_penalty\_}{\.{\\binoppenalty} primitive@>
+primitive("relpenalty", assign_int, int_base+rel_penalty_code);@/
+@!@:rel\_penalty\_}{\.{\\relpenalty} primitive@>
+primitive("predisplaypenalty", assign_int, int_base+pre_display_penalty_code);@/
+@!@:pre\_display\_penalty\_}{\.{\\predisplaypenalty} primitive@>
+primitive("postdisplaypenalty", assign_int, int_base+post_display_penalty_code);@/
+@!@:post\_display\_penalty\_}{\.{\\postdisplaypenalty} primitive@>
+primitive("interlinepenalty", assign_int, int_base+inter_line_penalty_code);@/
+@!@:inter\_line\_penalty\_}{\.{\\interlinepenalty} primitive@>
+primitive("doublehyphendemerits",
+ assign_int, int_base+double_hyphen_demerits_code);@/
+@!@:double\_hyphen\_demerits\_}{\.{\\doublehyphendemerits} primitive@>
+primitive("finalhyphendemerits",
+ assign_int, int_base+final_hyphen_demerits_code);@/
+@!@:final\_hyphen\_demerits\_}{\.{\\finalhyphendemerits} primitive@>
+primitive("adjdemerits", assign_int, int_base+adj_demerits_code);@/
+@!@:adj\_demerits\_}{\.{\\adjdemerits} primitive@>
+primitive("mag", assign_int, int_base+mag_code);@/
+@!@:mag\_}{\.{\\mag} primitive@>
+primitive("delimiterfactor", assign_int, int_base+delimiter_factor_code);@/
+@!@:delimiter\_factor\_}{\.{\\delimiterfactor} primitive@>
+primitive("looseness", assign_int, int_base+looseness_code);@/
+@!@:looseness\_}{\.{\\looseness} primitive@>
+primitive("time", assign_int, int_base+time_code);@/
+@!@:time\_}{\.{\\time} primitive@>
+primitive("day", assign_int, int_base+day_code);@/
+@!@:day\_}{\.{\\day} primitive@>
+primitive("month", assign_int, int_base+month_code);@/
+@!@:month\_}{\.{\\month} primitive@>
+primitive("year", assign_int, int_base+year_code);@/
+@!@:year\_}{\.{\\year} primitive@>
+primitive("showboxbreadth", assign_int, int_base+show_box_breadth_code);@/
+@!@:show\_box\_breadth\_}{\.{\\showboxbreadth} primitive@>
+primitive("showboxdepth", assign_int, int_base+show_box_depth_code);@/
+@!@:show\_box\_depth\_}{\.{\\showboxdepth} primitive@>
+primitive("hbadness", assign_int, int_base+hbadness_code);@/
+@!@:hbadness\_}{\.{\\hbadness} primitive@>
+primitive("vbadness", assign_int, int_base+vbadness_code);@/
+@!@:vbadness\_}{\.{\\vbadness} primitive@>
+primitive("pausing", assign_int, int_base+pausing_code);@/
+@!@:pausing\_}{\.{\\pausing} primitive@>
+primitive("tracingonline", assign_int, int_base+tracing_online_code);@/
+@!@:tracing\_online\_}{\.{\\tracingonline} primitive@>
+primitive("tracingmacros", assign_int, int_base+tracing_macros_code);@/
+@!@:tracing\_macros\_}{\.{\\tracingmacros} primitive@>
+primitive("tracingstats", assign_int, int_base+tracing_stats_code);@/
+@!@:tracing\_stats\_}{\.{\\tracingstats} primitive@>
+primitive("tracingparagraphs", assign_int, int_base+tracing_paragraphs_code);@/
+@!@:tracing\_paragraphs\_}{\.{\\tracingparagraphs} primitive@>
+primitive("tracingpages", assign_int, int_base+tracing_pages_code);@/
+@!@:tracing\_pages\_}{\.{\\tracingpages} primitive@>
+primitive("tracingoutput", assign_int, int_base+tracing_output_code);@/
+@!@:tracing\_output\_}{\.{\\tracingoutput} primitive@>
+primitive("tracinglostchars", assign_int, int_base+tracing_lost_chars_code);@/
+@!@:tracing\_lost\_chars\_}{\.{\\tracinglostchars} primitive@>
+primitive("tracingcommands", assign_int, int_base+tracing_commands_code);@/
+@!@:tracing\_commands\_}{\.{\\tracingcommands} primitive@>
+primitive("tracingrestores", assign_int, int_base+tracing_restores_code);@/
+@!@:tracing\_restores\_}{\.{\\tracingrestores} primitive@>
+primitive("uchyph", assign_int, int_base+uc_hyph_code);@/
+@!@:uc\_hyph\_}{\.{\\uchyph} primitive@>
+primitive("outputpenalty", assign_int, int_base+output_penalty_code);@/
+@!@:output\_penalty\_}{\.{\\outputpenalty} primitive@>
+primitive("maxdeadcycles", assign_int, int_base+max_dead_cycles_code);@/
+@!@:max\_dead\_cycles\_}{\.{\\maxdeadcycles} primitive@>
+primitive("hangafter", assign_int, int_base+hang_after_code);@/
+@!@:hang\_after\_}{\.{\\hangafter} primitive@>
+primitive("floatingpenalty", assign_int, int_base+floating_penalty_code);@/
+@!@:floating\_penalty\_}{\.{\\floatingpenalty} primitive@>
+primitive("globaldefs", assign_int, int_base+global_defs_code);@/
+@!@:global\_defs\_}{\.{\\globaldefs} primitive@>
+primitive("fam", assign_int, int_base+cur_fam_code);@/
+@!@:fam\_}{\.{\\fam} primitive@>
+primitive("escapechar", assign_int, int_base+escape_char_code);@/
+@!@:escape\_char\_}{\.{\\escapechar} primitive@>
+primitive("defaulthyphenchar", assign_int, int_base+default_hyphen_char_code);@/
+@!@:default\_hyphen\_char\_}{\.{\\defaulthyphenchar} primitive@>
+primitive("defaultskewchar", assign_int, int_base+default_skew_char_code);@/
+@!@:default\_skew\_char\_}{\.{\\defaultskewchar} primitive@>
+primitive("endlinechar", assign_int, int_base+end_line_char_code);@/
+@!@:end\_line\_char\_}{\.{\\endlinechar} primitive@>
+primitive("newlinechar", assign_int, int_base+new_line_char_code);@/
+@!@:new\_line\_char\_}{\.{\\newlinechar} primitive@>
+primitive("language", assign_int, int_base+language_code);@/
+@!@:language\_}{\.{\\language} primitive@>
+primitive("lefthyphenmin", assign_int, int_base+left_hyphen_min_code);@/
+@!@:left\_hyphen\_min\_}{\.{\\lefthyphenmin} primitive@>
+primitive("righthyphenmin", assign_int, int_base+right_hyphen_min_code);@/
+@!@:right\_hyphen\_min\_}{\.{\\righthyphenmin} primitive@>
+primitive("holdinginserts", assign_int, int_base+holding_inserts_code);@/
+@!@:holding\_inserts\_}{\.{\\holdinginserts} primitive@>
+primitive("errorcontextlines", assign_int, int_base+error_context_lines_code);@/
+@!@:error\_context\_lines\_}{\.{\\errorcontextlines} primitive@>
+
+@ @<Cases of |print_cmd_chr|...@>=
+case assign_int: if (chr_code < count_base) print_param(chr_code-int_base);
+ else{@+print_esc("count");print_int(chr_code-count_base);
+ } @+break;
+
+@ The integer parameters should really be initialized by a macro package;
+the following initialization does the minimum to keep \TeX\ from
+complete failure.
+@^null delimiter@>
+
+@<Initialize table entries...@>=
+for (k=int_base; k<=del_code_base-1; k++) eqtb[k].i=0;
+mag=1000;tolerance=10000;hang_after=1;max_dead_cycles=25;
+escape_char='\\';end_line_char=carriage_return;
+for (k=0; k<=255; k++) del_code(k)=-1;
+del_code('.')=0; /*this null delimiter is used in error recovery*/
+
+@ The following procedure, which is called just before \TeX\ initializes its
+input and output, establishes the initial values of the date and time.
+This does include too, for system integrators, the creation date and
+the reference moment for the timer---\Prote\ extensions. If the system
+supports environment variables, if |FORCE_SOURCE_DATE| is set to $1$ and
+|SOURCE_DATE_EPOCH| is set, the date related values: year, month, day
+and time, including creation date, will be taken relative from the value
+defined by |SOURCE_DATE_EPOCH|.
+@^creation date@>
+@^reference time@>
+@^system dependencies@>
+\TeX\ Live calls |tl_now| to obtain the current time as a |tm| structure.
+@p static void fix_date_and_time(void)
+{@+ struct tm *t=tl_now();
+ time=sys_time= t->tm_hour*60+t->tm_min;/*minutes since midnight*/
+ day=sys_day= t->tm_mday;/*day of the month*/
+ month=sys_month=t->tm_mon+1;/*month of the year*/
+ year=sys_year=t->tm_year+1900;/*Anno Domini*/
+}
+
+@ @<Show equivalent |n|, in region 5@>=
+{@+if (n < count_base) print_param(n-int_base);
+else if (n < del_code_base)
+ {@+print_esc("count");print_int(n-count_base);
+ }
+else{@+print_esc("delcode");print_int(n-del_code_base);
+ }
+print_char('=');print_int(eqtb[n].i);
+}
+
+@ @<Set variable |c| to the current escape character@>=c=escape_char
+
+@ @<Character |s| is the current new-line character@>=s==new_line_char
+
+@ \TeX\ is occasionally supposed to print diagnostic information that
+goes only into the transcript file, unless |tracing_online| is positive.
+Here are two routines that adjust the destination of print commands:
+
+@p static void begin_diagnostic(void) /*prepare to do some tracing*/
+{@+old_setting=selector;
+if ((tracing_online <= 0)&&(selector==term_and_log))
+ {@+decr(selector);
+ if (history==spotless) history=warning_issued;
+ }
+}
+@#
+static void end_diagnostic(bool @!blank_line)
+ /*restore proper conditions after tracing*/
+{@+print_nl("");
+if (blank_line) print_ln();
+selector=old_setting;
+}
+
+@ Of course we had better declare a few more global variables, if the previous
+routines are going to work.
+
+@<Glob...@>=
+static int @!old_setting;
+static int @!sys_time, @!sys_day, @!sys_month, @!sys_year;
+ /*date and time supplied by external system*/
+
+@ The final region of |eqtb| contains the dimension parameters defined
+here, and the 256 \.{\\dimen} registers.
+
+@d par_indent_code 0 /*indentation of paragraphs*/
+@d math_surround_code 1 /*space around math in text*/
+@d line_skip_limit_code 2 /*threshold for |line_skip| instead of |baseline_skip|*/
+@d hsize_code 3 /*line width in horizontal mode*/
+@d vsize_code 4 /*page height in vertical mode*/
+@d max_depth_code 5 /*maximum depth of boxes on main pages*/
+@d split_max_depth_code 6 /*maximum depth of boxes on split pages*/
+@d box_max_depth_code 7 /*maximum depth of explicit vboxes*/
+@d hfuzz_code 8 /*tolerance for overfull hbox messages*/
+@d vfuzz_code 9 /*tolerance for overfull vbox messages*/
+@d delimiter_shortfall_code 10 /*maximum amount uncovered by variable delimiters*/
+@d null_delimiter_space_code 11 /*blank space in null delimiters*/
+@d script_space_code 12 /*extra space after subscript or superscript*/
+@d pre_display_size_code 13 /*length of text preceding a display*/
+@d display_width_code 14 /*length of line for displayed equation*/
+@d display_indent_code 15 /*indentation of line for displayed equation*/
+@d overfull_rule_code 16 /*width of rule that identifies overfull hboxes*/
+@d hang_indent_code 17 /*amount of hanging indentation*/
+@d h_offset_code 18 /*amount of horizontal offset when shipping pages out*/
+@d v_offset_code 19 /*amount of vertical offset when shipping pages out*/
+@d emergency_stretch_code 20 /*reduces badnesses on final pass of line-breaking*/
+@d page_width_code 21 /*current paper page width*/
+@d page_height_code 22 /*current paper page height*/
+@d dimen_pars (pdftex_last_dimen_code+1) /*total number of dimension parameters*/
+@d scaled_base (dimen_base+dimen_pars)
+ /*table of 256 user-defined \.{\\dimen} registers*/
+@d eqtb_size (scaled_base+255) /*largest subscript of |eqtb|*/
+@#
+@d dimen(A) eqtb[scaled_base+A].sc
+@d dimen_par(A) eqtb[dimen_base+A].sc /*a scaled quantity*/
+@d par_indent dimen_par(par_indent_code)
+@d math_surround dimen_par(math_surround_code)
+@d line_skip_limit dimen_par(line_skip_limit_code)
+@d hsize dimen_par(hsize_code)
+@d vsize dimen_par(vsize_code)
+@d max_depth dimen_par(max_depth_code)
+@d split_max_depth dimen_par(split_max_depth_code)
+@d box_max_depth dimen_par(box_max_depth_code)
+@d hfuzz dimen_par(hfuzz_code)
+@d vfuzz dimen_par(vfuzz_code)
+@d delimiter_shortfall dimen_par(delimiter_shortfall_code)
+@d null_delimiter_space dimen_par(null_delimiter_space_code)
+@d script_space dimen_par(script_space_code)
+@d pre_display_size dimen_par(pre_display_size_code)
+@d display_width dimen_par(display_width_code)
+@d display_indent dimen_par(display_indent_code)
+@d overfull_rule dimen_par(overfull_rule_code)
+@d hang_indent dimen_par(hang_indent_code)
+@d h_offset dimen_par(h_offset_code)
+@d v_offset dimen_par(v_offset_code)
+@d emergency_stretch dimen_par(emergency_stretch_code)
+@d page_height dimen_par(page_height_code)
+
+@p static void print_length_param(int @!n)
+{@+switch (n) {
+case par_indent_code: print_esc("parindent");@+break;
+case math_surround_code: print_esc("mathsurround");@+break;
+case line_skip_limit_code: print_esc("lineskiplimit");@+break;
+case hsize_code: print_esc("hsize");@+break;
+case vsize_code: print_esc("vsize");@+break;
+case max_depth_code: print_esc("maxdepth");@+break;
+case split_max_depth_code: print_esc("splitmaxdepth");@+break;
+case box_max_depth_code: print_esc("boxmaxdepth");@+break;
+case hfuzz_code: print_esc("hfuzz");@+break;
+case vfuzz_code: print_esc("vfuzz");@+break;
+case delimiter_shortfall_code: print_esc("delimitershortfall");@+break;
+case null_delimiter_space_code: print_esc("nulldelimiterspace");@+break;
+case script_space_code: print_esc("scriptspace");@+break;
+case pre_display_size_code: print_esc("predisplaysize");@+break;
+case display_width_code: print_esc("displaywidth");@+break;
+case display_indent_code: print_esc("displayindent");@+break;
+case overfull_rule_code: print_esc("overfullrule");@+break;
+case hang_indent_code: print_esc("hangindent");@+break;
+case h_offset_code: print_esc("hoffset");@+break;
+case v_offset_code: print_esc("voffset");@+break;
+case emergency_stretch_code: print_esc("emergencystretch");@+break;
+case page_width_code:
+ if (pdf_on) print_esc("pdfpagewidth");
+ else print_esc("pagewidth");
+ @+break;
+case page_height_code:
+ if (pdf_on) print_esc("pdfpageheight");
+ else print_esc("pageheight");
+ @+break;
+@<Cases for |print_lenght_param|@>@;
+default:print("[unknown dimen parameter!]");
+}
+}
+
+@ @<Put each...@>=
+primitive("parindent", assign_dimen, dimen_base+par_indent_code);@/
+@!@:par\_indent\_}{\.{\\parindent} primitive@>
+primitive("mathsurround", assign_dimen, dimen_base+math_surround_code);@/
+@!@:math\_surround\_}{\.{\\mathsurround} primitive@>
+primitive("lineskiplimit", assign_dimen, dimen_base+line_skip_limit_code);@/
+@!@:line\_skip\_limit\_}{\.{\\lineskiplimit} primitive@>
+primitive("hsize", assign_dimen, dimen_base+hsize_code);@/
+@!@:hsize\_}{\.{\\hsize} primitive@>
+primitive("vsize", assign_dimen, dimen_base+vsize_code);@/
+@!@:vsize\_}{\.{\\vsize} primitive@>
+primitive("maxdepth", assign_dimen, dimen_base+max_depth_code);@/
+@!@:max\_depth\_}{\.{\\maxdepth} primitive@>
+primitive("splitmaxdepth", assign_dimen, dimen_base+split_max_depth_code);@/
+@!@:split\_max\_depth\_}{\.{\\splitmaxdepth} primitive@>
+primitive("boxmaxdepth", assign_dimen, dimen_base+box_max_depth_code);@/
+@!@:box\_max\_depth\_}{\.{\\boxmaxdepth} primitive@>
+primitive("hfuzz", assign_dimen, dimen_base+hfuzz_code);@/
+@!@:hfuzz\_}{\.{\\hfuzz} primitive@>
+primitive("vfuzz", assign_dimen, dimen_base+vfuzz_code);@/
+@!@:vfuzz\_}{\.{\\vfuzz} primitive@>
+primitive("delimitershortfall",
+ assign_dimen, dimen_base+delimiter_shortfall_code);@/
+@!@:delimiter\_shortfall\_}{\.{\\delimitershortfall} primitive@>
+primitive("nulldelimiterspace",
+ assign_dimen, dimen_base+null_delimiter_space_code);@/
+@!@:null\_delimiter\_space\_}{\.{\\nulldelimiterspace} primitive@>
+primitive("scriptspace", assign_dimen, dimen_base+script_space_code);@/
+@!@:script\_space\_}{\.{\\scriptspace} primitive@>
+primitive("predisplaysize", assign_dimen, dimen_base+pre_display_size_code);@/
+@!@:pre\_display\_size\_}{\.{\\predisplaysize} primitive@>
+primitive("displaywidth", assign_dimen, dimen_base+display_width_code);@/
+@!@:display\_width\_}{\.{\\displaywidth} primitive@>
+primitive("displayindent", assign_dimen, dimen_base+display_indent_code);@/
+@!@:display\_indent\_}{\.{\\displayindent} primitive@>
+primitive("overfullrule", assign_dimen, dimen_base+overfull_rule_code);@/
+@!@:overfull\_rule\_}{\.{\\overfullrule} primitive@>
+primitive("hangindent", assign_dimen, dimen_base+hang_indent_code);@/
+@!@:hang\_indent\_}{\.{\\hangindent} primitive@>
+primitive("hoffset", assign_dimen, dimen_base+h_offset_code);@/
+@!@:h\_offset\_}{\.{\\hoffset} primitive@>
+primitive("voffset", assign_dimen, dimen_base+v_offset_code);@/
+@!@:v\_offset\_}{\.{\\voffset} primitive@>
+primitive("emergencystretch", assign_dimen, dimen_base+emergency_stretch_code);@/
+@!@:emergency\_stretch\_}{\.{\\emergencystretch} primitive@>
+
+@ @<Cases of |print_cmd_chr|...@>=
+case assign_dimen: if (chr_code < scaled_base)
+ print_length_param(chr_code-dimen_base);
+ else{@+print_esc("dimen");print_int(chr_code-scaled_base);
+ } @+break;
+
+@ @<Initialize table entries...@>=
+for (k=dimen_base; k<=eqtb_size; k++) eqtb[k].sc=0;
+
+@ @<Show equivalent |n|, in region 6@>=
+{@+if (n < scaled_base) print_length_param(n-dimen_base);
+else{@+print_esc("dimen");print_int(n-scaled_base);
+ }
+print_char('=');print_scaled(eqtb[n].sc);print("pt");
+}
+
+@ Here is a procedure that displays the contents of |eqtb[n]|
+symbolically.
+
+@p @t\4@>@<Declare the procedure called |print_cmd_chr|@>@;@/
+#ifdef @!STAT
+static void show_eqtb(pointer @!n)
+{@+if (n < active_base) print_char('?'); /*this can't happen*/
+else if (n < glue_base) @<Show equivalent |n|, in region 1 or 2@>@;
+else if (n < local_base) @<Show equivalent |n|, in region 3@>@;
+else if (n < int_base) @<Show equivalent |n|, in region 4@>@;
+else if (n < dimen_base) @<Show equivalent |n|, in region 5@>@;
+else if (n <= eqtb_size) @<Show equivalent |n|, in region 6@>@;
+else print_char('?'); /*this can't happen either*/
+}
+#endif
+
+@ The last two regions of |eqtb| have fullword values instead of the
+three fields |eq_level|, |eq_type|, and |equiv|. An |eq_type| is unnecessary,
+but \TeX\ needs to store the |eq_level| information in another array
+called |xeq_level|.
+
+@<Glob...@>=
+static memory_word @!eqtb0[eqtb_size-active_base+1],
+ *const @!eqtb = @!eqtb0-active_base;
+static quarterword @!xeq_level0[eqtb_size-int_base+1],
+ *const @!xeq_level = @!xeq_level0-int_base;
+
+@ @<Set init...@>=
+for (k=int_base; k<=eqtb_size; k++) xeq_level[k]=level_one;
+
+@ When the debugging routine |search_mem| is looking for pointers having a
+given value, it is interested only in regions 1 to~3 of~|eqtb|, and in the
+first part of region~4.
+
+@<Search |eqtb| for equivalents equal to |p|@>=
+for (q=active_base; q<=box_base+255; q++)
+ {@+if (equiv(q)==p)
+ {@+print_nl("EQUIV(");print_int(q);print_char(')');
+ }
+ }
+
+@* The hash table.
+Control sequences are stored and retrieved by means of a fairly standard hash
+table algorithm called the method of ``coalescing lists'' (cf.\ Algorithm 6.4C
+in {\sl The Art of Computer Programming\/}). Once a control sequence enters the
+table, it is never removed, because there are complicated situations
+involving \.{\\gdef} where the removal of a control sequence at the end of
+a group would be a mistake preventable only by the introduction of a
+complicated reference-count mechanism.
+
+The actual sequence of letters forming a control sequence identifier is
+stored in the |str_pool| array together with all the other strings. An
+auxiliary array |hash| consists of items with two halfword fields per
+word. The first of these, called |next(p)|, points to the next identifier
+belonging to the same coalesced list as the identifier corresponding to~|p|;
+and the other, called |text(p)|, points to the |str_start| entry for
+|p|'s identifier. If position~|p| of the hash table is empty, we have
+|text(p)==0|; if position |p| is either empty or the end of a coalesced
+hash list, we have |next(p)==0|. An auxiliary pointer variable called
+|hash_used| is maintained in such a way that all locations |p >= hash_used|
+are nonempty. The global variable |cs_count| tells how many multiletter
+control sequences have been defined, if statistics are being kept.
+
+A global boolean variable called |no_new_control_sequence| is set to
+|true| during the time that new hash table entries are forbidden.
+
+@d next(A) hash[A].lh /*link for coalesced lists*/
+@d text(A) hash[A].rh /*string number for control sequence name*/
+@d hash_is_full (hash_used==hash_base) /*test if all positions are occupied*/
+@d font_id_text(A) text(font_id_base+A) /*a frozen font identifier's name*/
+
+@<Glob...@>=
+static two_halves @!hash0[undefined_control_sequence-hash_base],
+ *const @!hash = @!hash0-hash_base;
+ /*the hash table*/
+static pointer @!hash_used; /*allocation pointer for |hash|*/
+static bool @!no_new_control_sequence; /*are new identifiers legal?*/
+static int @!cs_count; /*total number of known identifiers*/
+
+@ @<Set init...@>=
+no_new_control_sequence=true; /*new identifiers are usually forbidden*/
+next(hash_base)=0;text(hash_base)=0;
+for (k=hash_base+1; k<=undefined_control_sequence-1; k++) hash[k]=hash[hash_base];
+
+@ @<Initialize table entries...@>=
+hash_used=frozen_control_sequence; /*nothing is used*/
+cs_count=0;
+eq_type(frozen_dont_expand)=dont_expand;
+text(frozen_dont_expand)=s_no("notexpanded:");
+@.notexpanded:@>
+
+@ Here is the subroutine that searches the hash table for an identifier
+that matches a given string of length |l > 1| appearing in |buffer[j dotdot
+(j+l-1)]|. If the identifier is found, the corresponding hash table address
+is returned. Otherwise, if the global variable |no_new_control_sequence|
+is |true|, the dummy address |undefined_control_sequence| is returned.
+Otherwise the identifier is inserted into the hash table and its location
+is returned.
+
+@p static pointer id_lookup(int @!j, int @!l) /*search the hash table*/
+{@+ /*go here if you found it*/
+int h; /*hash code*/
+int @!d; /*number of characters in incomplete current string*/
+pointer @!p; /*index in |hash| array*/
+int @!k; /*index in |buffer| array*/
+@<Compute the hash code |h|@>;
+p=h+hash_base; /*we start searching here; note that |0 <= h < hash_prime|*/
+loop@+{@+if (text(p) > 0) if (length(text(p))==l)
+ if (str_eq_buf(text(p), j)) goto found;
+ if (next(p)==0)
+ {@+if (no_new_control_sequence)
+ p=undefined_control_sequence;
+ else@<Insert a new control sequence after |p|, then make |p| point to
+it@>;
+ goto found;
+ }
+ p=next(p);
+ }
+found: return p;
+}
+
+@ @<Insert a new control...@>=
+{@+if (text(p) > 0)
+ {@+@/do@+{if (hash_is_full) overflow("hash size", hash_size);
+@:TeX capacity exceeded hash size}{\quad hash size@>
+ decr(hash_used);
+ }@+ while (!(text(hash_used)==0)); /*search for an empty location in |hash|*/
+ next(p)=hash_used;p=hash_used;
+ }
+str_room(l);d=cur_length;
+while (pool_ptr > str_start[str_ptr])
+ {@+decr(pool_ptr);str_pool[pool_ptr+l]=str_pool[pool_ptr];
+ } /*move current string up to make room for another*/
+for (k=j; k<=j+l-1; k++) append_char(buffer[k]);
+text(p)=make_string();pool_ptr=pool_ptr+d;
+#ifdef @!STAT
+incr(cs_count);
+#endif
+@;@/
+}
+
+@ The value of |hash_prime| should be roughly 85\pct! of |hash_size|, and it
+should be a prime number. The theory of hashing tells us to expect fewer
+than two table probes, on the average, when the search is successful.
+[See J.~S. Vitter, {\sl Journal of the ACM\/ \bf30} (1983), 231--258.]
+@^Vitter, Jeffrey Scott@>
+
+@<Compute the hash code |h|@>=
+h=buffer[j];
+for (k=j+1; k<=j+l-1; k++)
+ {@+h=h+h+buffer[k];
+ while (h >= hash_prime) h=h-hash_prime;
+ }
+
+@ Single-character control sequences do not need to be looked up in a hash
+table, since we can use the character code itself as a direct address.
+The procedure |print_cs| prints the name of a control sequence, given
+a pointer to its address in |eqtb|. A space is printed after the name
+unless it is a single nonletter or an active character. This procedure
+might be invoked with invalid data, so it is ``extra robust.'' The
+individual characters must be printed one at a time using |print|, since
+they may be unprintable.
+
+@<Basic printing...@>=
+static void print_cs(int @!p) /*prints a purported control sequence*/
+{@+if (p < hash_base) /*single character*/
+ if (p >= single_base)
+ if (p==null_cs)
+ {@+print_esc("csname");print_esc("endcsname");print_char(' ');
+ }
+ else{@+printn_esc(p-single_base);
+ if (cat_code(p-single_base)==letter) print_char(' ');
+ }
+ else if (p < active_base) print_esc("IMPOSSIBLE.");
+@.IMPOSSIBLE@>
+ else printn(p-active_base);
+else if (p >= undefined_control_sequence) print_esc("IMPOSSIBLE.");
+else if ((text(p) < 0)||(text(p) >= str_ptr)) print_esc("NONEXISTENT.");
+@.NONEXISTENT@>
+else{@+if (p==frozen_primitive) print_esc("primitive");
+ printn_esc(text(p));print_char(' ');
+ }
+}
+
+@ Here is a similar procedure; it avoids the error checks, and it never
+prints a space after the control sequence.
+
+@<Basic printing procedures@>=
+static void sprint_cs(pointer @!p) /*prints a control sequence*/
+{@+if (p < hash_base)
+ if (p < single_base) printn(p-active_base);
+ else if (p < null_cs) printn_esc(p-single_base);
+ else{@+print_esc("csname");print_esc("endcsname");
+ }
+else printn_esc(text(p));
+}
+
+@ We need to put \TeX's ``primitive'' control sequences into the hash
+table, together with their command code (which will be the |eq_type|)
+and an operand (which will be the |equiv|). The |primitive| procedure
+does this, in a way that no \TeX\ user can. The global value |cur_val|
+contains the new |eqtb| pointer after |primitive| has acted.
+
+@p
+#ifdef @!INIT
+static void primitive(char *@!str, quarterword @!c, halfword @!o)
+{@+str_number s=s_no(str);
+int k; /*index into |str_pool|*/
+int @!j; /*index into |buffer|*/
+small_number @!l; /*length of the string*/
+pointer @!p; /*pointer in |ROM|*/
+if (s < 256) cur_val=s+single_base;
+else{@+k=str_start[s];l=str_start[s+1]-k;
+ /*we will move |s| into the (possibly non-empty) |buffer|*/
+ if (first+l > buf_size+1)
+ overflow("buffer size", buf_size);
+@:TeX capacity exceeded buffer size}{\quad buffer size@>
+ for (j=0; j<=l-1; j++) buffer[first+j]=so(str_pool[k+j]);
+ cur_val=id_lookup(first, l); /*|no_new_control_sequence| is |false|*/
+ flush_string;text(cur_val)=s; /*we don't want to have the string twice*/
+ }
+eq_level(cur_val)=level_one;eq_type(cur_val)=c;equiv(cur_val)=o;
+@<Add primitive definition to the |ROM| array@>;
+}
+#endif
+
+@ Many of \TeX's primitives need no |equiv|, since they are identifiable
+by their |eq_type| alone. These primitives are loaded into the hash table
+as follows:
+
+@<Put each of \TeX's primitives into the hash table@>=
+primitive(" ", ex_space, 0);@/
+@!@:Single-character primitives /}{\quad\.{\\\ }@>
+primitive("/", ital_corr, 0);@/
+@!@:Single-character primitives /}{\quad\.{\\/}@>
+primitive("accent", accent, 0);@/
+@!@:accent\_}{\.{\\accent} primitive@>
+primitive("advance", advance, 0);@/
+@!@:advance\_}{\.{\\advance} primitive@>
+primitive("afterassignment", after_assignment, 0);@/
+@!@:after\_assignment\_}{\.{\\afterassignment} primitive@>
+primitive("aftergroup", after_group, 0);@/
+@!@:after\_group\_}{\.{\\aftergroup} primitive@>
+primitive("begingroup", begin_group, 0);@/
+@!@:begin\_group\_}{\.{\\begingroup} primitive@>
+primitive("char", char_num, 0);@/
+@!@:char\_}{\.{\\char} primitive@>
+primitive("csname", cs_name, 0);@/
+@!@:cs\_name\_}{\.{\\csname} primitive@>
+primitive("delimiter", delim_num, 0);@/
+@!@:delimiter\_}{\.{\\delimiter} primitive@>
+primitive("divide", divide, 0);@/
+@!@:divide\_}{\.{\\divide} primitive@>
+primitive("endcsname", end_cs_name, 0);@/
+@!@:end\_cs\_name\_}{\.{\\endcsname} primitive@>
+primitive("endgroup", end_group, 0);
+@!@:end\_group\_}{\.{\\endgroup} primitive@>
+text(frozen_end_group)=text(cur_val);eqtb[frozen_end_group]=eqtb[cur_val];@/
+primitive("expandafter", expand_after, 0);@/
+@!@:expand\_after\_}{\.{\\expandafter} primitive@>
+primitive("font", def_font, 0);@/
+@!@:font\_}{\.{\\font} primitive@>
+primitive("fontdimen", assign_font_dimen, 0);@/
+@!@:font\_dimen\_}{\.{\\fontdimen} primitive@>
+primitive("halign", halign, 0);@/
+@!@:halign\_}{\.{\\halign} primitive@>
+primitive("hrule", hrule, 0);@/
+@!@:hrule\_}{\.{\\hrule} primitive@>
+primitive("ignorespaces", ignore_spaces, 0);@/
+@!@:ignore\_spaces\_}{\.{\\ignorespaces} primitive@>
+primitive("insert", insert, 0);@/
+@!@:insert\_}{\.{\\insert} primitive@>
+primitive("mark", mark, 0);@/
+@!@:mark\_}{\.{\\mark} primitive@>
+primitive("mathaccent", math_accent, 0);@/
+@!@:math\_accent\_}{\.{\\mathaccent} primitive@>
+primitive("mathchar", math_char_num, 0);@/
+@!@:math\_char\_}{\.{\\mathchar} primitive@>
+primitive("mathchoice", math_choice, 0);@/
+@!@:math\_choice\_}{\.{\\mathchoice} primitive@>
+primitive("multiply", multiply, 0);@/
+@!@:multiply\_}{\.{\\multiply} primitive@>
+primitive("noalign", no_align, 0);@/
+@!@:no\_align\_}{\.{\\noalign} primitive@>
+primitive("noboundary", no_boundary, 0);@/
+@!@:no\_boundary\_}{\.{\\noboundary} primitive@>
+primitive("noexpand", no_expand, 0);@/
+@!@:no\_expand\_}{\.{\\noexpand} primitive@>
+primitive("nonscript", non_script, 0);@/
+@!@:non\_script\_}{\.{\\nonscript} primitive@>
+primitive("omit", omit, 0);@/
+@!@:omit\_}{\.{\\omit} primitive@>
+primitive("parshape", set_shape, par_shape_loc);@/
+@!@:par\_shape\_}{\.{\\parshape} primitive@>
+primitive("penalty", break_penalty, 0);@/
+@!@:penalty\_}{\.{\\penalty} primitive@>
+primitive("prevgraf", set_prev_graf, 0);@/
+@!@:prev\_graf\_}{\.{\\prevgraf} primitive@>
+primitive("radical", radical, 0);@/
+@!@:radical\_}{\.{\\radical} primitive@>
+primitive("read", read_to_cs, 0);@/
+@!@:read\_}{\.{\\read} primitive@>
+primitive("relax", relax, 256); /*cf.\ |scan_file_name|*/
+@!@:relax\_}{\.{\\relax} primitive@>
+text(frozen_relax)=text(cur_val);eqtb[frozen_relax]=eqtb[cur_val];@/
+primitive("setbox", set_box, 0);@/
+@!@:set\_box\_}{\.{\\setbox} primitive@>
+primitive("the", the, 0);@/
+@!@:the\_}{\.{\\the} primitive@>
+primitive("toks", toks_register, mem_bot);@/
+@!@:toks\_}{\.{\\toks} primitive@>
+primitive("vadjust", vadjust, 0);@/
+@!@:vadjust\_}{\.{\\vadjust} primitive@>
+primitive("valign", valign, 0);@/
+@!@:valign\_}{\.{\\valign} primitive@>
+primitive("vcenter", vcenter, 0);@/
+@!@:vcenter\_}{\.{\\vcenter} primitive@>
+primitive("vrule", vrule, 0);@/
+@!@:vrule\_}{\.{\\vrule} primitive@>
+
+@ Each primitive has a corresponding inverse, so that it is possible to
+display the cryptic numeric contents of |eqtb| in symbolic form.
+Every call of |primitive| in this program is therefore accompanied by some
+straightforward code that forms part of the |print_cmd_chr| routine
+below.
+
+@<Cases of |print_cmd_chr|...@>=
+case accent: print_esc("accent");@+break;
+case advance: print_esc("advance");@+break;
+case after_assignment: print_esc("afterassignment");@+break;
+case after_group: print_esc("aftergroup");@+break;
+case assign_font_dimen: print_esc("fontdimen");@+break;
+case begin_group: print_esc("begingroup");@+break;
+case break_penalty: print_esc("penalty");@+break;
+case char_num: print_esc("char");@+break;
+case cs_name: print_esc("csname");@+break;
+case def_font: print_esc("font");@+break;
+case delim_num: print_esc("delimiter");@+break;
+case divide: print_esc("divide");@+break;
+case end_cs_name: print_esc("endcsname");@+break;
+case end_group: print_esc("endgroup");@+break;
+case ex_space: print_esc(" ");@+break;
+case expand_after: switch (chr_code) {
+case 0: print_esc("expandafter");@+break;
+@/@<Cases of |expandafter| for |print_cmd_chr|@>@/
+} @+break; /*there are no other cases*/
+case halign: print_esc("halign");@+break;
+case hrule: print_esc("hrule");@+break;
+case ignore_spaces: print_esc("ignorespaces");@+break;
+case insert: print_esc("insert");@+break;
+case ital_corr: print_esc("/");@+break;
+case mark: {@+print_esc("mark");
+ if (chr_code > 0) print_char('s');
+ } @+break;
+case math_accent: print_esc("mathaccent");@+break;
+case math_char_num: print_esc("mathchar");@+break;
+case math_choice: print_esc("mathchoice");@+break;
+case multiply: print_esc("multiply");@+break;
+case no_align: print_esc("noalign");@+break;
+case no_boundary: print_esc("noboundary");@+break;
+case no_expand: print_esc("noexpand");@+break;
+case non_script: print_esc("nonscript");@+break;
+case omit: print_esc("omit");@+break;
+case radical: print_esc("radical");@+break;
+case read_to_cs: if (chr_code==0) print_esc("read")
+ @<Cases of |read| for |print_cmd_chr|@>;@+break;
+case relax: print_esc("relax");@+break;
+case set_box: print_esc("setbox");@+break;
+case set_prev_graf: print_esc("prevgraf");@+break;
+case set_shape: switch (chr_code) {
+ case par_shape_loc: print_esc("parshape");@+break;
+ @<Cases of |set_shape| for |print_cmd_chr|@>@;@/
+ } @+break; /*there are no other cases*/
+case the: if (chr_code==0) print_esc("the")
+ @<Cases of |the| for |print_cmd_chr|@>;@+break;
+case toks_register: @<Cases of |toks_register| for |print_cmd_chr|@>@;@+break;
+case vadjust: print_esc("vadjust");@+break;
+case valign: print_esc("valign");@+break;
+case vcenter: print_esc("vcenter");@+break;
+case vrule: print_esc("vrule");@+break;
+
+@ We will deal with the other primitives later, at some point in the program
+where their |eq_type| and |equiv| values are more meaningful. For example,
+the primitives for math mode will be loaded when we consider the routines
+that deal with formulas. It is easy to find where each particular
+primitive was treated by looking in the index at the end; for example, the
+section where |"radical"| entered |eqtb| is listed under `\.{\\radical}
+primitive'. (Primitives consisting of a single nonalphabetic character,
+@!like `\.{\\/}', are listed under `Single-character primitives'.)
+@!@^Single-character primitives@>
+
+Meanwhile, this is a convenient place to catch up on something we were unable
+to do before the hash table was defined:
+
+@<Print the font identifier for |font(p)|@>=
+printn_esc(font_id_text(font(p)))
+
+@* Saving and restoring equivalents.
+The nested structure provided by `$\.{\char'173}\ldots\.{\char'175}$' groups
+in \TeX\ means that |eqtb| entries valid in outer groups should be saved
+and restored later if they are overridden inside the braces. When a new |eqtb|
+value is being assigned, the program therefore checks to see if the previous
+entry belongs to an outer level. In such a case, the old value is placed
+on the |save_stack| just before the new value enters |eqtb|. At the
+end of a grouping level, i.e., when the right brace is sensed, the
+|save_stack| is used to restore the outer values, and the inner ones are
+destroyed.
+
+Entries on the |save_stack| are of type |memory_word|. The top item on
+this stack is |save_stack[p]|, where |p==save_ptr-1|; it contains three
+fields called |save_type|, |save_level|, and |save_index|, and it is
+interpreted in one of five ways:
+
+\yskip\hangg 1) If |save_type(p)==restore_old_value|, then
+|save_index(p)| is a location in |eqtb| whose current value should
+be destroyed at the end of the current group and replaced by |save_stack[p-1]|.
+Furthermore if |save_index(p) >= int_base|, then |save_level(p)|
+should replace the corresponding entry in |xeq_level|.
+
+\yskip\hangg 2) If |save_type(p)==restore_zero|, then |save_index(p)|
+is a location in |eqtb| whose current value should be destroyed at the end
+of the current group, when it should be
+replaced by the value of |eqtb[undefined_control_sequence]|.
+
+\yskip\hangg 3) If |save_type(p)==insert_token|, then |save_index(p)|
+is a token that should be inserted into \TeX's input when the current
+group ends.
+
+\yskip\hangg 4) If |save_type(p)==level_boundary|, then |save_level(p)|
+is a code explaining what kind of group we were previously in, and
+|save_index(p)| points to the level boundary word at the bottom of
+the entries for that group.
+Furthermore, in extended \eTeX\ mode, |save_stack[p-1]| contains the
+source line number at which the current level of grouping was entered.
+
+\yskip\hang 5) If |save_type(p)==restore_sa|, then |sa_chain| points to a
+chain of sparse array entries to be restored at the end of the current
+group. Furthermore |save_index(p)| and |save_level(p)| should replace
+the values of |sa_chain| and |sa_level| respectively.
+
+@d save_type(A) save_stack[A].hh.b0 /*classifies a |save_stack| entry*/
+@d save_level(A) save_stack[A].hh.b1
+ /*saved level for regions 5 and 6, or group code*/
+@d save_index(A) save_stack[A].hh.rh
+ /*|eqtb| location or token or |save_stack| location*/
+@d restore_old_value 0 /*|save_type| when a value should be restored later*/
+@d restore_zero 1 /*|save_type| when an undefined entry should be restored*/
+@d insert_token 2 /*|save_type| when a token is being saved for later use*/
+@d level_boundary 3 /*|save_type| corresponding to beginning of group*/
+@d restore_sa 4 /*|save_type| when sparse array entries should be restored*/
+
+@p @t\4@>@<Declare \eTeX\ procedures for tracing and input@>@;
+
+@ Here are the group codes that are used to discriminate between different
+kinds of groups. They allow \TeX\ to decide what special actions, if any,
+should be performed when a group ends.
+\def\grp{\.{\char'173...\char'175}}
+
+Some groups are not supposed to be ended by right braces. For example,
+the `\.\$' that begins a math formula causes a |math_shift_group| to
+be started, and this should be terminated by a matching `\.\$'. Similarly,
+a group that starts with \.{\\left} should end with \.{\\right}, and
+one that starts with \.{\\begingroup} should end with \.{\\endgroup}.
+
+@d bottom_level 0 /*group code for the outside world*/
+@d simple_group 1 /*group code for local structure only*/
+@d hbox_group 2 /*code for `\.{\\hbox}\grp'*/
+@d adjusted_hbox_group 3 /*code for `\.{\\hbox}\grp' in vertical mode*/
+@d vbox_group 4 /*code for `\.{\\vbox}\grp'*/
+@d vtop_group 5 /*code for `\.{\\vtop}\grp'*/
+@d align_group 6 /*code for `\.{\\halign}\grp', `\.{\\valign}\grp'*/
+@d no_align_group 7 /*code for `\.{\\noalign}\grp'*/
+@d output_group 8 /*code for output routine*/
+@d math_group 9 /*code for, e.g., `\.{\char'136}\grp'*/
+@d disc_group 10 /*code for `\.{\\discretionary}\grp\grp\grp'*/
+@d insert_group 11 /*code for `\.{\\insert}\grp', `\.{\\vadjust}\grp'*/
+@d vcenter_group 12 /*code for `\.{\\vcenter}\grp'*/
+@d math_choice_group 13 /*code for `\.{\\mathchoice}\grp\grp\grp\grp'*/
+@d semi_simple_group 14 /*code for `\.{\\begingroup...\\endgroup}'*/
+@d math_shift_group 15 /*code for `\.{\$...\$}'*/
+@d math_left_group 16 /*code for `\.{\\left...\\right}'*/
+@d max_group_code 16
+
+@<Types...@>=
+typedef int8_t group_code; /*|save_level| for a level boundary*/
+
+@ The global variable |cur_group| keeps track of what sort of group we are
+currently in. Another global variable, |cur_boundary|, points to the
+topmost |level_boundary| word. And |cur_level| is the current depth of
+nesting. The routines are designed to preserve the condition that no entry
+in the |save_stack| or in |eqtb| ever has a level greater than |cur_level|.
+
+@ @<Glob...@>=
+static memory_word @!save_stack[save_size+1];
+static int @!save_ptr; /*first unused entry on |save_stack|*/
+static int @!max_save_stack; /*maximum usage of save stack*/
+static quarterword @!cur_level; /*current nesting level for groups*/
+static group_code @!cur_group; /*current group type*/
+static int @!cur_boundary; /*where the current level begins*/
+
+@ At this time it might be a good idea for the reader to review the introduction
+to |eqtb| that was given above just before the long lists of parameter names.
+Recall that the ``outer level'' of the program is |level_one|, since
+undefined control sequences are assumed to be ``defined'' at |level_zero|.
+
+@<Set init...@>=
+save_ptr=0;cur_level=level_one;cur_group=bottom_level;cur_boundary=0;
+max_save_stack=0;
+
+@ The following macro is used to test if there is room for up to seven more
+entries on |save_stack|. By making a conservative test like this, we can
+get by with testing for overflow in only a few places.
+
+@d check_full_save_stack if (save_ptr > max_save_stack)
+ {@+max_save_stack=save_ptr;
+ if (max_save_stack > save_size-7) overflow("save size", save_size);
+@:TeX capacity exceeded save size}{\quad save size@>
+ }
+
+@ Procedure |new_save_level| is called when a group begins. The
+argument is a group identification code like `|hbox_group|'. After
+calling this routine, it is safe to put five more entries on |save_stack|.
+
+In some cases integer-valued items are placed onto the
+|save_stack| just below a |level_boundary| word, because this is a
+convenient place to keep information that is supposed to ``pop up'' just
+when the group has finished.
+For example, when `\.{\\hbox to 100pt}\grp' is being treated, the 100pt
+dimension is stored on |save_stack| just before |new_save_level| is
+called.
+
+We use the notation |saved(k)| to stand for an integer item that
+appears in location |save_ptr+k| of the save stack.
+
+@d saved(A) save_stack[save_ptr+A].i
+
+@p static void new_save_level(group_code @!c) /*begin a new level of grouping*/
+{@+check_full_save_stack;
+if (eTeX_ex)
+ {@+saved(0)=line;incr(save_ptr);
+ }
+save_type(save_ptr)=level_boundary;save_level(save_ptr)=cur_group;
+save_index(save_ptr)=cur_boundary;
+if (cur_level==max_quarterword) overflow("grouping levels",
+@:TeX capacity exceeded grouping levels}{\quad grouping levels@>
+ max_quarterword-min_quarterword);
+ /*quit if |(cur_level+1)| is too big to be stored in |eqtb|*/
+cur_boundary=save_ptr;cur_group=c;
+#ifdef @!STAT
+if (tracing_groups > 0) group_trace(false);
+#endif
+@;@/
+incr(cur_level);incr(save_ptr);
+}
+
+@ Just before an entry of |eqtb| is changed, the following procedure should
+be called to update the other data structures properly. It is important
+to keep in mind that reference counts in |mem| include references from
+within |save_stack|, so these counts must be handled carefully.
+@^reference counts@>
+
+@p static void eq_destroy(memory_word @!w) /*gets ready to forget |w|*/
+{@+pointer q; /*|equiv| field of |w|*/
+switch (eq_type_field(w)) {
+case call: case long_call: case outer_call:
+ case long_outer_call: delete_token_ref(equiv_field(w));@+break;
+case glue_ref: delete_glue_ref(equiv_field(w));@+break;
+case shape_ref: {@+q=equiv_field(w); /*we need to free a \.{\\parshape} block*/
+ if (q!=null) free_node(q, info(q)+info(q)+1);
+ } @+break; /*such a block is |2 n+1| words long, where |n==info(q)|*/
+case box_ref: flush_node_list(equiv_field(w));@+break;
+@/@<Cases for |eq_destroy|@>@/
+default:do_nothing;
+}
+}
+
+@ To save a value of |eqtb[p]| that was established at level |l|, we
+can use the following subroutine.
+
+@p static void eq_save(pointer @!p, quarterword @!l) /*saves |eqtb[p]|*/
+{@+check_full_save_stack;
+if (l==level_zero) save_type(save_ptr)=restore_zero;
+else{@+save_stack[save_ptr]=eqtb[p];incr(save_ptr);
+ save_type(save_ptr)=restore_old_value;
+ }
+save_level(save_ptr)=l;save_index(save_ptr)=p;incr(save_ptr);
+}
+
+@ The procedure |eq_define| defines an |eqtb| entry having specified
+|eq_type| and |equiv| fields, and saves the former value if appropriate.
+This procedure is used only for entries in the first four regions of |eqtb|,
+i.e., only for entries that have |eq_type| and |equiv| fields.
+After calling this routine, it is safe to put four more entries on
+|save_stack|, provided that there was room for four more entries before
+the call, since |eq_save| makes the necessary test.
+
+@p
+#ifdef @!STAT
+#define assign_trace(A, B) if (tracing_assigns > 0) restore_trace(A, B);
+#else
+#define assign_trace(A, B)
+#endif
+
+static void eq_define(pointer @!p, quarterword @!t, halfword @!e)
+ /*new data for |eqtb|*/
+{@+
+if (eTeX_ex&&(eq_type(p)==t)&&(equiv(p)==e))
+ {@+assign_trace(p,"reassigning")@;@/
+ eq_destroy(eqtb[p]);return;
+ }
+assign_trace(p,"changing")@;@/
+if (eq_level(p)==cur_level) eq_destroy(eqtb[p]);
+else if (cur_level > level_one) eq_save(p, eq_level(p));
+eq_level(p)=cur_level;eq_type(p)=t;equiv(p)=e;
+assign_trace(p,"into")@;@/
+}
+
+@ The counterpart of |eq_define| for the remaining (fullword) positions in
+|eqtb| is called |eq_word_define|. Since |xeq_level[p] >= level_one| for all
+|p|, a `|restore_zero|' will never be used in this case.
+
+@p static void eq_word_define(pointer @!p, int @!w)
+{@+
+if (eTeX_ex&&(eqtb[p].i==w))
+ {@+assign_trace(p,"reassigning")@;@/
+ return;
+ }
+assign_trace(p,"changing")@;@/
+if (xeq_level[p]!=cur_level)
+ {@+eq_save(p, xeq_level[p]);xeq_level[p]=cur_level;
+ }
+eqtb[p].i=w;
+assign_trace(p,"into")@;@/
+}
+
+@ The |eq_define| and |eq_word_define| routines take care of local definitions.
+@^global definitions@>
+Global definitions are done in almost the same way, but there is no need
+to save old values, and the new value is associated with |level_one|.
+
+@p static void geq_define(pointer @!p, quarterword @!t, halfword @!e)
+ /*global |eq_define|*/
+{@+assign_trace(p,"globally changing")@;@/
+{@+eq_destroy(eqtb[p]);
+eq_level(p)=level_one;eq_type(p)=t;equiv(p)=e;
+}
+assign_trace(p,"into");@/
+}
+@#
+static void geq_word_define(pointer @!p, int @!w) /*global |eq_word_define|*/
+{@+assign_trace(p,"globally changing")@;@/
+{@+eqtb[p].i=w;xeq_level[p]=level_one;
+}
+assign_trace(p,"into");@/
+}
+
+@ Subroutine |save_for_after| puts a token on the stack for save-keeping.
+
+@p static void save_for_after(halfword @!t)
+{@+if (cur_level > level_one)
+ {@+check_full_save_stack;
+ save_type(save_ptr)=insert_token;save_level(save_ptr)=level_zero;
+ save_index(save_ptr)=t;incr(save_ptr);
+ }
+}
+
+@ The |unsave| routine goes the other way, taking items off of |save_stack|.
+This routine takes care of restoration when a level ends; everything
+belonging to the topmost group is cleared off of the save stack.
+
+@p
+static void back_input(void);
+static void unsave(void) /*pops the top level off the save stack*/
+{@+
+pointer p; /*position to be restored*/
+quarterword @!l; /*saved level, if in fullword regions of |eqtb|*/
+halfword @!t; /*saved value of |cur_tok|*/
+bool @!a; /*have we already processed an \.{\\aftergroup} ?*/
+a=false;
+if (cur_level > level_one)
+ {@+decr(cur_level);
+ @<Clear off top level from |save_stack|@>;
+ }
+else confusion("curlevel"); /*|unsave| is not used when |cur_group==bottom_level|*/
+@:this can't happen curlevel}{\quad curlevel@>
+}
+
+@ @<Clear off...@>=
+loop@+{@+decr(save_ptr);
+ if (save_type(save_ptr)==level_boundary) goto done;
+ p=save_index(save_ptr);
+ if (save_type(save_ptr)==insert_token)
+ @<Insert token |p| into \TeX's input@>@;
+ else if (save_type(save_ptr)==restore_sa)
+ {@+sa_restore();sa_chain=p;sa_level=save_level(save_ptr);
+ }
+ else{@+if (save_type(save_ptr)==restore_old_value)
+ {@+l=save_level(save_ptr);decr(save_ptr);
+ }
+ else save_stack[save_ptr]=eqtb[undefined_control_sequence];
+ @<Store \(s)|save_stack[save_ptr]| in |eqtb[p]|, unless |eqtb[p]| holds
+a global value@>;
+ }
+ }
+done:
+#ifdef @!STAT
+if (tracing_groups > 0) group_trace(true);
+#endif
+@;@/
+if (grp_stack[in_open]==cur_boundary) group_warning();
+ /*groups possibly not properly nested with files*/
+cur_group=save_level(save_ptr);cur_boundary=save_index(save_ptr);
+if (eTeX_ex) decr(save_ptr)
+
+@ A global definition, which sets the level to |level_one|,
+@^global definitions@>
+will not be undone by |unsave|. If at least one global definition of
+|eqtb[p]| has been carried out within the group that just ended, the
+last such definition will therefore survive.
+
+@<Store \(s)|save...@>=
+if (p < int_base)
+ if (eq_level(p)==level_one)
+ {@+eq_destroy(save_stack[save_ptr]); /*destroy the saved value*/
+#ifdef @!STAT
+ if (tracing_restores > 0) restore_trace(p,"retaining");
+#endif
+@;@/
+ }
+ else{@+eq_destroy(eqtb[p]); /*destroy the current value*/
+ eqtb[p]=save_stack[save_ptr]; /*restore the saved value*/
+#ifdef @!STAT
+ if (tracing_restores > 0) restore_trace(p,"restoring");
+#endif
+@;@/
+ }
+else if (xeq_level[p]!=level_one)
+ {@+eqtb[p]=save_stack[save_ptr];xeq_level[p]=l;
+#ifdef @!STAT
+ if (tracing_restores > 0) restore_trace(p,"restoring");
+#endif
+@;@/
+ }
+else{
+#ifdef @!STAT
+ if (tracing_restores > 0) restore_trace(p,"retaining");
+#endif
+@;@/
+ }
+
+@ @<Declare \eTeX\ procedures for tr...@>=
+#ifdef @!STAT
+static void restore_trace(pointer @!p, char *@!s)
+ /*|eqtb[p]| has just been restored or retained*/
+{@+begin_diagnostic();print_char('{');print(s);print_char(' ');
+show_eqtb(p);print_char('}');
+end_diagnostic(false);
+}
+#endif
+
+@ When looking for possible pointers to a memory location, it is helpful
+to look for references from |eqtb| that might be waiting on the
+save stack. Of course, we might find spurious pointers too; but this
+routine is merely an aid when debugging, and at such times we are
+grateful for any scraps of information, even if they prove to be irrelevant.
+@^dirty \PASCAL@>
+
+@<Search |save_stack| for equivalents that point to |p|@>=
+if (save_ptr > 0) for (q=0; q<=save_ptr-1; q++)
+ {@+if (equiv_field(save_stack[q])==p)
+ {@+print_nl("SAVE(");print_int(q);print_char(')');
+ }
+ }
+
+@ Most of the parameters kept in |eqtb| can be changed freely, but there's
+an exception: The magnification should not be used with two different
+values during any \TeX\ job, since a single magnification is applied to an
+entire run. The global variable |mag_set| is set to the current magnification
+whenever it becomes necessary to ``freeze'' it at a particular value.
+
+@<Glob...@>=
+static int @!mag_set; /*if nonzero, this magnification should be used henceforth*/
+
+@ @<Set init...@>=
+mag_set=0;
+
+@ The |prepare_mag| subroutine is called whenever \TeX\ wants to use |mag|
+for magnification.
+
+@p static void prepare_mag(void)
+{@+if ((mag_set > 0)&&(mag!=mag_set))
+ {@+print_err("Incompatible magnification (");print_int(mag);
+@.Incompatible magnification@>
+ print(");");print_nl(" the previous value will be retained");
+ help2("I can handle only one magnification ratio per job. So I've",@/
+ "reverted to the magnification you used earlier on this run.");@/
+ int_error(mag_set);
+ geq_word_define(int_base+mag_code, mag_set); /*|mag=mag_set|*/
+ }
+if ((mag <= 0)||(mag > 32768))
+ {@+print_err("Illegal magnification has been changed to 1000");@/
+@.Illegal magnification...@>
+ help1("The magnification ratio must be between 1 and 32768.");
+ int_error(mag);geq_word_define(int_base+mag_code, 1000);
+ }
+mag_set=mag;
+}
+
+@* Token lists.
+A \TeX\ token is either a character or a control sequence, and it is
+@^token@>
+represented internally in one of two ways: (1)~A character whose ASCII
+code number is |c| and whose command code is |m| is represented as the
+number $2^8m+c$; the command code is in the range |1 <= m <= 14|. (2)~A control
+sequence whose |eqtb| address is |p| is represented as the number
+|cs_token_flag+p|. Here |cs_token_flag==@t$2^{12}-1$@>| is larger than
+$2^8m+c$, yet it is small enough that |cs_token_flag+p < max_halfword|;
+thus, a token fits comfortably in a halfword.
+
+A token |t| represents a |left_brace| command if and only if
+|t < left_brace_limit|; it represents a |right_brace| command if and only if
+we have |left_brace_limit <= t < right_brace_limit|; and it represents a |match| or
+|end_match| command if and only if |match_token <= t <= end_match_token|.
+The following definitions take care of these token-oriented constants
+and a few others.
+
+@d cs_token_flag 07777 /*amount added to the |eqtb| location in a
+ token that stands for a control sequence; is a multiple of~256, less~1*/
+@d left_brace_token 00400 /*$2^8\cdot|left_brace|$*/
+@d left_brace_limit 01000 /*$2^8\cdot(|left_brace|+1)$*/
+@d right_brace_token 01000 /*$2^8\cdot|right_brace|$*/
+@d right_brace_limit 01400 /*$2^8\cdot(|right_brace|+1)$*/
+@d math_shift_token 01400 /*$2^8\cdot|math_shift|$*/
+@d tab_token 02000 /*$2^8\cdot|tab_mark|$*/
+@d out_param_token 02400 /*$2^8\cdot|out_param|$*/
+@d space_token 05040 /*$2^8\cdot|spacer|+|' '|$*/
+@d letter_token 05400 /*$2^8\cdot|letter|$*/
+@d other_token 06000 /*$2^8\cdot|other_char|$*/
+@d match_token 06400 /*$2^8\cdot|match|$*/
+@d end_match_token 07000 /*$2^8\cdot|end_match|$*/
+@d protected_token 07001 /*$2^8\cdot|end_match|+1$*/
+
+@ @<Check the ``constant''...@>=
+if (cs_token_flag+undefined_control_sequence > max_halfword) bad=21;
+
+@ A token list is a singly linked list of one-word nodes in |mem|, where
+each word contains a token and a link. Macro definitions, output-routine
+definitions, marks, \.{\\write} texts, and a few other things
+are remembered by \TeX\ in the form
+of token lists, usually preceded by a node with a reference count in its
+|token_ref_count| field. The token stored in location |p| is called
+|info(p)|.
+
+Three special commands appear in the token lists of macro definitions.
+When |m==match|, it means that \TeX\ should scan a parameter
+for the current macro; when |m==end_match|, it means that parameter
+matching should end and \TeX\ should start reading the macro text; and
+when |m==out_param|, it means that \TeX\ should insert parameter
+number |c| into the text at this point.
+
+The enclosing \.{\char'173} and \.{\char'175} characters of a macro
+definition are omitted, but an output routine
+will be enclosed in braces.
+
+Here is an example macro definition that illustrates these conventions.
+After \TeX\ processes the text
+$$\.{\\def\\mac a\#1\#2 \\b \{\#1\\-a \#\#1\#2 \#2\}}$$
+the definition of \.{\\mac} is represented as a token list containing
+$$\def\,{\hskip2pt}
+\vbox{\halign{\hfil#\hfil\cr
+(reference count), |letter|\,\.a, |match|\,\#, |match|\,\#, |spacer|\,\.\ ,
+\.{\\b}, |end_match|,\cr
+|out_param|\,1, \.{\\-}, |letter|\,\.a, |spacer|\,\.\ , |mac_param|\,\#,
+|other_char|\,\.1,\cr
+|out_param|\,2, |spacer|\,\.\ , |out_param|\,2.\cr}}$$
+The procedure |scan_toks| builds such token lists, and |macro_call|
+does the parameter matching.
+@^reference counts@>
+
+Examples such as
+$$\.{\\def\\m\{\\def\\m\{a\}\ b\}}$$
+explain why reference counts would be needed even if \TeX\ had no \.{\\let}
+operation: When the token list for \.{\\m} is being read, the redefinition of
+\.{\\m} changes the |eqtb| entry before the token list has been fully
+consumed, so we dare not simply destroy a token list when its
+control sequence is being redefined.
+
+If the parameter-matching part of a definition ends with `\.{\#\{}',
+the corresponding token list will have `\.\{' just before the `|end_match|'
+and also at the very end. The first `\.\{' is used to delimit the parameter; the
+second one keeps the first from disappearing.
+
+@ The procedure |show_token_list|, which prints a symbolic form of
+the token list that starts at a given node |p|, illustrates these
+conventions. The token list being displayed should not begin with a reference
+count. However, the procedure is intended to be robust, so that if the
+memory links are awry or if |p| is not really a pointer to a token list,
+nothing catastrophic will happen.
+
+An additional parameter |q| is also given; this parameter is either null
+or it points to a node in the token list where a certain magic computation
+takes place that will be explained later. (Basically, |q| is non-null when
+we are printing the two-line context information at the time of an error
+message; |q| marks the place corresponding to where the second line
+should begin.)
+
+For example, if |p| points to the node containing the first \.a in the
+token list above, then |show_token_list| will print the string
+$$\hbox{`\.{a\#1\#2\ \\b\ ->\#1\\-a\ \#\#1\#2\ \#2}';}$$
+and if |q| points to the node containing the second \.a,
+the magic computation will be performed just before the second \.a is printed.
+
+The generation will stop, and `\.{\\ETC.}' will be printed, if the length
+of printing exceeds a given limit~|l|. Anomalous entries are printed in the
+form of control sequences that are not followed by a blank space, e.g.,
+`\.{\\BAD.}'; this cannot be confused with actual control sequences because
+a real control sequence named \.{BAD} would come out `\.{\\BAD\ }'.
+
+@<Declare the procedure called |show_token_list|@>=
+static void show_token_list(int @!p, int @!q, int @!l)
+{@+
+int m, @!c; /*pieces of a token*/
+ASCII_code @!match_chr; /*character used in a `|match|'*/
+ASCII_code @!n; /*the highest parameter number, as an ASCII digit*/
+match_chr='#';n='0';tally=0;
+while ((p!=null)&&(tally < l))
+ {@+if (p==q) @<Do magic computation@>;
+ @<Display token |p|, and |return| if there are problems@>;
+ p=link(p);
+ }
+if (p!=null) print_esc("ETC.");
+@.ETC@>
+
+}
+
+@ @<Display token |p|...@>=
+if ((p < hi_mem_min)||(p > mem_end))
+ {@+print_esc("CLOBBERED.");return;
+@.CLOBBERED@>
+ }
+if (info(p) >= cs_token_flag) print_cs(info(p)-cs_token_flag);
+else{@+m=info(p)/0400;c=info(p)%0400;
+ if (info(p) < 0) print_esc("BAD.");
+@.BAD@>
+ else@<Display the token $(|m|,|c|)$@>;
+ }
+
+@ The procedure usually ``learns'' the character code used for macro
+parameters by seeing one in a |match| command before it runs into any
+|out_param| commands.
+
+@<Display the token...@>=
+switch (m) {
+case left_brace: case right_brace: case math_shift:
+ case tab_mark: case sup_mark: case sub_mark:
+ case spacer:
+ case letter: case other_char: printn(c);@+break;
+case mac_param: {@+printn(c);printn(c);
+ } @+break;
+case out_param: {@+printn(match_chr);
+ if (c <= 9) print_char(c+'0');
+ else{@+print_char('!');return;
+ }
+ } @+break;
+case match: {@+match_chr=c;printn(c);incr(n);print_char(n);
+ if (n > '9') return;
+ } @+break;
+case end_match: if (c==0) print("->");@+break;
+@.->@>
+default:print_esc("BAD.");
+@.BAD@>
+}
+
+@ Here's the way we sometimes want to display a token list, given a pointer
+to its reference count; the pointer may be null.
+
+@p static void token_show(pointer @!p)
+{@+if (p!=null) show_token_list(link(p), null, 10000000);
+}
+
+@ The |print_meaning| subroutine displays |cur_cmd| and |cur_chr| in
+symbolic form, including the expansion of a macro or mark.
+
+@p static void print_meaning(void)
+{@+print_cmd_chr(cur_cmd, cur_chr);
+if (cur_cmd >= call)
+ {@+print_char(':');print_ln();token_show(cur_chr);
+ }
+else if ((cur_cmd==top_bot_mark)&&(cur_chr < marks_code))
+ {@+print_char(':');print_ln();
+ token_show(cur_mark[cur_chr]);
+ }
+}
+
+@* Introduction to the syntactic routines.
+Let's pause a moment now and try to look at the Big Picture.
+The \TeX\ program consists of three main parts: syntactic routines,
+semantic routines, and output routines. The chief purpose of the
+syntactic routines is to deliver the user's input to the semantic routines,
+one token at a time. The semantic routines act as an interpreter
+responding to these tokens, which may be regarded as commands. And the
+output routines are periodically called on to convert box-and-glue
+lists into a compact set of instructions that will be sent
+to a typesetter. We have discussed the basic data structures and utility
+routines of \TeX, so we are good and ready to plunge into the real activity by
+considering the syntactic routines.
+
+Our current goal is to come to grips with the |get_next| procedure,
+which is the keystone of \TeX's input mechanism. Each call of |get_next|
+sets the value of three variables |cur_cmd|, |cur_chr|, and |cur_cs|,
+representing the next input token.
+$$\vbox{\halign{#\hfil\cr
+ \hbox{|cur_cmd| denotes a command code from the long list of codes
+ given above;}\cr
+ \hbox{|cur_chr| denotes a character code or other modifier of the command
+ code;}\cr
+ \hbox{|cur_cs| is the |eqtb| location of the current control sequence,}\cr
+ \hbox{\qquad if the current token was a control sequence,
+ otherwise it's zero.}\cr}}$$
+Underlying this external behavior of |get_next| is all the machinery
+necessary to convert from character files to tokens. At a given time we
+may be only partially finished with the reading of several files (for
+which \.{\\input} was specified), and partially finished with the expansion
+of some user-defined macros and/or some macro parameters, and partially
+finished with the generation of some text in a template for \.{\\halign},
+and so on. When reading a character file, special characters must be
+classified as math delimiters, etc.; comments and extra blank spaces must
+be removed, paragraphs must be recognized, and control sequences must be
+found in the hash table. Furthermore there are occasions in which the
+scanning routines have looked ahead for a word like `\.{plus}' but only
+part of that word was found, hence a few characters must be put back
+into the input and scanned again.
+
+To handle these situations, which might all be present simultaneously,
+\TeX\ uses various stacks that hold information about the incomplete
+activities, and there is a finite state control for each level of the
+input mechanism. These stacks record the current state of an implicitly
+recursive process, but the |get_next| procedure is not recursive.
+Therefore it will not be difficult to translate these algorithms into
+low-level languages that do not support recursion.
+
+@<Glob...@>=
+static eight_bits @!cur_cmd; /*current command set by |get_next|*/
+static halfword @!cur_chr; /*operand of current command*/
+static pointer @!cur_cs; /*control sequence found here, zero if none found*/
+static halfword @!cur_tok; /*packed representative of |cur_cmd| and |cur_chr|*/
+
+@ The |print_cmd_chr| routine prints a symbolic interpretation of a
+command code and its modifier. This is used in certain `\.{You can\'t}'
+error messages, and in the implementation of diagnostic routines like
+\.{\\show}.
+
+The body of |print_cmd_chr| is a rather tedious listing of print
+commands, and most of it is essentially an inverse to the |primitive|
+routine that enters a \TeX\ primitive into |eqtb|. Therefore much of
+this procedure appears elsewhere in the program,
+together with the corresponding |primitive| calls.
+
+@d chr_cmd(A) {@+print(A);print_ASCII(chr_code);
+ }
+
+@<Declare the procedure called |print_cmd_chr|@>=
+static void print_cmd_chr(quarterword @!cmd, halfword @!chr_code)
+{@+int n; /*temp variable*/
+switch (cmd) {
+case left_brace: chr_cmd("begin-group character ")@;@+break;
+case right_brace: chr_cmd("end-group character ")@;@+break;
+case math_shift: chr_cmd("math shift character ")@;@+break;
+case mac_param: chr_cmd("macro parameter character ")@;@+break;
+case sup_mark: chr_cmd("superscript character ")@;@+break;
+case sub_mark: chr_cmd("subscript character ")@;@+break;
+case endv: print("end of alignment template");@+break;
+case spacer: chr_cmd("blank space ")@;@+break;
+case letter: chr_cmd("the letter ")@;@+break;
+case other_char: chr_cmd("the character ")@;@+break;
+@t\4@>@<Cases of |print_cmd_chr| for symbolic printing of primitives@>@/
+default:print("[unknown command code!]");
+}
+}
+
+@ Here is a procedure that displays the current command.
+
+@p static void show_cur_cmd_chr(void)
+{@+int n; /*level of \.{\\if...\\fi} nesting*/
+int @!l; /*line where \.{\\if} started*/
+pointer @!p;
+begin_diagnostic();print_nl("{");
+if (mode!=shown_mode)
+ {@+print_mode(mode);print(": ");shown_mode=mode;
+ }
+print_cmd_chr(cur_cmd, cur_chr);
+if (tracing_ifs > 0)
+ if (cur_cmd >= if_test) if (cur_cmd <= fi_or_else)
+ {@+print(": ");
+ if (cur_cmd==fi_or_else)
+ {@+print_cmd_chr(if_test, cur_if);print_char(' ');
+ n=0;l=if_line;
+ }
+ else{@+n=1;l=line;
+ }
+ p=cond_ptr;
+ while (p!=null)
+ {@+incr(n);p=link(p);
+ }
+ print("(level ");print_int(n);print_char(')');print_if_line(l);
+ }
+print_char('}');
+end_diagnostic(false);
+}
+
+@* Input stacks and states.
+This implementation of
+\TeX\ uses two different conventions for representing sequential stacks.
+@^stack conventions@>@^conventions for representing stacks@>
+
+\yskip\hangg 1) If there is frequent access to the top entry, and if the
+stack is essentially never empty, then the top entry is kept in a global
+variable (even better would be a machine register), and the other entries
+appear in the array $\\{stack}[0\to(\\{ptr}-1)]$. For example, the
+semantic stack described above is handled this way, and so is the input
+stack that we are about to study.
+
+\yskip\hangg 2) If there is infrequent top access, the entire stack contents
+are in the array $\\{stack}[0\to(\\{ptr}-1)]$. For example, the |save_stack|
+is treated this way, as we have seen.
+
+\yskip\noindent
+The state of \TeX's input mechanism appears in the input stack, whose
+entries are records with six fields, called |state|, |index|, |start|, |loc|,
+|limit|, and |name|. This stack is maintained with
+convention~(1), so it is declared in the following way:
+
+@<Types...@>=
+typedef struct {
+ quarterword @!state_field, @!index_field;
+ halfword @!start_field, @!loc_field, @!limit_field, @!name_field;
+ halfword @!depth_field;
+ } in_state_record;
+
+@ @<Glob...@>=
+static in_state_record @!input_stack[stack_size+1];
+static int @!input_ptr; /*first unused location of |input_stack|*/
+static int @!max_in_stack; /*largest value of |input_ptr| when pushing*/
+static in_state_record @!cur_input;
+ /*the ``top'' input state, according to convention (1)*/
+
+@ We've already defined the special variable |loc==cur_input.loc_field|
+in our discussion of basic input-output routines. The other components of
+|cur_input| are defined in the same way:
+
+@d state cur_input.state_field /*current scanner state*/
+@d index cur_input.index_field /*reference for buffer information*/
+@d start cur_input.start_field /*starting position in |buffer|*/
+@d limit cur_input.limit_field /*end of current line in |buffer|*/
+@d name cur_input.name_field /*name of the current file*/
+@d cur_depth cur_input.depth_field /* nesting depth of current macro */
+
+@ Let's look more closely now at the control variables
+(|state|,~|index|,~|start|,~|loc|,~|limit|,~|name|),
+assuming that \TeX\ is reading a line of characters that have been input
+from some file or from the user's terminal. There is an array called
+|buffer| that acts as a stack of all lines of characters that are
+currently being read from files, including all lines on subsidiary
+levels of the input stack that are not yet completed. \TeX\ will return to
+the other lines when it is finished with the present input file.
+
+(Incidentally, on a machine with byte-oriented addressing, it might be
+appropriate to combine |buffer| with the |str_pool| array,
+letting the buffer entries grow downward from the top of the string pool
+and checking that these two tables don't bump into each other.)
+
+The line we are currently working on begins in position |start| of the
+buffer; the next character we are about to read is |buffer[loc]|; and
+|limit| is the location of the last character present. If |loc > limit|,
+the line has been completely read. Usually |buffer[limit]| is the
+|end_line_char|, denoting the end of a line, but this is not
+true if the current line is an insertion that was entered on the user's
+terminal in response to an error message.
+
+The |name| variable is a string number that designates the name of
+the current file, if we are reading a text file. It is zero if we
+are reading from the terminal; it is |n+1| if we are reading from
+input stream |n|, where |0 <= n <= 16|. (Input stream 16 stands for
+an invalid stream number; in such cases the input is actually from
+the terminal, under control of the procedure |read_toks|.)
+Finally |18 <= name <= 19| indicates that we are reading a pseudo file
+created by the \.{\\scantokens} command.
+
+The |state| variable has one of three values, when we are scanning such
+files:
+$$\baselineskip 15pt\vbox{\halign{#\hfil\cr
+1) |state==mid_line| is the normal state.\cr
+2) |state==skip_blanks| is like |mid_line|, but blanks are ignored.\cr
+3) |state==new_line| is the state at the beginning of a line.\cr}}$$
+These state values are assigned numeric codes so that if we add the state
+code to the next character's command code, we get distinct values. For
+example, `|mid_line+spacer|' stands for the case that a blank
+space character occurs in the middle of a line when it is not being
+ignored; after this case is processed, the next value of |state| will
+be |skip_blanks|.
+
+@d mid_line 1 /*|state| code when scanning a line of characters*/
+@d skip_blanks (2+max_char_code) /*|state| code when ignoring blanks*/
+@d new_line (3+max_char_code+max_char_code) /*|state| code at start of line*/
+
+@ Additional information about the current line is available via the
+|index| variable, which counts how many lines of characters are present
+in the buffer below the current level. We have |index==0| when reading
+from the terminal and prompting the user for each line; then if the user types,
+e.g., `\.{\\input paper}', we will have |index==1| while reading
+the file \.{paper.tex}. However, it does not follow that |index| is the
+same as the input stack pointer, since many of the levels on the input
+stack may come from token lists. For example, the instruction `\.{\\input
+paper}' might occur in a token list.
+
+The global variable |in_open| is equal to the |index|
+value of the highest non-token-list level. Thus, the number of partially read
+lines in the buffer is |in_open+1|, and we have |in_open==index|
+when we are not reading a token list.
+
+If we are not currently reading from the terminal, or from an input
+stream, we are reading from the file variable |input_file[index]|. We use
+the notation |terminal_input| as a convenient abbreviation for |name==0|,
+and |cur_file| as an abbreviation for |input_file[index]|.
+
+The global variable |line| contains the line number in the topmost
+open file, for use in error messages. If we are not reading from
+the terminal, |line_stack[index]| holds the line number for the
+enclosing level, so that |line| can be restored when the current
+file has been read. Line numbers should never be negative, since the
+negative of the current line number is used to identify the user's output
+routine in the |mode_line| field of the semantic nest entries.
+
+If more information about the input state is needed, it can be
+included in small arrays like those shown here. For example,
+the current page or segment number in the input file might be
+put into a variable |@!page|, maintained for enclosing levels in
+`\ignorespaces|@!page_stack: array[1 dotdot max_in_open]int|\unskip'
+by analogy with |line_stack|.
+@^system dependencies@>
+
+@d terminal_input (name==0) /*are we reading from the terminal?*/
+@d cur_file input_file[index] /*the current |alpha_file| variable*/
+
+@<Glob...@>=
+static int @!in_open; /*the number of lines in the buffer, less one*/
+static int @!open_parens; /*the number of open text files*/
+static alpha_file @!input_file0[max_in_open],
+ *const @!input_file = @!input_file0-1;
+static int @!line; /*current line number in the current source file*/
+static int @!line_stack0[max_in_open],
+ *const @!line_stack = @!line_stack0-1;
+
+@ Users of \TeX\ sometimes forget to balance left and right braces properly,
+and one of the ways \TeX\ tries to spot such errors is by considering an
+input file as broken into subfiles by control sequences that
+are declared to be \.{\\outer}.
+
+A variable called |scanner_status| tells \TeX\ whether or not to complain
+when a subfile ends. This variable has six possible values:
+
+\yskip\hang|normal|, means that a subfile can safely end here without incident.
+
+\yskip\hang|skipping|, means that a subfile can safely end here, but not a file,
+because we're reading past some conditional text that was not selected.
+
+\yskip\hang|defining|, means that a subfile shouldn't end now because a
+macro is being defined.
+
+\yskip\hang|matching|, means that a subfile shouldn't end now because a
+macro is being used and we are searching for the end of its arguments.
+
+\yskip\hang|aligning|, means that a subfile shouldn't end now because we are
+not finished with the preamble of an \.{\\halign} or \.{\\valign}.
+
+\yskip\hang|absorbing|, means that a subfile shouldn't end now because we are
+reading a balanced token list for \.{\\message}, \.{\\write}, etc.
+
+\yskip\noindent
+If the |scanner_status| is not |normal|, the variable |warning_index| points
+to the |eqtb| location for the relevant control sequence name to print
+in an error message.
+
+@d skipping 1 /*|scanner_status| when passing conditional text*/
+@d defining 2 /*|scanner_status| when reading a macro definition*/
+@d matching 3 /*|scanner_status| when reading macro arguments*/
+@d aligning 4 /*|scanner_status| when reading an alignment preamble*/
+@d absorbing 5 /*|scanner_status| when reading a balanced text*/
+
+@<Glob...@>=
+static int @!scanner_status; /*can a subfile end now?*/
+static pointer @!warning_index; /*identifier relevant to non-|normal| scanner status*/
+static pointer @!def_ref; /*reference count of token list being defined*/
+
+@ Here is a procedure that uses |scanner_status| to print a warning message
+when a subfile has ended, and at certain other crucial times:
+
+@<Declare the procedure called |runaway|@>=
+static void runaway(void)
+{@+pointer p; /*head of runaway list*/
+if (scanner_status > skipping)
+ {@+print_nl("Runaway ");
+@.Runaway...@>
+ switch (scanner_status) {
+ case defining: {@+print("definition");p=def_ref;
+ } @+break;
+ case matching: {@+print("argument");p=temp_head;
+ } @+break;
+ case aligning: {@+print("preamble");p=hold_head;
+ } @+break;
+ case absorbing: {@+print("text");p=def_ref;
+ }
+ } /*there are no other cases*/
+ print_char('?');print_ln();show_token_list(link(p), null, error_line-10);
+ }
+}
+
+@ However, all this discussion about input state really applies only to the
+case that we are inputting from a file. There is another important case,
+namely when we are currently getting input from a token list. In this case
+|state==token_list|, and the conventions about the other state variables
+are different:
+
+\yskip\hang|loc| is a pointer to the current node in the token list, i.e.,
+the node that will be read next. If |loc==null|, the token list has been
+fully read.
+
+\yskip\hang|start| points to the first node of the token list; this node
+may or may not contain a reference count, depending on the type of token
+list involved.
+
+\yskip\hang|token_type|, which takes the place of |index| in the
+discussion above, is a code number that explains what kind of token list
+is being scanned.
+
+\yskip\hang|name| points to the |eqtb| address of the control sequence
+being expanded, if the current token list is a macro.
+
+\yskip\hang|param_start|, which takes the place of |limit|, tells where
+the parameters of the current macro begin in the |param_stack|, if the
+current token list is a macro.
+
+\yskip\noindent The |token_type| can take several values, depending on
+where the current token list came from:
+
+\yskip\hang|parameter|, if a parameter is being scanned;
+
+\hang|u_template|, if the \<u_j> part of an alignment
+template is being scanned;
+
+\hang|v_template|, if the \<v_j> part of an alignment
+template is being scanned;
+
+\hang|backed_up|, if the token list being scanned has been inserted as
+`to be read again';
+
+\hang|inserted|, if the token list being scanned has been inserted as
+the text expansion of a \.{\\count} or similar variable;
+
+\hang|macro|, if a user-defined control sequence is being scanned;
+
+\hang|output_text|, if an \.{\\output} routine is being scanned;
+
+\hang|every_par_text|, if the text of \.{\\everypar} is being scanned;
+
+\hang|every_math_text|, if the text of \.{\\everymath} is being scanned;
+
+\hang|every_display_text|, if the text of \.{\\everydisplay} is being scanned;
+
+\hang|every_hbox_text|, if the text of \.{\\everyhbox} is being scanned;
+
+\hang|every_vbox_text|, if the text of \.{\\everyvbox} is being scanned;
+
+\hang|every_job_text|, if the text of \.{\\everyjob} is being scanned;
+
+\hang|every_cr_text|, if the text of \.{\\everycr} is being scanned;
+
+\hang|mark_text|, if the text of a \.{\\mark} is being scanned;
+
+\hang|write_text|, if the text of a \.{\\write} is being scanned.
+
+\yskip\noindent
+The codes for |output_text|, |every_par_text|, etc., are equal to a constant
+plus the corresponding codes for token list parameters |output_routine_loc|,
+|every_par_loc|, etc. The token list begins with a reference count if and
+only if |token_type >= macro|.
+@^reference counts@>
+
+Since \eTeX's additional token list parameters precede |toks_base|, the
+corresponding token types must precede |write_text|.
+
+@d token_list 0 /*|state| code when scanning a token list*/
+@d token_type index /*type of current token list*/
+@d param_start limit /*base of macro parameters in |param_stack|*/
+@d parameter 0 /*|token_type| code for parameter*/
+@d u_template 1 /*|token_type| code for \<u_j> template*/
+@d v_template 2 /*|token_type| code for \<v_j> template*/
+@d backed_up 3 /*|token_type| code for text to be reread*/
+@d inserted 4 /*|token_type| code for inserted texts*/
+@d macro 5 /*|token_type| code for defined control sequences*/
+@d output_text 6 /*|token_type| code for output routines*/
+@d every_par_text 7 /*|token_type| code for \.{\\everypar}*/
+@d every_math_text 8 /*|token_type| code for \.{\\everymath}*/
+@d every_display_text 9 /*|token_type| code for \.{\\everydisplay}*/
+@d every_hbox_text 10 /*|token_type| code for \.{\\everyhbox}*/
+@d every_vbox_text 11 /*|token_type| code for \.{\\everyvbox}*/
+@d every_job_text 12 /*|token_type| code for \.{\\everyjob}*/
+@d every_cr_text 13 /*|token_type| code for \.{\\everycr}*/
+@d mark_text 14 /*|token_type| code for \.{\\topmark}, etc.*/
+@#
+@d eTeX_text_offset (output_routine_loc-output_text)
+@d every_eof_text (every_eof_loc-eTeX_text_offset)
+ /*|token_type| code for \.{\\everyeof}*/
+@#
+@d write_text (toks_base-eTeX_text_offset) /*|token_type| code for \.{\\write}*/
+
+@ The |param_stack| is an auxiliary array used to hold pointers to the token
+lists for parameters at the current level and subsidiary levels of input.
+This stack is maintained with convention (2), and it grows at a different
+rate from the others.
+
+@<Glob...@>=
+static pointer @!param_stack[param_size+1];
+ /*token list pointers for parameters*/
+static int @!param_ptr; /*first unused entry in |param_stack|*/
+static int @!max_param_stack;
+ /*largest value of |param_ptr|, will be | <= param_size+9|*/
+
+@ The input routines must also interact with the processing of
+\.{\\halign} and \.{\\valign}, since the appearance of tab marks and
+\.{\\cr} in certain places is supposed to trigger the beginning of special
+\<v_j> template text in the scanner. This magic is accomplished by an
+|align_state| variable that is increased by~1 when a `\.{\char'173}' is
+scanned and decreased by~1 when a `\.{\char'175}' is scanned. The |align_state|
+is nonzero during the \<u_j> template, after which it is set to zero; the
+\<v_j> template begins when a tab mark or \.{\\cr} occurs at a time that
+|align_state==0|.
+
+The same principle applies when entering the definition of a
+control sequence between \.{\\csname} and \.{\\endcsname}.
+
+@<Glob...@>=
+static int @!align_state; /*group level with respect to current alignment*/
+static int @!incsname_state; /*group level with respect to in csname state*/
+
+@ Thus, the ``current input state'' can be very complicated indeed; there
+can be many levels and each level can arise in a variety of ways. The
+|show_context| procedure, which is used by \TeX's error-reporting routine to
+print out the current input state on all levels down to the most recent
+line of characters from an input file, illustrates most of these conventions.
+The global variable |base_ptr| contains the lowest level that was
+displayed by this procedure.
+
+@<Glob...@>=
+static int @!base_ptr; /*shallowest level shown by |show_context|*/
+
+@ The status at each level is indicated by printing two lines, where the first
+line indicates what was read so far and the second line shows what remains
+to be read. The context is cropped, if necessary, so that the first line
+contains at most |half_error_line| characters, and the second contains
+at most |error_line|. Non-current input levels whose |token_type| is
+`|backed_up|' are shown only if they have not been fully read.
+
+@p static void show_context(void) /*prints where the scanner is*/
+{@+
+int old_setting; /*saved |selector| setting*/
+int @!nn; /*number of contexts shown so far, less one*/
+bool @!bottom_line; /*have we reached the final context to be shown?*/
+@<Local variables for formatting calculations@>@/
+base_ptr=input_ptr;input_stack[base_ptr]=cur_input;
+ /*store current state*/
+nn=-1;bottom_line=false;
+loop@+{@+cur_input=input_stack[base_ptr]; /*enter into the context*/
+ if ((state!=token_list))
+ if ((name > 19)||(base_ptr==0)) bottom_line=true;
+ if ((base_ptr==input_ptr)||bottom_line||(nn < error_context_lines))
+ @<Display the current context@>@;
+ else if (nn==error_context_lines)
+ {@+print_nl("...");incr(nn); /*omitted if |error_context_lines < 0|*/
+ }
+ if (bottom_line) goto done;
+ decr(base_ptr);
+ }
+done: cur_input=input_stack[input_ptr]; /*restore original state*/
+}
+
+@ @<Display the current context@>=
+{@+if ((base_ptr==input_ptr)||(state!=token_list)||
+ (token_type!=backed_up)||(loc!=null))
+ /*we omit backed-up token lists that have already been read*/
+ {@+tally=0; /*get ready to count characters*/
+ old_setting=selector;
+ if (state!=token_list)
+ {@+@<Print location of current line@>;
+ @<Pseudoprint the line@>;
+ }
+ else{@+@<Print type of token list@>;
+ @<Pseudoprint the token list@>;
+ }
+ selector=old_setting; /*stop pseudoprinting*/
+ @<Print two lines using the tricky pseudoprinted information@>;
+ incr(nn);
+ }
+}
+
+@ This routine should be changed, if necessary, to give the best possible
+indication of where the current line resides in the input file.
+For example, on some systems it is best to print both a page and line number.
+@^system dependencies@>
+
+@<Print location of current line@>=
+if (name <= 17)
+ if (terminal_input)
+ if (base_ptr==0) print_nl("<*>");else print_nl("<insert> ");
+ else{@+print_nl("<read ");
+ if (name==17) print_char('*');@+else print_int(name-1);
+@.*\relax@>
+ print_char('>');
+ }
+else{@+print_nl("l.");
+ if (index==in_open) print_int(line);
+ else print_int(line_stack[index+1]); /*input from a pseudo file*/
+ }
+print_char(' ')
+
+@ @<Print type of token list@>=
+switch (token_type) {
+case parameter: print_nl("<argument> ");@+break;
+case u_template: case v_template: print_nl("<template> ");@+break;
+case backed_up: if (loc==null) print_nl("<recently read> ");
+ else print_nl("<to be read again> ");@+break;
+case inserted: print_nl("<inserted text> ");@+break;
+case macro: {@+print_ln();print_cs(name);
+ } @+break;
+case output_text: print_nl("<output> ");@+break;
+case every_par_text: print_nl("<everypar> ");@+break;
+case every_math_text: print_nl("<everymath> ");@+break;
+case every_display_text: print_nl("<everydisplay> ");@+break;
+case every_hbox_text: print_nl("<everyhbox> ");@+break;
+case every_vbox_text: print_nl("<everyvbox> ");@+break;
+case every_job_text: print_nl("<everyjob> ");@+break;
+case every_cr_text: print_nl("<everycr> ");@+break;
+case mark_text: print_nl("<mark> ");@+break;
+case every_eof_text: print_nl("<everyeof> ");@+break;
+case write_text: print_nl("<write> ");@+break;
+default:print_nl("?"); /*this should never happen*/
+}
+
+@ Here it is necessary to explain a little trick. We don't want to store a long
+string that corresponds to a token list, because that string might take up
+lots of memory; and we are printing during a time when an error message is
+being given, so we dare not do anything that might overflow one of \TeX's
+tables. So `pseudoprinting' is the answer: We enter a mode of printing
+that stores characters into a buffer of length |error_line|, where character
+$k+1$ is placed into \hbox{|trick_buf[k%error_line]|} if
+|k < trick_count|, otherwise character |k| is dropped. Initially we set
+|tally=0| and |trick_count=1000000|; then when we reach the
+point where transition from line 1 to line 2 should occur, we
+set |first_count=tally| and |trick_count=@tmax@>(error_line,
+tally+1+error_line-half_error_line)|. At the end of the
+pseudoprinting, the values of |first_count|, |tally|, and
+|trick_count| give us all the information we need to print the two lines,
+and all of the necessary text is in |trick_buf|.
+
+Namely, let |l| be the length of the descriptive information that appears
+on the first line. The length of the context information gathered for that
+line is |k==first_count|, and the length of the context information
+gathered for line~2 is $m=\min(|tally|, |trick_count|)-k$. If |l+k <= h|,
+where |h==half_error_line|, we print |trick_buf[0 dotdot k-1]| after the
+descriptive information on line~1, and set |n=l+k|; here |n| is the
+length of line~1. If $l+k>h$, some cropping is necessary, so we set |n=h|
+and print `\.{...}' followed by
+$$\hbox{|trick_buf[(l+k-h+3)dotdot k-1]|,}$$
+where subscripts of |trick_buf| are circular modulo |error_line|. The
+second line consists of |n|~spaces followed by |trick_buf[k dotdot(k+m-1)]|,
+unless |n+m > error_line|; in the latter case, further cropping is done.
+This is easier to program than to explain.
+
+@<Local variables for formatting...@>=
+int @!i; /*index into |buffer|*/
+int @!j; /*end of current line in |buffer|*/
+int @!l; /*length of descriptive information on line 1*/
+int @!m; /*context information gathered for line 2*/
+int @!n; /*length of line 1*/
+int @!p; /*starting or ending place in |trick_buf|*/
+int @!q; /*temporary index*/
+
+@ The following code sets up the print routines so that they will gather
+the desired information.
+
+@d begin_pseudoprint
+ {@+l=tally;tally=0;selector=pseudo;
+ trick_count=1000000;
+ }
+@d set_trick_count
+ {@+first_count=tally;
+ trick_count=tally+1+error_line-half_error_line;
+ if (trick_count < error_line) trick_count=error_line;
+ }
+
+@ And the following code uses the information after it has been gathered.
+
+@<Print two lines using the tricky pseudoprinted information@>=
+if (trick_count==1000000) set_trick_count;
+ /*|set_trick_count| must be performed*/
+if (tally < trick_count) m=tally-first_count;
+else m=trick_count-first_count; /*context on line 2*/
+if (l+first_count <= half_error_line)
+ {@+p=0;n=l+first_count;
+ }
+else{@+print("...");p=l+first_count-half_error_line+3;
+ n=half_error_line;
+ }
+for (q=p; q<=first_count-1; q++) print_char(trick_buf[q%error_line]);
+print_ln();
+for (q=1; q<=n; q++) print_char(' '); /*print |n| spaces to begin line~2*/
+if (m+n <= error_line) p=first_count+m;else p=first_count+(error_line-n-3);
+for (q=first_count; q<=p-1; q++) print_char(trick_buf[q%error_line]);
+if (m+n > error_line) print("...")
+
+@ But the trick is distracting us from our current goal, which is to
+understand the input state. So let's concentrate on the data structures that
+are being pseudoprinted as we finish up the |show_context| procedure.
+
+@<Pseudoprint the line@>=
+begin_pseudoprint;
+if (buffer[limit]==end_line_char) j=limit;
+else j=limit+1; /*determine the effective end of the line*/
+if (j > 0) for (i=start; i<=j-1; i++)
+ {@+if (i==loc) set_trick_count;
+ printn(buffer[i]);
+ }
+
+@ @<Pseudoprint the token list@>=
+begin_pseudoprint;
+if (token_type < macro) show_token_list(start, loc, 100000);
+else show_token_list(link(start), loc, 100000) /*avoid reference count*/
+
+@ Here is the missing piece of |show_token_list| that is activated when the
+token beginning line~2 is about to be shown:
+
+@<Do magic computation@>=set_trick_count
+
+@* Maintaining the input stacks.
+The following subroutines change the input status in commonly needed ways.
+
+First comes |push_input|, which stores the current state and creates a
+new level (having, initially, the same properties as the old).
+
+@d push_input @t@> /*enter a new input level, save the old*/
+ {@+if (input_ptr > max_in_stack)
+ {@+max_in_stack=input_ptr;
+ if (input_ptr==stack_size) overflow("input stack size", stack_size);
+@:TeX capacity exceeded input stack size}{\quad input stack size@>
+ }
+ input_stack[input_ptr]=cur_input; /*stack the record*/
+ incr(input_ptr);
+ }
+
+@ And of course what goes up must come down.
+
+@d pop_input @t@> /*leave an input level, re-enter the old*/
+ {@+decr(input_ptr);cur_input=input_stack[input_ptr];
+ }
+
+@ Here is a procedure that starts a new level of token-list input, given
+a token list |p| and its type |t|. If |t==macro|, the calling routine should
+set |name| and |loc|.
+
+@d back_list(A) begin_token_list(A, backed_up) /*backs up a simple token list*/
+@d ins_list(A) begin_token_list(A, inserted) /*inserts a simple token list*/
+
+@p static void begin_token_list(pointer @!p, quarterword @!t)
+{@+push_input;state=token_list;start=p;token_type=t;
+if (t >= macro) /*the token list starts with a reference count*/
+ {@+add_token_ref(p);
+ if (t==macro) param_start=param_ptr;
+ else{@+
+ @<additional local variables for |begin_token_list|@>
+ loc=link(p);
+ if (tracing_macros > 1)
+ {@+begin_diagnostic();print_nl("");
+ switch (t) {
+ case mark_text: print_esc("mark");@+break;
+ case write_text: print_esc("write");@+break;
+ default:print_cmd_chr(assign_toks, t-output_text+output_routine_loc);
+ } @/
+ print("->");token_show(p);end_diagnostic(false);
+ }
+ @<update the macro stack@>@;
+ }
+ }
+else loc=p;
+}
+
+@ When a token list has been fully scanned, the following computations
+should be done as we leave that level of input. The |token_type| tends
+to be equal to either |backed_up| or |inserted| about 2/3 of the time.
+@^inner loop@>
+
+@p static void end_token_list(void) /*leave a token-list input level*/
+{@+if (token_type >= backed_up) /*token list to be deleted*/
+ {@+if (token_type <= inserted) flush_list(start);
+ else{@+delete_token_ref(start); /*update reference count*/
+ if (token_type==macro) /*parameters must be flushed*/
+ while (param_ptr > param_start)
+ {@+decr(param_ptr);
+ flush_list(param_stack[param_ptr]);
+ }
+ }
+ }
+else if (token_type==u_template)
+ if (align_state > 500000) align_state=0;
+ else fatal_error("(interwoven alignment preambles are not allowed)");
+@.interwoven alignment preambles...@>
+pop_input;
+check_interrupt;
+}
+
+@ Sometimes \TeX\ has read too far and wants to ``unscan'' what it has
+seen. The |back_input| procedure takes care of this by putting the token
+just scanned back into the input stream, ready to be read again. This
+procedure can be used only if |cur_tok| represents the token to be
+replaced. Some applications of \TeX\ use this procedure a lot,
+so it has been slightly optimized for speed.
+@^inner loop@>
+
+We charge the backup token to the current file and line.
+@^profiling@>
+
+@p static void back_input(void) /*undoes one token of input*/
+{@+pointer p; /*a token list of length one*/
+while ((state==token_list)&&(loc==null)&&(token_type!=v_template))
+ end_token_list(); /*conserve stack space*/
+p=get_avail();info(p)=cur_tok; fl_mem[p]=prof_file_line;
+if (cur_tok < right_brace_limit)
+ if (cur_tok < left_brace_limit) decr(align_state);
+ else incr(align_state);
+push_input;state=token_list;start=p;token_type=backed_up;
+loc=p; /*that was |back_list(p)|, without procedure overhead*/
+}
+
+@ @<Insert token |p| into \TeX's input@>=
+{@+t=cur_tok;cur_tok=p;
+if (a)
+ {@+p=get_avail();info(p)=cur_tok;link(p)=loc;loc=p;start=p;
+ if (cur_tok < right_brace_limit)
+ if (cur_tok < left_brace_limit) decr(align_state);
+ else incr(align_state);
+ }
+else{@+back_input();a=eTeX_ex;
+ }
+cur_tok=t;
+}
+
+@ The |back_error| routine is used when we want to replace an offending token
+just before issuing an error message. This routine, like |back_input|,
+requires that |cur_tok| has been set. We disable interrupts during the
+call of |back_input| so that the help message won't be lost.
+
+@p static void back_error(void) /*back up one token and call |error|*/
+{@+OK_to_interrupt=false;back_input();OK_to_interrupt=true;error();
+}
+@#
+static void ins_error(void) /*back up one inserted token and call |error|*/
+{@+OK_to_interrupt=false;back_input();token_type=inserted;
+OK_to_interrupt=true;error();
+}
+
+@ The |begin_file_reading| procedure starts a new level of input for lines
+of characters to be read from a file, or as an insertion from the
+terminal. It does not take care of opening the file, nor does it set |loc|
+or |limit| or |line|.
+@^system dependencies@>
+
+@p static void begin_file_reading(void)
+{@+if (in_open==max_in_open) overflow("text input levels", max_in_open);
+@:TeX capacity exceeded text input levels}{\quad text input levels@>
+if (first==buf_size) overflow("buffer size", buf_size);
+@:TeX capacity exceeded buffer size}{\quad buffer size@>
+incr(in_open);push_input;index=in_open;@/
+source_filename_stack[index]=NULL; /* \TeX\ Live */
+full_source_filename_stack[index]=NULL; /* \TeX\ Live */
+eof_seen[index]=false;
+grp_stack[index]=cur_boundary;if_stack[index]=cond_ptr;
+line_stack[index]=line;start=first;state=mid_line;
+name=0; /*|terminal_input| is now |true|*/
+cur_file_num=terminal_file;
+}
+
+@ Conversely, the variables must be downdated when such a level of input
+is finished:
+
+@p static void end_file_reading(void)
+{@+first=start;line=line_stack[index];
+if ((name==18)||(name==19)) pseudo_close();else
+if (name > 17) a_close(&cur_file); /*forget it*/
+if (full_source_filename_stack[in_open]!=NULL)
+{ free(full_source_filename_stack[in_open]);
+ full_source_filename_stack[in_open]=NULL;
+}
+pop_input;decr(in_open);
+}
+
+@ In order to keep the stack from overflowing during a long sequence of
+inserted `\.{\\show}' commands, the following routine removes completed
+error-inserted lines from memory.
+
+@p static void clear_for_error_prompt(void)
+{@+while ((state!=token_list)&&terminal_input&&@|
+ (input_ptr > 0)&&(loc > limit)) end_file_reading();
+print_ln();clear_terminal;
+}
+
+@ To get \TeX's whole input mechanism going, we perform the following
+actions.
+
+@<Initialize the input routines@>=
+{@+input_ptr=0;max_in_stack=0;
+in_open=0;open_parens=0;max_buf_stack=0;
+grp_stack[0]=0;if_stack[0]=null;
+param_ptr=0;max_param_stack=0;
+first=buf_size;@/do@+{buffer[first]=0;decr(first);}@+ while (!(first==0));
+scanner_status=normal;warning_index=null;first=1;
+state=new_line;start=1;index=0;line=0;name=0;cur_depth=0;
+force_eof=false;
+align_state=1000000;@/
+if (!init_terminal()) exit(0);
+limit=last;first=last+1; /*|init_terminal| has set |loc| and |last|*/
+}
+
+@* Getting the next token.
+The heart of \TeX's input mechanism is the |get_next| procedure, which
+we shall develop in the next few sections of the program. Perhaps we
+shouldn't actually call it the ``heart,'' however, because it really acts
+as \TeX's eyes and mouth, reading the source files and gobbling them up.
+And it also helps \TeX\ to regurgitate stored token lists that are to be
+processed again.
+@^eyes and mouth@>
+
+The main duty of |get_next| is to input one token and to set |cur_cmd|
+and |cur_chr| to that token's command code and modifier. Furthermore, if
+the input token is a control sequence, the |eqtb| location of that control
+sequence is stored in |cur_cs|; otherwise |cur_cs| is set to zero.
+
+Underlying this simple description is a certain amount of complexity
+because of all the cases that need to be handled.
+However, the inner loop of |get_next| is reasonably short and fast.
+
+When |get_next| is asked to get the next token of a \.{\\read} line,
+it sets |cur_cmd==cur_chr==cur_cs==0| in the case that no more tokens
+appear on that line. (There might not be any tokens at all, if the
+|end_line_char| has |ignore| as its catcode.)
+
+@ The value of |par_loc| is the |eqtb| address of `\.{\\par}'. This quantity
+is needed because a blank line of input is supposed to be exactly equivalent
+to the appearance of \.{\\par}; we must set |cur_cs:=par_loc|
+when detecting a blank line.
+
+The same is true for the input, for the warning message, since input
+is expected by default before every scanning and hence setting of
+|cur_cs|.
+
+@<Glob...@>=
+static pointer @!par_loc; /*location of `\.{\\par}' in |eqtb|*/
+static halfword @!par_token; /*token representing `\.{\\par}'*/
+static pointer @!input_loc; /*location of `\.{\\input}' in |eqtb|*/
+static halfword @!input_token; /*token representing `\.{\\input}'*/
+
+@ @<Put each...@>=
+primitive("par", par_end, 256); /*cf.\ |scan_file_name|*/
+@!@:par\_}{\.{\\par} primitive@>
+par_loc=cur_val;par_token=cs_token_flag+par_loc;
+
+@ @<Cases of |print_cmd_chr|...@>=
+case par_end: print_esc("par");@+break;
+
+@ Before getting into |get_next|, let's consider the subroutine that
+is called when an `\.{\\outer}' control sequence has been scanned or
+when the end of a file has been reached. These two cases are distinguished
+by |cur_cs|, which is zero at the end of a file.
+
+@p static void check_outer_validity(void)
+{@+pointer p; /*points to inserted token list*/
+pointer @!q; /*auxiliary pointer*/
+if (scanner_status!=normal)
+ {@+deletions_allowed=false;
+ @<Back up an outer control sequence so that it can be reread@>;
+ if (scanner_status > skipping)
+ @<Tell the user what has run away and try to recover@>@;
+ else{@+print_err("Incomplete ");print_cmd_chr(if_test, cur_if);
+@.Incomplete \\if...@>
+ print("; all text was ignored after line ");print_int(skip_line);
+ help3("A forbidden control sequence occurred in skipped text.",@/
+ "This kind of error happens when you say `\\if...' and forget",@/
+ "the matching `\\fi'. I've inserted a `\\fi'; this might work.");
+ if (cur_cs!=0) cur_cs=0;
+ else help_line[2]=@|
+ "The file ended while I was skipping conditional text.";
+ cur_tok=cs_token_flag+frozen_fi;ins_error();
+ }
+ deletions_allowed=true;
+ }
+}
+
+@ An outer control sequence that occurs in a \.{\\read} will not be reread,
+since the error recovery for \.{\\read} is not very powerful.
+
+@<Back up an outer control sequence so that it can be reread@>=
+if (cur_cs!=0)
+ {@+if ((state==token_list)||(name < 1)||(name > 17))
+ {@+p=get_avail();info(p)=cs_token_flag+cur_cs;
+ back_list(p); /*prepare to read the control sequence again*/
+ }
+ cur_cmd=spacer;cur_chr=' '; /*replace it by a space*/
+ }
+
+@ @<Tell the user what has run away...@>=
+{@+runaway(); /*print a definition, argument, or preamble*/
+if (cur_cs==0) print_err("File ended");
+@.File ended while scanning...@>
+else{@+cur_cs=0;print_err("Forbidden control sequence found");
+@.Forbidden control sequence...@>
+ }
+print(" while scanning ");
+@<Print either `\.{definition}' or `\.{use}' or `\.{preamble}' or `\.{text}',
+and insert tokens that should lead to recovery@>;
+print(" of ");sprint_cs(warning_index);
+help4("I suspect you have forgotten a `}', causing me",@/
+"to read past where you wanted me to stop.",@/
+"I'll try to recover; but if the error is serious,",@/
+"you'd better type `E' or `X' now and fix your file.");@/
+error();
+}
+
+@ The recovery procedure can't be fully understood without knowing more
+about the \TeX\ routines that should be aborted, but we can sketch the
+ideas here: For a runaway definition or a runaway balanced text
+we will insert a right brace; for a
+runaway preamble, we will insert a special \.{\\cr} token and a right
+brace; and for a runaway argument, we will set |long_state| to
+|outer_call| and insert \.{\\par}.
+
+@<Print either `\.{definition}' or...@>=
+p=get_avail();
+switch (scanner_status) {
+case defining: {@+print("definition");info(p)=right_brace_token+'}';
+ } @+break;
+case matching: {@+print("use");info(p)=par_token;long_state=outer_call;
+ } @+break;
+case aligning: {@+print("preamble");info(p)=right_brace_token+'}';q=p;
+ p=get_avail();link(p)=q;info(p)=cs_token_flag+frozen_cr;
+ align_state=-1000000;
+ } @+break;
+case absorbing: {@+print("text");info(p)=right_brace_token+'}';
+ }
+} /*there are no other cases*/
+ins_list(p)
+
+@ We need to mention a procedure here that may be called by |get_next|.
+
+@p static void firm_up_the_line(void);
+
+@ Now we're ready to take the plunge into |get_next| itself. Parts of
+this routine are executed more often than any other instructions of \TeX.
+@^mastication@>@^inner loop@>
+
+@p static void get_next(void) /*sets |cur_cmd|, |cur_chr|, |cur_cs| to next token*/
+{@+ /*go here to get the next input token*/
+ /*go here to eat the next character from a file*/
+ /*go here to digest it again*/
+ /*go here to start looking for a control sequence*/
+ /*go here when a control sequence has been found*/
+ /*go here when the next input token has been got*/
+int k; /*an index into |buffer|*/
+halfword @!t; /*a token*/
+int @!cat; /*|cat_code(cur_chr)|, usually*/
+ASCII_code @!c, @!cc; /*constituents of a possible expanded code*/
+int @!d; /*number of excess characters in an expanded code*/
+restart: cur_cs=0;
+if (state!=token_list)
+@<Input from external file, |goto restart| if no input found@>@;
+else@<Input from token list, |goto restart| if end of list or if a parameter
+needs to be expanded@>;
+@<If an alignment entry has just ended, take appropriate action@>;
+}
+
+@ An alignment entry ends when a tab or \.{\\cr} occurs, provided that the
+current level of braces is the same as the level that was present at the
+beginning of that alignment entry; i.e., provided that |align_state| has
+returned to the value it had after the \<u_j> template for that entry.
+@^inner loop@>
+
+@<If an alignment entry has just ended, take appropriate action@>=
+if (cur_cmd <= car_ret) if (cur_cmd >= tab_mark) if (align_state==0)
+ @<Insert the \(v)\<v_j> template and |goto restart|@>@;
+
+@ @<Input from external file, |goto restart| if no input found@>=
+@^inner loop@>
+{@+get_cur_chr: if (loc <= limit) /*current line not yet finished*/
+ {@+cur_chr=buffer[loc];incr(loc);
+ @<Set |cur_file_line| based on the information in |cur_input|@>@;
+ reswitch: cur_cmd=cat_code(cur_chr);
+ @<Change state if necessary, and |goto get_cur_chr| if the current character
+should be ignored, or |goto reswitch| if the current character changes to
+another@>;
+ }
+else{@+state=new_line;@/
+ @<Move to next line of file, or |goto restart| if there is no next line,
+or |return| if a \.{\\read} line has finished@>;
+ check_interrupt;
+ goto get_cur_chr;
+ }
+}
+
+@ The following 48-way switch accomplishes the scanning quickly, assuming
+that a decent \PASCAL\ compiler has translated the code. Note that the numeric
+values for |mid_line|, |skip_blanks|, and |new_line| are spaced
+apart from each other by |max_char_code+1|, so we can add a character's
+command code to the state to get a single number that characterizes both.
+
+@d any_state_plus(A) case mid_line+A:
+ case skip_blanks+A: case new_line+A
+
+@<Change state if necessary...@>=
+switch (state+cur_cmd) {
+@<Cases where character is ignored@>: goto get_cur_chr;
+any_state_plus(escape): @<Scan a control sequence and set |state:=skip_blanks|
+or |mid_line|@>@;@+break;
+any_state_plus(active_char): @<Process an active-character control sequence
+and set |state:=mid_line|@>@;@+break;
+any_state_plus(sup_mark): @<If this |sup_mark| starts an expanded character
+like~\.{\^\^A} or~\.{\^\^df}, then |goto reswitch|, otherwise set |state:=mid_line|@>@;@+break;
+any_state_plus(invalid_char): @<Decry the invalid character and |goto restart|@>@;
+@t\4@>@<Handle situations involving spaces, braces, changes of state@>@;
+default:do_nothing;
+}
+
+@ @<Cases where character is ignored@>=
+any_state_plus(ignore): case skip_blanks+spacer:
+ case new_line+spacer
+
+@ We go to |restart| instead of to |get_cur_chr|, because |state| might equal
+|token_list| after the error has been dealt with
+(cf.\ |clear_for_error_prompt|).
+
+@<Decry the invalid...@>=
+{@+print_err("Text line contains an invalid character");
+@.Text line contains...@>
+help2("A funny symbol that I can't read has just been input.",@/
+"Continue, and I'll forget that it ever happened.");@/
+deletions_allowed=false;error();deletions_allowed=true;
+goto restart;
+}
+
+@ @d add_delims_to(A) A+math_shift: A+tab_mark: A+mac_param:
+ A+sub_mark: A+letter: A+other_char
+
+@<Handle situations involving spaces, braces, changes of state@>=
+case mid_line+spacer: @<Enter |skip_blanks| state, emit a space@>@;@+break;
+case mid_line+car_ret: @<Finish line, emit a space@>@;@+break;
+case skip_blanks+car_ret: any_state_plus(comment):
+ @<Finish line, |goto switch|@>@;
+case new_line+car_ret: @<Finish line, emit a \.{\\par}@>@;@+break;
+case mid_line+left_brace: incr(align_state);@+break;
+case skip_blanks+left_brace: case new_line+left_brace: {@+
+ state=mid_line;incr(align_state);
+ } @+break;
+case mid_line+right_brace: decr(align_state);@+break;
+case skip_blanks+right_brace: case new_line+right_brace: {@+
+ state=mid_line;decr(align_state);
+ } @+break;
+add_delims_to(case skip_blanks): add_delims_to(
+ case new_line): state=mid_line;@+break;
+
+@ When a character of type |spacer| gets through, its character code is
+changed to $\.{"\ "}=040$. This means that the ASCII codes for tab and space,
+and for the space inserted at the end of a line, will
+be treated alike when macro parameters are being matched. We do this
+since such characters are indistinguishable on most computer terminal displays.
+
+@<Finish line, emit a space@>=
+{@+loc=limit+1;cur_cmd=spacer;cur_chr=' ';
+}
+
+@ The following code is performed only when |cur_cmd==spacer|.
+
+@<Enter |skip_blanks| state, emit a space@>=
+{@+state=skip_blanks;cur_chr=' ';
+}
+
+@ @<Finish line, |goto switch|@>=
+{@+loc=limit+1;goto get_cur_chr;
+}
+
+@ @<Finish line, emit a \.{\\par}@>=
+{@+loc=limit+1;cur_cs=par_loc;cur_cmd=eq_type(cur_cs);
+cur_chr=equiv(cur_cs);
+if (cur_cmd >= outer_call) check_outer_validity();
+}
+
+@ Notice that a code like \.{\^\^8} becomes \.x if not followed by a hex digit.
+
+@d is_hex(A) (((A >= '0')&&(A <= '9'))||((A >= 'a')&&(A <= 'f')))
+@d hex_to_cur_chr
+ if (c <= '9') cur_chr=c-'0';@+else cur_chr=c-'a'+10;
+ if (cc <= '9') cur_chr=16*cur_chr+cc-'0';
+ else cur_chr=16*cur_chr+cc-'a'+10
+
+@<If this |sup_mark| starts an expanded character...@>=
+{@+if (cur_chr==buffer[loc]) if (loc < limit)
+ {@+c=buffer[loc+1];@+if (c < 0200) /*yes we have an expanded char*/
+ {@+loc=loc+2;
+ if (is_hex(c)) if (loc <= limit)
+ {@+cc=buffer[loc];@+if (is_hex(cc))
+ {@+incr(loc);hex_to_cur_chr;goto reswitch;
+ }
+ }
+ if (c < 0100) cur_chr=c+0100;@+else cur_chr=c-0100;
+ goto reswitch;
+ }
+ }
+state=mid_line;
+}
+
+@ @<Process an active-character...@>=
+{@+cur_cs=cur_chr+active_base;
+cur_cmd=eq_type(cur_cs);cur_chr=equiv(cur_cs);state=mid_line;
+if (cur_cmd >= outer_call) check_outer_validity();
+}
+
+@ Control sequence names are scanned only when they appear in some line of
+a file; once they have been scanned the first time, their |eqtb| location
+serves as a unique identification, so \TeX\ doesn't need to refer to the
+original name any more except when it prints the equivalent in symbolic form.
+
+The program that scans a control sequence has been written carefully
+in order to avoid the blowups that might otherwise occur if a malicious
+user tried something like `\.{\\catcode\'15=0}'. The algorithm might
+look at |buffer[limit+1]|, but it never looks at |buffer[limit+2]|.
+
+If expanded characters like `\.{\^\^A}' or `\.{\^\^df}'
+appear in or just following
+a control sequence name, they are converted to single characters in the
+buffer and the process is repeated, slowly but surely.
+
+@<Scan a control...@>=
+{@+if (loc > limit) cur_cs=null_cs; /*|state| is irrelevant in this case*/
+else{@+start_cs: k=loc;cur_chr=buffer[k];cat=cat_code(cur_chr);
+ incr(k);
+ if (cat==letter) state=skip_blanks;
+ else if (cat==spacer) state=skip_blanks;
+ else state=mid_line;
+ if ((cat==letter)&&(k <= limit))
+ @<Scan ahead in the buffer until finding a nonletter; if an expanded code
+is encountered, reduce it and |goto start_cs|; otherwise if a multiletter
+control sequence is found, adjust |cur_cs| and |loc|, and |goto found|@>@;
+ else@<If an expanded code is present, reduce it and |goto start_cs|@>;
+ cur_cs=single_base+buffer[loc];incr(loc);
+ }
+found: cur_cmd=eq_type(cur_cs);cur_chr=equiv(cur_cs);
+if (cur_cmd >= outer_call) check_outer_validity();
+}
+
+@ Whenever we reach the following piece of code, we will have
+|cur_chr==buffer[k-1]| and |k <= limit+1| and |cat==cat_code(cur_chr)|. If an
+expanded code like \.{\^\^A} or \.{\^\^df} appears in |buffer[(k-1)dotdot(k+1)]|
+or |buffer[(k-1)dotdot(k+2)]|, we
+will store the corresponding code in |buffer[k-1]| and shift the rest of
+the buffer left two or three places.
+
+@<If an expanded...@>=
+{@+if (buffer[k]==cur_chr) @+if (cat==sup_mark) @+if (k < limit)
+ {@+c=buffer[k+1];@+if (c < 0200) /*yes, one is indeed present*/
+ {@+d=2;
+ if (is_hex(c)) @+if (k+2 <= limit)
+ {@+cc=buffer[k+2];@+if (is_hex(cc)) incr(d);
+ }
+ if (d > 2)
+ {@+hex_to_cur_chr;buffer[k-1]=cur_chr;
+ }
+ else if (c < 0100) buffer[k-1]=c+0100;
+ else buffer[k-1]=c-0100;
+ limit=limit-d;first=first-d;
+ while (k <= limit)
+ {@+buffer[k]=buffer[k+d];incr(k);
+ }
+ goto start_cs;
+ }
+ }
+}
+
+@ @<Scan ahead in the buffer...@>=
+{@+@/do@+{cur_chr=buffer[k];cat=cat_code(cur_chr);incr(k);
+}@+ while (!((cat!=letter)||(k > limit)));
+@<If an expanded...@>;
+if (cat!=letter) decr(k);
+ /*now |k| points to first nonletter*/
+if (k > loc+1) /*multiletter control sequence has been scanned*/
+ {@+cur_cs=id_lookup(loc, k-loc);loc=k;goto found;
+ }
+}
+
+@ Let's consider now what happens when |get_next| is looking at a token list.
+We restore the file and line information.
+@^profiling@>
+
+@<Input from token list, |goto restart| if end of list or if a parameter needs
+to be expanded@>=
+if (loc!=null) /*list not exhausted*/
+@^inner loop@>
+ {@+t=info(loc); cur_file_line=fl_mem[loc];
+ loc=link(loc); /*move to next*/
+ if (t >= cs_token_flag) /*a control sequence token*/
+ {@+cur_cs=t-cs_token_flag;
+ cur_cmd=eq_type(cur_cs);cur_chr=equiv(cur_cs);
+ if (cur_cmd >= outer_call)
+ if (cur_cmd==dont_expand)
+ @<Get the next token, suppressing expansion@>@;
+ else check_outer_validity();
+ }
+ else{@+cur_cmd=t/0400;cur_chr=t%0400;
+ switch (cur_cmd) {
+ case left_brace: incr(align_state);@+break;
+ case right_brace: decr(align_state);@+break;
+ case out_param: @<Insert macro parameter and |goto restart|@>@;
+ default:do_nothing;
+ }
+ }
+ }
+else{@+ /*we are done with this token list*/
+ end_token_list();goto restart; /*resume previous level*/
+ }
+
+@ The present point in the program is reached only when the |expand|
+routine has inserted a special marker into the input. In this special
+case, |info(loc)| is known to be a control sequence token, and |link(loc)==null|.
+
+@d no_expand_flag 257 /*this characterizes a special variant of |relax|*/
+
+@<Get the next token, suppressing expansion@>=
+{@+cur_cs=info(loc)-cs_token_flag;loc=null;@/
+cur_cmd=eq_type(cur_cs);cur_chr=equiv(cur_cs);
+if (cur_cmd > max_command)
+ {@+cur_cmd=relax;cur_chr=no_expand_flag;
+ }
+}
+
+@ @<Insert macro parameter...@>=
+{@+begin_token_list(param_stack[param_start+cur_chr-1], parameter);
+goto restart;
+}
+
+@ All of the easy branches of |get_next| have now been taken care of.
+There is one more branch.
+
+@d end_line_char_inactive (end_line_char < 0)||(end_line_char > 255)
+
+@<Move to next line of file, or |goto restart|...@>=
+if (name > 17) @<Read next line of file into |buffer|, or |goto restart| if
+the file has ended@>@;
+else{@+if (!terminal_input) /*\.{\\read} line has ended*/
+ {@+cur_cmd=0;cur_chr=0;
+ @<Set |cur_file_line| when a \.{\\read} line ends@>@;
+ return;
+ }
+ if (input_ptr > 0) /*text was inserted during error recovery*/
+ {@+end_file_reading();goto restart; /*resume previous level*/
+ }
+ if (selector < log_only) open_log_file();
+ if (interaction > nonstop_mode)
+ {@+if (end_line_char_inactive) incr(limit);
+ if (limit==start) /*previous line was empty*/
+ print_nl("(Please type a command or say `\\end')");
+@.Please type...@>
+ print_ln();first=start;
+ prompt_input("*"); /*input on-line into |buffer|*/
+@.*\relax@>
+ limit=last;
+ if (end_line_char_inactive) decr(limit);
+ else buffer[limit]=end_line_char;
+ first=limit+1;
+ loc=start;
+ }
+ else fatal_error("*** (job aborted, no legal \\end found)");
+@.job aborted@>
+ /*nonstop mode, which is intended for overnight batch processing,
+ never waits for on-line input*/
+ }
+
+@ The global variable |force_eof| is normally |false|; it is set |true|
+by an \.{\\endinput} command.
+
+@<Glob...@>=
+static bool @!force_eof; /*should the next \.{\\input} be aborted early?*/
+
+@ @<Read next line of file into |buffer|, or |goto restart| if the file has
+ended@>=
+{@+incr(line);first=start;
+@<check |line| for overflow@>@;
+if (!force_eof)
+ if (name <= 19)
+ {@+if (pseudo_input()) /*not end of file*/
+ firm_up_the_line(); /*this sets |limit|*/
+ else if ((every_eof!=null)&&!eof_seen[index])
+ {@+limit=first-1;eof_seen[index]=true; /*fake one empty line*/
+ begin_token_list(every_eof, every_eof_text);goto restart;
+ }
+ else force_eof=true;
+ }
+ else
+ {@+if (input_ln(&cur_file, true)) /*not end of file*/
+ firm_up_the_line(); /*this sets |limit|*/
+ else if ((every_eof!=null)&&!eof_seen[index])
+ {@+limit=first-1;eof_seen[index]=true; /*fake one empty line*/
+ begin_token_list(every_eof, every_eof_text);goto restart;
+ }
+ else force_eof=true;
+ }
+if (force_eof)
+ {@+if (tracing_nesting > 0)
+ if ((grp_stack[in_open]!=cur_boundary)||@|
+ (if_stack[in_open]!=cond_ptr)) file_warning();
+ /*give warning for some unfinished groups and/or conditionals*/
+ if (name >= 19)
+ {@+print_char(')');decr(open_parens);
+ update_terminal; /*show user that file has been read*/
+ }
+ force_eof=false;
+ end_file_reading(); /*resume previous level*/
+ check_outer_validity();goto restart;
+ }
+if (end_line_char_inactive) decr(limit);
+else buffer[limit]=end_line_char;
+first=limit+1;loc=start; /*ready to read*/
+}
+
+@ If the user has set the |pausing| parameter to some positive value,
+and if nonstop mode has not been selected, each line of input is displayed
+on the terminal and the transcript file, followed by `\.{=>}'.
+\TeX\ waits for a response. If the response is simply |carriage_return|, the
+line is accepted as it stands, otherwise the line typed is
+used instead of the line in the file.
+
+@p static void firm_up_the_line(void)
+{@+int k; /*an index into |buffer|*/
+limit=last;
+if (pausing > 0) if (interaction > nonstop_mode)
+ {@+wake_up_terminal;print_ln();
+ if (start < limit) for (k=start; k<=limit-1; k++) printn(buffer[k]);
+ first=limit;prompt_input("=>"); /*wait for user response*/
+@.=>@>
+ if (last > first)
+ {@+for (k=first; k<=last-1; k++) /*move line down in buffer*/
+ buffer[k+start-first]=buffer[k];
+ limit=start+last-first;
+ }
+ }
+}
+
+@ Since |get_next| is used so frequently in \TeX, it is convenient
+to define three related procedures that do a little more:
+
+\yskip\hang|get_token| not only sets |cur_cmd| and |cur_chr|, it
+also sets |cur_tok|, a packed halfword version of the current token.
+
+\yskip\hang|get_x_token|, meaning ``get an expanded token,'' is like
+|get_token|, but if the current token turns out to be a user-defined
+control sequence (i.e., a macro call), or a conditional,
+or something like \.{\\topmark} or \.{\\expandafter} or \.{\\csname},
+it is eliminated from the input by beginning the expansion of the macro
+or the evaluation of the conditional.
+
+\yskip\hang|x_token| is like |get_x_token| except that it assumes that
+|get_next| has already been called.
+
+\yskip\noindent
+In fact, these three procedures account for almost every use of |get_next|.
+
+@ No new control sequences will be defined except during a call of
+|get_token|, or when \.{\\csname} compresses a token list, because
+|no_new_control_sequence| is always |true| at other times.
+
+@p static void get_token(void) /*sets |cur_cmd|, |cur_chr|, |cur_tok|*/
+{@+no_new_control_sequence=false;get_next();no_new_control_sequence=true;
+@^inner loop@>
+if (cur_cs==0) cur_tok=(cur_cmd*0400)+cur_chr;
+else cur_tok=cs_token_flag+cur_cs;
+}
+
+@* Expanding the next token.
+Only a dozen or so command codes | > max_command| can possibly be returned by
+|get_next|; in increasing order, they are |undefined_cs|, |expand_after|,
+|no_expand|, |input|, |if_test|, |fi_or_else|, |cs_name|, |convert|, |the|,
+|top_bot_mark|, |call|, |long_call|, |outer_call|, |long_outer_call|, and
+|end_template|.{\emergencystretch=40pt\par}
+
+The |expand| subroutine is used when |cur_cmd > max_command|. It removes a
+``call'' or a conditional or one of the other special operations just
+listed. It follows that |expand| might invoke itself recursively. In all
+cases, |expand| destroys the current token, but it sets things up so that
+the next |get_next| will deliver the appropriate next token. The value of
+|cur_tok| need not be known when |expand| is called.
+
+Since several of the basic scanning routines communicate via global variables,
+their values are saved as local variables of |expand| so that
+recursive calls don't invalidate them.
+@^recursion@>
+
+@p @t\4@>@<Declare the procedure called |macro_call|@>@;@/
+@t\4@>@<Declare the procedure called |insert_relax|@>@;@/
+@t\4@>@<Declare \eTeX\ procedures for expanding@>@;@/
+static void pass_text(void);
+static void start_input(void);
+static void conditional(void);
+static void get_x_token(void);
+static void conv_toks(void);
+static void ins_the_toks(void);
+static void expand(void)
+{@+
+halfword t; /*token that is being ``expanded after''*/
+pointer @!p, @!q, @!r; /*for list manipulation*/
+int @!j; /*index into |buffer|*/
+int @!cv_backup; /*to save the global quantity |cur_val|*/
+small_number @!cvl_backup, @!radix_backup, @!co_backup;
+ /*to save |cur_val_level|, etc.*/
+pointer @!backup_backup; /*to save |link(backup_head)|*/
+small_number @!save_scanner_status; /*temporary storage of |scanner_status|*/
+cv_backup=cur_val;cvl_backup=cur_val_level;radix_backup=radix;
+co_backup=cur_order;backup_backup=link(backup_head);
+reswitch:
+if (cur_cmd < call) @<Expand a nonmacro@>@;
+else if (cur_cmd < end_template) macro_call();
+else@<Insert a token containing |frozen_endv|@>;
+cur_val=cv_backup;cur_val_level=cvl_backup;radix=radix_backup;
+cur_order=co_backup;link(backup_head)=backup_backup;
+}
+
+@ @<Expand a nonmacro@>=
+{@+if (tracing_commands > 1) show_cur_cmd_chr();
+switch (cur_cmd) {
+case top_bot_mark: @<Insert the \(a)appropriate mark text into the scanner@>@;@+break;
+case expand_after: switch (cur_chr) {
+case 0: @<Expand the token after the next token@>@;@+break;
+case 1: @<Negate a boolean conditional and |goto reswitch|@>@;@+break;
+@/@<Cases for |expandafter|@>@/
+} @+break; /*there are no other cases*/
+case no_expand: @<Suppress expansion of the next token@>@;@+break;
+case cs_name: @<Manufacture a control sequence name@>@;@+break;
+case convert: conv_toks();@+break; /*this procedure is discussed in Part 27 below*/
+case the: ins_the_toks();@+break; /*this procedure is discussed in Part 27 below*/
+case if_test: conditional();@+break; /*this procedure is discussed in Part 28 below*/
+case fi_or_else: @<Terminate the current conditional and skip to \.{\\fi}@>@;@+break;
+case input: @<Initiate or terminate input from a file@>;@+break;
+default:@<Complain about an undefined macro@>@;
+}
+}
+
+@ It takes only a little shuffling to do what \TeX\ calls \.{\\expandafter}.
+
+@<Expand the token after...@>=
+{@+get_token();t=cur_tok;get_token();
+if (cur_cmd > max_command) expand();@+else back_input();
+cur_tok=t;back_input();
+}
+
+@ The implementation of \.{\\noexpand} is a bit trickier, because it is
+necessary to insert a special `|dont_expand|' marker into \TeX's reading
+mechanism. This special marker is processed by |get_next|, but it does
+not slow down the inner loop.
+
+Since \.{\\outer} macros might arise here, we must also
+clear the |scanner_status| temporarily.
+
+@<Suppress expansion...@>=
+{@+save_scanner_status=scanner_status;scanner_status=normal;
+get_token();scanner_status=save_scanner_status;t=cur_tok;
+back_input(); /*now |start| and |loc| point to the backed-up token |t|*/
+if (t >= cs_token_flag)
+ {@+p=get_avail();info(p)=cs_token_flag+frozen_dont_expand;
+ link(p)=loc;start=p;loc=p;
+ }
+}
+
+@ @<Complain about an undefined macro@>=
+{@+print_err("Undefined control sequence");
+@.Undefined control sequence@>
+help5("The control sequence at the end of the top line",@/
+"of your error message was never \\def'ed. If you have",@/
+"misspelled it (e.g., `\\hobx'), type `I' and the correct",@/
+"spelling (e.g., `I\\hbox'). Otherwise just continue,",@/
+"and I'll forget about whatever was undefined.");
+error();
+}
+
+@ The |expand| procedure and some other routines that construct token
+lists find it convenient to use the following macros, which are valid only if
+the variables |p| and |q| are reserved for token-list building.
+Here we add code to store file and line information for each token.
+@^profiling@>
+
+@d store_new_token(A) {@+q=get_avail();link(p)=q;info(q)=A;
+ fl_mem[q]=cur_file_line;
+ p=q; /*|link(p)| is |null|*/
+ }
+@d fast_store_new_token(A) {@+fast_get_avail(q);link(p)=q;info(q)=A;
+ fl_mem[q]=cur_file_line;
+ p=q; /*|link(p)| is |null|*/
+ }
+
+@ @<Manufacture a control...@>=
+{@+r=get_avail();p=r; /*head of the list of characters*/
+incr(incsname_state);
+@/do@+{get_x_token();
+if (cur_cs==0) store_new_token(cur_tok);
+}@+ while (!(cur_cs!=0));
+if (cur_cmd!=end_cs_name) @<Complain about missing \.{\\endcsname}@>;
+decr(incsname_state);
+@<Look up the characters of list |r| in the hash table, and set |cur_cs|@>;
+flush_list(r);
+if (eq_type(cur_cs)==undefined_cs)
+ {@+eq_define(cur_cs, relax, 256); /*N.B.: The |save_stack| might change*/
+ } /*the control sequence will now match `\.{\\relax}'*/
+cur_tok=cur_cs+cs_token_flag;back_input();
+}
+
+@ @<Complain about missing \.{\\endcsname}@>=
+{@+print_err("Missing ");print_esc("endcsname");print(" inserted");
+@.Missing \\endcsname...@>
+help2("The control sequence marked <to be read again> should",@/
+ "not appear between \\csname and \\endcsname.");
+back_error();
+}
+
+@ @<Look up the characters of list |r| in the hash table...@>=
+j=first;p=link(r);
+while (p!=null)
+ {@+if (j >= max_buf_stack)
+ {@+max_buf_stack=j+1;
+ if (max_buf_stack==buf_size)
+ overflow("buffer size", buf_size);
+@:TeX capacity exceeded buffer size}{\quad buffer size@>
+ }
+ buffer[j]=info(p)%0400;incr(j);p=link(p);
+ }
+if (j==first) cur_cs=null_cs; /*the list is empty*/
+else if (j > first+1)
+ {@+no_new_control_sequence=false;cur_cs=id_lookup(first, j-first);
+ no_new_control_sequence=true;
+ }
+else cur_cs=single_base+buffer[first] /*the list has length one*/
+
+@ An |end_template| command is effectively changed to an |endv| command
+by the following code. (The reason for this is discussed below; the
+|frozen_end_template| at the end of the template has passed the
+|check_outer_validity| test, so its mission of error detection has been
+accomplished.)
+
+@<Insert a token containing |frozen_endv|@>=
+{@+cur_tok=cs_token_flag+frozen_endv;back_input();
+}
+
+@ The processing of \.{\\input} involves the |start_input| subroutine,
+which will be declared later; the processing of \.{\\endinput} is trivial.
+
+@<Put each...@>=
+primitive("input", input, 0);@/
+@!@:input\_}{\.{\\input} primitive@>
+input_loc=cur_val;input_token=cs_token_flag+input_loc;
+primitive("endinput", input, 1);@/
+@!@:end\_input\_}{\.{\\endinput} primitive@>
+
+@ @<Cases of |print_cmd_chr|...@>=
+case input: if (chr_code==0) print_esc("input")
+ @/@<Cases of |input| for |print_cmd_chr|@>;@/
+ else print_esc("endinput");@+break;
+
+@ @<Initiate or terminate input...@>=
+if (cur_chr==1) force_eof=true
+@/@<Cases for |input|@>;@/
+else if (name_in_progress) insert_relax();
+else start_input()
+
+@ Sometimes the expansion looks too far ahead, so we want to insert
+a harmless \.{\\relax} into the user's input.
+
+@<Declare the procedure called |insert_relax|@>=
+static void insert_relax(void)
+{@+cur_tok=cs_token_flag+cur_cs;back_input();
+cur_tok=cs_token_flag+frozen_relax;back_input();token_type=inserted;
+}
+
+@ Here is a recursive procedure that is \TeX's usual way to get the
+next token of input. It has been slightly optimized to take account of
+common cases.
+
+@p static void get_x_token(void) /*sets |cur_cmd|, |cur_chr|, |cur_tok|,
+ and expands macros*/
+{@+
+restart: get_next();
+@^inner loop@>
+if (cur_cmd <= max_command) goto done;
+if (cur_cmd >= call)
+ if (cur_cmd < end_template) macro_call();
+ else{@+cur_cs=frozen_endv;cur_cmd=endv;
+ goto done; /*|cur_chr==null_list|*/
+ }
+else expand();
+goto restart;
+done: if (cur_cs==0) cur_tok=(cur_cmd*0400)+cur_chr;
+else cur_tok=cs_token_flag+cur_cs;
+}
+
+@ The |get_x_token| procedure is essentially equivalent to two consecutive
+procedure calls: |get_next;x_token|.
+
+@p static void x_token(void) /*|get_x_token| without the initial |get_next|*/
+{@+while (cur_cmd > max_command)
+ {@+expand();
+ get_next();
+ }
+if (cur_cs==0) cur_tok=(cur_cmd*0400)+cur_chr;
+else cur_tok=cs_token_flag+cur_cs;
+}
+
+@ A control sequence that has been \.{\\def}'ed by the user is expanded by
+\TeX's |macro_call| procedure.
+
+Before we get into the details of |macro_call|, however, let's consider the
+treatment of primitives like \.{\\topmark}, since they are essentially
+macros without parameters. The token lists for such marks are kept in a
+global array of five pointers; we refer to the individual entries of this
+array by symbolic names |top_mark|, etc. The value of |top_mark| is either
+|null| or a pointer to the reference count of a token list.
+
+@d marks_code 5 /*add this for \.{\\topmarks} etc.*/
+@#
+@d top_mark_code 0 /*the mark in effect at the previous page break*/
+@d first_mark_code 1 /*the first mark between |top_mark| and |bot_mark|*/
+@d bot_mark_code 2 /*the mark in effect at the current page break*/
+@d split_first_mark_code 3 /*the first mark found by \.{\\vsplit}*/
+@d split_bot_mark_code 4 /*the last mark found by \.{\\vsplit}*/
+@d top_mark cur_mark[top_mark_code]
+@d first_mark cur_mark[first_mark_code]
+@d bot_mark cur_mark[bot_mark_code]
+@d split_first_mark cur_mark[split_first_mark_code]
+@d split_bot_mark cur_mark[split_bot_mark_code]
+
+@<Glob...@>=
+static pointer @!cur_mark0[split_bot_mark_code-top_mark_code+1],
+ *const @!cur_mark = @!cur_mark0-top_mark_code;
+ /*token lists for marks*/
+
+@ @<Set init...@>=
+top_mark=null;first_mark=null;bot_mark=null;
+split_first_mark=null;split_bot_mark=null;
+
+@ @<Put each...@>=
+primitive("topmark", top_bot_mark, top_mark_code);
+@!@:top\_mark\_}{\.{\\topmark} primitive@>
+primitive("firstmark", top_bot_mark, first_mark_code);
+@!@:first\_mark\_}{\.{\\firstmark} primitive@>
+primitive("botmark", top_bot_mark, bot_mark_code);
+@!@:bot\_mark\_}{\.{\\botmark} primitive@>
+primitive("splitfirstmark", top_bot_mark, split_first_mark_code);
+@!@:split\_first\_mark\_}{\.{\\splitfirstmark} primitive@>
+primitive("splitbotmark", top_bot_mark, split_bot_mark_code);
+@!@:split\_bot\_mark\_}{\.{\\splitbotmark} primitive@>
+
+@ @<Cases of |print_cmd_chr|...@>=
+case top_bot_mark: {@+switch ((chr_code%marks_code)) {
+ case first_mark_code: print_esc("firstmark");@+break;
+ case bot_mark_code: print_esc("botmark");@+break;
+ case split_first_mark_code: print_esc("splitfirstmark");@+break;
+ case split_bot_mark_code: print_esc("splitbotmark");@+break;
+ default:print_esc("topmark");
+ }
+ if (chr_code >= marks_code) print_char('s');
+ } @+break;
+
+@ The following code is activated when |cur_cmd==top_bot_mark| and
+when |cur_chr| is a code like |top_mark_code|.
+
+@<Insert the \(a)appropriate mark text into the scanner@>=
+{@+t=cur_chr%marks_code;
+if (cur_chr >= marks_code) scan_register_num();@+else cur_val=0;
+if (cur_val==0) cur_ptr=cur_mark[t];
+else@<Compute the mark pointer for mark type |t| and class |cur_val|@>;
+if (cur_ptr!=null) begin_token_list(cur_ptr, mark_text);
+}
+
+@ Now let's consider |macro_call| itself, which is invoked when \TeX\ is
+scanning a control sequence whose |cur_cmd| is either |call|, |long_call|,
+|outer_call|, or |long_outer_call|. The control sequence definition
+appears in the token list whose reference count is in location |cur_chr|
+of |mem|.
+
+The global variable |long_state| will be set to |call| or to |long_call|,
+depending on whether or not the control sequence disallows \.{\\par}
+in its parameters. The |get_next| routine will set |long_state| to
+|outer_call| and emit \.{\\par}, if a file ends or if an \.{\\outer}
+control sequence occurs in the midst of an argument.
+
+@<Glob...@>=
+static int @!long_state; /*governs the acceptance of \.{\\par}*/
+
+@ The parameters, if any, must be scanned before the macro is expanded.
+Parameters are token lists without reference counts. They are placed on
+an auxiliary stack called |pstack| while they are being scanned, since
+the |param_stack| may be losing entries during the matching process.
+(Note that |param_stack| can't be gaining entries, since |macro_call| is
+the only routine that puts anything onto |param_stack|, and it
+is not recursive.)
+
+@<Glob...@>=
+static pointer @!pstack[9]; /*arguments supplied to a macro*/
+
+@ After parameter scanning is complete, the parameters are moved to the
+|param_stack|. Then the macro body is fed to the scanner; in other words,
+|macro_call| places the defined text of the control sequence at the
+top of\/ \TeX's input stack, so that |get_next| will proceed to read it
+next.
+
+The global variable |cur_cs| contains the |eqtb| address of the control sequence
+being expanded, when |macro_call| begins. If this control sequence has not been
+declared \.{\\long}, i.e., if its command code in the |eq_type| field is
+not |long_call| or |long_outer_call|, its parameters are not allowed to contain
+the control sequence \.{\\par}. If an illegal \.{\\par} appears, the macro
+call is aborted, and the \.{\\par} will be rescanned.
+
+@<Declare the procedure called |macro_call|@>=
+static void macro_call(void) /*invokes a user-defined control sequence*/
+{@+
+pointer r; /*current node in the macro's token list*/
+pointer @!p; /*current node in parameter token list being built*/
+pointer @!q; /*new node being put into the token list*/
+pointer @!s; /*backup pointer for parameter matching*/
+pointer @!t; /*cycle pointer for backup recovery*/
+pointer @!u, @!v; /*auxiliary pointers for backup recovery*/
+pointer @!rbrace_ptr; /*one step before the last |right_brace| token*/
+small_number @!n; /*the number of parameters scanned*/
+halfword @!unbalance; /*unmatched left braces in current parameter*/
+int @!m; /*the number of tokens or groups (usually)*/
+pointer @!ref_count; /*start of the token list*/
+small_number @!save_scanner_status; /*|scanner_status| upon entry*/
+pointer @!save_warning_index; /*|warning_index| upon entry*/
+ASCII_code @!match_chr; /*character used in parameter*/
+@<additional local variables for |macro_call|@>@;
+save_scanner_status=scanner_status;save_warning_index=warning_index;
+warning_index=cur_cs;ref_count=cur_chr;r=link(ref_count);n=0;
+if (tracing_macros > 0) @<Show the text of the macro being expanded@>;
+if (info(r)==protected_token) r=link(r);
+if (info(r)!=end_match_token)
+ @<Scan the parameters and make |link(r)| point to the macro body; but |goto end|
+if an illegal \.{\\par} is detected@>;
+@<Feed the macro body and its parameters to the scanner@>;
+end: scanner_status=save_scanner_status;warning_index=save_warning_index;
+@<update the macro stack@>@;
+}
+
+@ Before we put a new token list on the input stack, it is wise to clean off
+all token lists that have recently been depleted. Then a user macro that ends
+with a call to itself will not require unbounded stack space.
+
+@<Feed the macro body and its parameters to the scanner@>=
+while ((state==token_list)&&(loc==null)&&(token_type!=v_template))
+ end_token_list(); /*conserve stack space*/
+begin_token_list(ref_count, macro);name=warning_index;loc=link(r);
+if (n > 0)
+ {@+if (param_ptr+n > max_param_stack)
+ {@+max_param_stack=param_ptr+n;
+ if (max_param_stack > param_size)
+ overflow("parameter stack size", param_size);
+@:TeX capacity exceeded parameter stack size}{\quad parameter stack size@>
+ }
+ for (m=0; m<=n-1; m++) param_stack[param_ptr+m]=pstack[m];
+ param_ptr=param_ptr+n;
+ }
+
+@ At this point, the reader will find it advisable to review the explanation
+of token list format that was presented earlier, since many aspects of that
+format are of importance chiefly in the |macro_call| routine.
+
+The token list might begin with a string of compulsory tokens before the
+first |match| or |end_match|. In that case the macro name is supposed to be
+followed by those tokens; the following program will set |s==null| to
+represent this restriction. Otherwise |s| will be set to the first token of
+a string that will delimit the next parameter.
+
+@<Scan the parameters and make |link(r)| point to the macro body...@>=
+{@+scanner_status=matching;unbalance=0;
+long_state=eq_type(cur_cs);
+if (long_state >= outer_call) long_state=long_state-2;
+@/do@+{link(temp_head)=null;
+if ((info(r) > match_token+255)||(info(r) < match_token)) s=null;
+else{@+match_chr=info(r)-match_token;s=link(r);r=s;
+ p=temp_head;m=0;
+ }
+@<Scan a parameter until its delimiter string has been found; or, if |s=null|,
+simply scan the delimiter string@>;@/
+ /*now |info(r)| is a token whose command code is either |match| or |end_match|*/
+}@+ while (!(info(r)==end_match_token));
+}
+
+@ If |info(r)| is a |match| or |end_match| command, it cannot be equal to
+any token found by |get_token|. Therefore an undelimited parameter---i.e.,
+a |match| that is immediately followed by |match| or |end_match|---will
+always fail the test `|cur_tok==info(r)|' in the following algorithm.
+
+@<Scan a parameter until its delimiter string has been found; or,...@>=
+resume: get_token(); /*set |cur_tok| to the next token of input*/
+if (cur_tok==info(r))
+ @<Advance \(r)|r|; |goto found| if the parameter delimiter has been fully
+matched, otherwise |goto resume|@>;
+@<Contribute the recently matched tokens to the current parameter, and |goto
+resume| if a partial match is still in effect; but abort if |s=null|@>;
+if (cur_tok==par_token) if (long_state!=long_call)
+ @<Report a runaway argument and abort@>;
+if (cur_tok < right_brace_limit)
+ if (cur_tok < left_brace_limit)
+ @<Contribute an entire group to the current parameter@>@;
+ else@<Report an extra right brace and |goto resume|@>@;
+else@<Store the current token, but |goto resume| if it is a blank space
+that would become an undelimited parameter@>;
+incr(m);
+if (info(r) > end_match_token) goto resume;
+if (info(r) < match_token) goto resume;
+found: if (s!=null) @<Tidy up the parameter just scanned, and tuck it away@>@;
+
+@ @<Store the current token, but |goto resume| if it is...@>=
+{@+if (cur_tok==space_token)
+ if (info(r) <= end_match_token)
+ if (info(r) >= match_token) goto resume;
+store_new_token(cur_tok);
+}
+
+@ A slightly subtle point arises here: When the parameter delimiter ends
+with `\.{\#\{}', the token list will have a left brace both before and
+after the |end_match|\kern-.4pt. Only one of these should affect the
+|align_state|, but both will be scanned, so we must make a correction.
+
+@<Advance \(r)|r|; |goto found| if the parameter delimiter has been fully...@>=
+{@+r=link(r);
+if ((info(r) >= match_token)&&(info(r) <= end_match_token))
+ {@+if (cur_tok < left_brace_limit) decr(align_state);
+ goto found;
+ }
+else goto resume;
+}
+
+@ @<Report an extra right brace and |goto resume|@>=
+{@+back_input();print_err("Argument of ");sprint_cs(warning_index);
+@.Argument of \\x has...@>
+print(" has an extra }");
+help6("I've run across a `}' that doesn't seem to match anything.",@/
+ "For example, `\\def\\a#1{...}' and `\\a}' would produce",@/
+ "this error. If you simply proceed now, the `\\par' that",@/
+ "I've just inserted will cause me to report a runaway",@/
+ "argument that might be the root of the problem. But if",@/
+ "your `}' was spurious, just type `2' and it will go away.");
+incr(align_state);long_state=call;cur_tok=par_token;ins_error();
+goto resume;
+} /*a white lie; the \.{\\par} won't always trigger a runaway*/
+
+@ If |long_state==outer_call|, a runaway argument has already been reported.
+
+@<Report a runaway argument and abort@>=
+{@+if (long_state==call)
+ {@+runaway();print_err("Paragraph ended before ");
+@.Paragraph ended before...@>
+ sprint_cs(warning_index);print(" was complete");
+ help3("I suspect you've forgotten a `}', causing me to apply this",@/
+ "control sequence to too much text. How can we recover?",@/
+ "My plan is to forget the whole thing and hope for the best.");
+ back_error();
+ }
+pstack[n]=link(temp_head);align_state=align_state-unbalance;
+for (m=0; m<=n; m++) flush_list(pstack[m]);
+goto end;
+}
+
+@ When the following code becomes active, we have matched tokens from |s| to
+the predecessor of |r|, and we have found that |cur_tok!=info(r)|. An
+interesting situation now presents itself: If the parameter is to be
+delimited by a string such as `\.{ab}', and if we have scanned `\.{aa}',
+we want to contribute one `\.a' to the current parameter and resume
+looking for a `\.b'. The program must account for such partial matches and
+for others that can be quite complex. But most of the time we have |s==r|
+and nothing needs to be done.
+
+Incidentally, it is possible for \.{\\par} tokens to sneak in to certain
+parameters of non-\.{\\long} macros. For example, consider a case like
+`\.{\\def\\a\#1\\par!\{...\}}' where the first \.{\\par} is not followed
+by an exclamation point. In such situations it does not seem appropriate
+to prohibit the \.{\\par}, so \TeX\ keeps quiet about this bending of
+the rules.
+
+@<Contribute the recently matched tokens to the current parameter...@>=
+if (s!=r)
+ if (s==null) @<Report an improper use of the macro and abort@>@;
+ else{@+t=s;
+ @/do@+{store_new_token(info(t));incr(m);u=link(t);v=s;
+ loop@+{@+if (u==r)
+ if (cur_tok!=info(v)) goto done;
+ else{@+r=link(v);goto resume;
+ }
+ if (info(u)!=info(v)) goto done;
+ u=link(u);v=link(v);
+ }
+ done: t=link(t);
+ }@+ while (!(t==r));
+ r=s; /*at this point, no tokens are recently matched*/
+ }
+
+@ @<Report an improper use...@>=
+{@+print_err("Use of ");sprint_cs(warning_index);
+@.Use of x doesn't match...@>
+print(" doesn't match its definition");
+help4("If you say, e.g., `\\def\\a1{...}', then you must always",@/
+ "put `1' after `\\a', since control sequence names are",@/
+ "made up of letters only. The macro here has not been",@/
+ "followed by the required stuff, so I'm ignoring it.");
+error();goto end;
+}
+
+@ @<Contribute an entire group to the current parameter@>=
+{@+unbalance=1;
+@^inner loop@>
+loop@+{@+fast_store_new_token(cur_tok);get_token();
+ if (cur_tok==par_token) if (long_state!=long_call)
+ @<Report a runaway argument and abort@>;
+ if (cur_tok < right_brace_limit)
+ if (cur_tok < left_brace_limit) incr(unbalance);
+ else{@+decr(unbalance);
+ if (unbalance==0) goto done1;
+ }
+ }
+done1: rbrace_ptr=p;store_new_token(cur_tok);
+}
+
+@ If the parameter consists of a single group enclosed in braces, we must
+strip off the enclosing braces. That's why |rbrace_ptr| was introduced.
+
+@<Tidy up the parameter just scanned, and tuck it away@>=
+{@+if ((m==1)&&(info(p) < right_brace_limit))
+ {@+link(rbrace_ptr)=null;free_avail(p);
+ p=link(temp_head);pstack[n]=link(p);free_avail(p);
+ }
+else pstack[n]=link(temp_head);
+incr(n);
+if (tracing_macros > 0)
+ {@+begin_diagnostic();print_nl("");printn(match_chr);print_int(n);
+ print("<-");show_token_list(pstack[n-1], null, 1000);
+ end_diagnostic(false);
+ }
+}
+
+@ @<Show the text of the macro being expanded@>=
+{@+begin_diagnostic();print_ln();print_cs(warning_index);
+token_show(ref_count);end_diagnostic(false);
+}
+
+@* Basic scanning subroutines.
+Let's turn now to some procedures that \TeX\ calls upon frequently to digest
+certain kinds of patterns in the input. Most of these are quite simple;
+some are quite elaborate. Almost all of the routines call |get_x_token|,
+which can cause them to be invoked recursively.
+@^stomach@>
+@^recursion@>
+
+@ The |scan_left_brace| routine is called when a left brace is supposed to be
+the next non-blank token. (The term ``left brace'' means, more precisely,
+a character whose catcode is |left_brace|.) \TeX\ allows \.{\\relax} to
+appear before the |left_brace|.
+
+@p static void scan_left_brace(void) /*reads a mandatory |left_brace|*/
+{@+@<Get the next non-blank non-relax non-call token@>;
+if (cur_cmd!=left_brace)
+ {@+print_err("Missing { inserted");
+@.Missing \{ inserted@>
+ help4("A left brace was mandatory here, so I've put one in.",@/
+ "You might want to delete and/or insert some corrections",@/
+ "so that I will find a matching right brace soon.",@/
+ "(If you're confused by all this, try typing `I}' now.)");
+ back_error();cur_tok=left_brace_token+'{';cur_cmd=left_brace;
+ cur_chr='{';incr(align_state);
+ }
+}
+
+@ @<Get the next non-blank non-relax non-call token@>=
+@/do@+{get_x_token();
+}@+ while (!((cur_cmd!=spacer)&&(cur_cmd!=relax)))
+
+@ The |scan_optional_equals| routine looks for an optional `\.=' sign preceded
+by optional spaces; `\.{\\relax}' is not ignored here.
+
+@p static void scan_optional_equals(void)
+{@+@<Get the next non-blank non-call token@>;
+if (cur_tok!=other_token+'=') back_input();
+}
+
+@ @<Get the next non-blank non-call token@>=
+@/do@+{get_x_token();
+}@+ while (!(cur_cmd!=spacer))
+
+@ In case you are getting bored, here is a slightly less trivial routine:
+Given a string of lowercase letters, like `\.{pt}' or `\.{plus}' or
+`\.{width}', the |scan_keyword| routine checks to see whether the next
+tokens of input match this string. The match must be exact, except that
+uppercase letters will match their lowercase counterparts; uppercase
+equivalents are determined by subtracting |'a'-'A'|, rather than using the
+|uc_code| table, since \TeX\ uses this routine only for its own limited
+set of keywords.
+
+If a match is found, the characters are effectively removed from the input
+and |true| is returned. Otherwise |false| is returned, and the input
+is left essentially unchanged (except for the fact that some macros
+may have been expanded, etc.).
+@^inner loop@>
+
+@p static bool scan_keyword(char *@!s) /*look for a given string*/
+{@+
+pointer p; /*tail of the backup list*/
+pointer @!q; /*new node being added to the token list via |store_new_token|*/
+p=backup_head;link(p)=null;
+while (*s!=0)
+ {@+get_x_token(); /*recursion is possible here*/
+@^recursion@>
+ if ((cur_cs==0)&&@|
+ ((cur_chr==so(*s))||(cur_chr==so(*s)-'a'+'A')))
+ {@+store_new_token(cur_tok);incr(s);
+ }
+ else if ((cur_cmd!=spacer)||(p!=backup_head))
+ {@+back_input();
+ if (p!=backup_head) back_list(link(backup_head));
+ return false;
+ }
+ }
+flush_list(link(backup_head));return true;
+}
+
+@ Here is a procedure that sounds an alarm when mu and non-mu units
+are being switched.
+
+@p static void mu_error(void)
+{@+print_err("Incompatible glue units");
+@.Incompatible glue units@>
+help1("I'm going to assume that 1mu=1pt when they're mixed.");
+error();
+}
+
+@ The next routine `|scan_something_internal|' is used to fetch internal
+numeric quantities like `\.{\\hsize}', and also to handle the `\.{\\the}'
+when expanding constructions like `\.{\\the\\toks0}' and
+`\.{\\the\\baselineskip}'. Soon we will be considering the |scan_int|
+procedure, which calls |scan_something_internal|; on the other hand,
+|scan_something_internal| also calls |scan_int|, for constructions like
+`\.{\\catcode\`\\\$}' or `\.{\\fontdimen} \.3 \.{\\ff}'. So we
+have to declare |scan_int| as a |forward| procedure. A few other
+procedures are also declared at this point.
+
+@p static void scan_int(void); /*scans an integer value*/
+@t\4\4@>@<Declare procedures that scan restricted classes of integers@>@;
+@t\4\4@>@<Declare \eTeX\ procedures for scanning@>@;
+@t\4\4@>@<Declare procedures that scan font-related stuff@>@;
+
+@ \TeX\ doesn't know exactly what to expect when |scan_something_internal|
+begins. For example, an integer or dimension or glue value could occur
+immediately after `\.{\\hskip}'; and one can even say \.{\\the} with
+respect to token lists in constructions like
+`\.{\\xdef\\o\{\\the\\output\}}'. On the other hand, only integers are
+allowed after a construction like `\.{\\count}'. To handle the various
+possibilities, |scan_something_internal| has a |level| parameter, which
+tells the ``highest'' kind of quantity that |scan_something_internal| is
+allowed to produce. Six levels are distinguished, namely |int_val|,
+|dimen_val|, |glue_val|, |mu_val|, |ident_val|, and |tok_val|.
+
+The output of |scan_something_internal| (and of the other routines
+|scan_int|, |scan_dimen|, and |scan_glue| below) is put into the global
+variable |cur_val|, and its level is put into |cur_val_level|. The highest
+values of |cur_val_level| are special: |mu_val| is used only when
+|cur_val| points to something in a ``muskip'' register, or to one of the
+three parameters \.{\\thinmuskip}, \.{\\medmuskip}, \.{\\thickmuskip};
+|ident_val| is used only when |cur_val| points to a font identifier;
+|tok_val| is used only when |cur_val| points to |null| or to the reference
+count of a token list. The last two cases are allowed only when
+|scan_something_internal| is called with |level==tok_val|.
+
+If the output is glue, |cur_val| will point to a glue specification, and
+the reference count of that glue will have been updated to reflect this
+reference; if the output is a nonempty token list, |cur_val| will point to
+its reference count, but in this case the count will not have been updated.
+Otherwise |cur_val| will contain the integer or scaled value in question.
+
+@d int_val 0 /*integer values*/
+@d dimen_val 1 /*dimension values*/
+@d glue_val 2 /*glue specifications*/
+@d mu_val 3 /*math glue specifications*/
+@d ident_val 4 /*font identifier*/
+@d tok_val 5 /*token lists*/
+
+@<Glob...@>=
+static int @!cur_val; /*value returned by numeric scanners*/
+static int @!cur_val_level; /*the ``level'' of this value*/
+
+@ The hash table is initialized with `\.{\\count}', `\.{\\dimen}', `\.{\\skip}',
+and `\.{\\muskip}' all having |internal_register| as their command code; they are
+distinguished by the |chr_code|, which is either |int_val|, |dimen_val|,
+|glue_val|, or |mu_val| more than |mem_bot| (dynamic variable-size nodes
+cannot have these values)
+
+@<Put each...@>=
+primitive("count", internal_register, mem_bot+int_val);
+@!@:count\_}{\.{\\count} primitive@>
+primitive("dimen", internal_register, mem_bot+dimen_val);
+@!@:dimen\_}{\.{\\dimen} primitive@>
+primitive("skip", internal_register, mem_bot+glue_val);
+@!@:skip\_}{\.{\\skip} primitive@>
+primitive("muskip", internal_register, mem_bot+mu_val);
+@!@:mu\_skip\_}{\.{\\muskip} primitive@>
+
+@ @<Cases of |print_cmd_chr|...@>=
+case internal_register: @<Cases of |register| for |print_cmd_chr|@>@;@+break;
+
+@ OK, we're ready for |scan_something_internal| itself. A second parameter,
+|negative|, is set |true| if the value that is found should be negated.
+It is assumed that |cur_cmd| and |cur_chr| represent the first token of
+the internal quantity to be scanned; an error will be signalled if
+|cur_cmd < min_internal| or |cur_cmd > max_internal|.
+
+@d scanned_result(A, B) @+{@+cur_val=A;cur_val_level=B;@+}
+
+@p static void scan_something_internal(small_number @!level, bool @!negative)
+ /*fetch an internal parameter*/
+{@+
+halfword m; /*|chr_code| part of the operand token*/
+pointer @!q, @!r; /*general purpose indices*/
+pointer @!tx; /*effective tail node*/
+four_quarters @!i; /*character info*/
+int @!p; /*index into |nest|*/
+m=cur_chr;
+switch (cur_cmd) {
+case def_code: @<Fetch a character code from some table@>@;@+break;
+case toks_register: case assign_toks:
+ case def_family: case set_font: case def_font: @<Fetch a token list or font
+identifier, provided that |level=tok_val|@>@;@+break;
+case assign_int: scanned_result(eqtb[m].i, int_val)@;@+break;
+case assign_dimen: scanned_result(eqtb[m].sc, dimen_val)@;@+break;
+case assign_glue: scanned_result(equiv(m), glue_val)@;@+break;
+case assign_mu_glue: scanned_result(equiv(m), mu_val)@;@+break;
+case set_aux: @<Fetch the |space_factor| or the |prev_depth|@>@;@+break;
+case set_prev_graf: @<Fetch the |prev_graf|@>@;@+break;
+case set_page_int: @<Fetch the |dead_cycles| or the |insert_penalties|@>@;@+break;
+case set_page_dimen: @<Fetch something on the |page_so_far|@>@;@+break;
+case set_shape: @<Fetch the |par_shape| size@>@;@+break;
+case set_box_dimen: @<Fetch a box dimension@>@;@+break;
+case char_given: case math_given: scanned_result(cur_chr, int_val)@;@+break;
+case assign_font_dimen: @<Fetch a font dimension@>@;@+break;
+case assign_font_int: @<Fetch a font integer@>@;@+break;
+case internal_register: @<Fetch a register@>@;@+break;
+case last_item: @<Fetch an item in the current node, if appropriate@>@;@+break;
+default:@<Complain that \.{\\the} can't do this; give zero result@>@;
+} @/
+while (cur_val_level > level) @<Convert \(c)|cur_val| to a lower level@>;
+@<Fix the reference count, if any, and negate |cur_val| if |negative|@>;
+}
+
+@ @<Fetch a character code from some table@>=
+{@+scan_char_num();
+if (m==math_code_base) scanned_result(ho(math_code(cur_val)), int_val)@;
+else if (m < math_code_base) scanned_result(equiv(m+cur_val), int_val)@;
+else scanned_result(eqtb[m+cur_val].i, int_val);
+}
+
+@ @<Fetch a token list...@>=
+if (level!=tok_val)
+ {@+print_err("Missing number, treated as zero");
+@.Missing number...@>
+ help3("A number should have been here; I inserted `0'.",@/
+ "(If you can't figure out why I needed to see a number,",@/
+ "look up `weird error' in the index to The TeXbook.)");
+@:TeXbook}{\sl The \TeX book@>
+ back_error();scanned_result(0, dimen_val);
+ }
+else if (cur_cmd <= assign_toks)
+ {@+if (cur_cmd < assign_toks) /*|cur_cmd==toks_register|*/
+ if (m==mem_bot)
+ {@+scan_register_num();
+ if (cur_val < 256) cur_val=equiv(toks_base+cur_val);
+ else{@+find_sa_element(tok_val, cur_val, false);
+ if (cur_ptr==null) cur_val=null;
+ else cur_val=sa_ptr(cur_ptr);
+ }
+ }
+ else cur_val=sa_ptr(m);
+ else cur_val=equiv(m);
+ cur_val_level=tok_val;
+ }
+else{@+back_input();scan_font_ident();
+ scanned_result(font_id_base+cur_val, ident_val);
+ }
+
+@ Users refer to `\.{\\the\\spacefactor}' only in horizontal
+mode, and to `\.{\\the\\prevdepth}' only in vertical mode; so we put the
+associated mode in the modifier part of the |set_aux| command.
+The |set_page_int| command has modifier 0 or 1, for `\.{\\deadcycles}' and
+`\.{\\insertpenalties}', respectively. The |set_box_dimen| command is
+modified by either |width_offset|, |height_offset|, or |depth_offset|.
+And the |last_item| command is modified by either |int_val|, |dimen_val|,
+|glue_val|, |input_line_no_code|, or |badness_code|.
+\eTeX\ inserts |last_node_type_code| after |glue_val| and adds
+the codes for its extensions: |eTeX_version_code|, \dots\ .
+
+@d last_node_type_code (glue_val+1) /*code for \.{\\lastnodetype}*/
+@d input_line_no_code (glue_val+2) /*code for \.{\\inputlineno}*/
+@d badness_code (input_line_no_code+1) /*code for \.{\\badness}*/
+@#
+@d eTeX_int (pdftex_last_item_codes+1) /*first of \eTeX\ codes for integers*/
+@d eTeX_dim (eTeX_int+8) /*first of \eTeX\ codes for dimensions*/
+@d eTeX_glue (eTeX_dim+9) /*first of \eTeX\ codes for glue*/
+@d eTeX_mu (eTeX_glue+1) /*first of \eTeX\ codes for muglue*/
+@d eTeX_expr (eTeX_mu+1) /*first of \eTeX\ codes for expressions*/
+@d eTeX_last_last_item_cmd_mod (eTeX_expr-int_val+mu_val) /*\.{\\muexpr}*/
+
+@<Put each...@>=
+primitive("spacefactor", set_aux, hmode);
+@!@:space\_factor\_}{\.{\\spacefactor} primitive@>
+primitive("prevdepth", set_aux, vmode);@/
+@!@:prev\_depth\_}{\.{\\prevdepth} primitive@>
+primitive("deadcycles", set_page_int, 0);
+@!@:dead\_cycles\_}{\.{\\deadcycles} primitive@>
+primitive("insertpenalties", set_page_int, 1);
+@!@:insert\_penalties\_}{\.{\\insertpenalties} primitive@>
+primitive("wd", set_box_dimen, width_offset);
+@!@:wd\_}{\.{\\wd} primitive@>
+primitive("ht", set_box_dimen, height_offset);
+@!@:ht\_}{\.{\\ht} primitive@>
+primitive("dp", set_box_dimen, depth_offset);
+@!@:dp\_}{\.{\\dp} primitive@>
+primitive("lastpenalty", last_item, int_val);
+@!@:last\_penalty\_}{\.{\\lastpenalty} primitive@>
+primitive("lastkern", last_item, dimen_val);
+@!@:last\_kern\_}{\.{\\lastkern} primitive@>
+primitive("lastskip", last_item, glue_val);
+@!@:last\_skip\_}{\.{\\lastskip} primitive@>
+primitive("inputlineno", last_item, input_line_no_code);
+@!@:input\_line\_no\_}{\.{\\inputlineno} primitive@>
+primitive("badness", last_item, badness_code);
+@!@:badness\_}{\.{\\badness} primitive@>
+
+@ @<Cases of |print_cmd_chr|...@>=
+case set_aux: if (chr_code==vmode) print_esc("prevdepth");
+@+else print_esc("spacefactor");@+break;
+case set_page_int: if (chr_code==0) print_esc("deadcycles")
+@/@<Cases of |set_page_int| for |print_cmd_chr|@>;@/
+@+else print_esc("insertpenalties");@+break;
+case set_box_dimen: if (chr_code==width_offset) print_esc("wd");
+else if (chr_code==height_offset) print_esc("ht");
+else print_esc("dp");@+break;
+case last_item: switch (chr_code) {
+ case int_val: print_esc("lastpenalty");@+break;
+ case dimen_val: print_esc("lastkern");@+break;
+ case glue_val: print_esc("lastskip");@+break;
+ case input_line_no_code: print_esc("inputlineno");@+break;
+ @/@<Cases of |last_item| for |print_cmd_chr|@>@/
+ default:print_esc("badness");
+ } @+break;
+
+@ @<Fetch the |space_factor| or the |prev_depth|@>=
+if (abs(mode)!=m)
+ {@+print_err("Improper ");print_cmd_chr(set_aux, m);
+@.Improper \\spacefactor@>
+@.Improper \\prevdepth@>
+ help4("You can refer to \\spacefactor only in horizontal mode;",@/
+ "you can refer to \\prevdepth only in vertical mode; and",@/
+ "neither of these is meaningful inside \\write. So",@/
+ "I'm forgetting what you said and using zero instead.");
+ error();
+ if (level!=tok_val) scanned_result(0, dimen_val)@;
+ else scanned_result(0, int_val);
+ }
+else if (m==vmode) scanned_result(prev_depth, dimen_val)@;
+else scanned_result(space_factor, int_val)
+
+@ @<Fetch the |dead_cycles| or the |insert_penalties|@>=
+{@+if (m==0) cur_val=dead_cycles
+@/@<Cases for `Fetch the |dead_cycles| or the |insert_penalties|'@>;@/
+else cur_val=insert_penalties;
+cur_val_level=int_val;
+}
+
+@ @<Fetch a box dimension@>=
+{@+scan_register_num();fetch_box(q);
+if (q==null) cur_val=0;@+else cur_val=mem[q+m].sc;
+cur_val_level=dimen_val;
+}
+
+@ Inside an \.{\\output} routine, a user may wish to look at the page totals
+that were present at the moment when output was triggered.
+
+@d max_dimen 07777777777 /*$2^{30}-1$*/
+
+@<Fetch something on the |page_so_far|@>=
+{@+if ((page_contents==empty)&&(!output_active))
+ if (m==0) cur_val=max_dimen;@+else cur_val=0;
+else cur_val=page_so_far[m];
+cur_val_level=dimen_val;
+}
+
+@ @<Fetch the |prev_graf|@>=
+if (mode==0) scanned_result(0, int_val)@; /*|prev_graf==0| within \.{\\write}*/
+else{@+nest[nest_ptr]=cur_list;p=nest_ptr;
+ while (abs(nest[p].mode_field)!=vmode) decr(p);
+ scanned_result(nest[p].pg_field, int_val);
+ }
+
+@ @<Fetch the |par_shape| size@>=
+{@+if (m > par_shape_loc) @<Fetch a penalties array element@>@;
+else if (par_shape_ptr==null) cur_val=0;
+else cur_val=info(par_shape_ptr);
+cur_val_level=int_val;
+}
+
+@ Here is where \.{\\lastpenalty}, \.{\\lastkern}, \.{\\lastskip}, and
+\.{\\lastnodetype} are
+implemented. The reference count for \.{\\lastskip} will be updated later.
+
+We also handle \.{\\inputlineno} and \.{\\badness} here, because they are
+legal in similar contexts.
+
+@<Fetch an item in the current node...@>=
+if (m > eTeX_last_last_item_cmd_mod)
+{@+switch (m) {
+ @/@<Cases for fetching a \Prote\ int value@>@/
+ } /*there are no other cases*/
+ cur_val_level=int_val;
+}
+else if (m >= input_line_no_code)
+{if (m >= eTeX_glue) @<Process an expression and |return|@>@;
+ else if (m >= eTeX_dim)
+ {@+switch (m) {
+ @/@<Cases for fetching a dimension value@>@/
+ } /*there are no other cases*/
+ cur_val_level=dimen_val;
+ }
+ else
+ {@+switch (m) {
+ case input_line_no_code: cur_val=line;@+break;
+ case badness_code: cur_val=last_badness;@+break;
+ @/@<Cases for fetching an integer value@>@/
+ } /*there are no other cases*/
+ cur_val_level=int_val;
+ }
+}
+else{@+if (cur_chr==glue_val) cur_val=zero_glue;@+else cur_val=0;
+ tx=tail;
+ if (cur_chr==last_node_type_code)
+ {@+cur_val_level=int_val;
+ if ((tx==head)||(mode==0)) cur_val=-1;
+ }
+ else cur_val_level=cur_chr;
+ if (!is_char_node(tx)&&(mode!=0))
+ switch (cur_chr) {
+ case int_val: if (type(tx)==penalty_node) cur_val=penalty(tx);@+break;
+ case dimen_val: if (type(tx)==kern_node) cur_val=width(tx);@+break;
+ case glue_val: if (type(tx)==glue_node)
+ {@+cur_val=glue_ptr(tx);
+ if (subtype(tx)==mu_glue) cur_val_level=mu_val;
+ } @+break;
+ case last_node_type_code: if (type(tx) <= unset_node) cur_val=type(tx)+1;
+ else cur_val=unset_node+2;
+ } /*there are no other cases*/
+ else if ((mode==vmode)&&(tx==head))
+ switch (cur_chr) {
+ case int_val: cur_val=last_penalty;@+break;
+ case dimen_val: cur_val=last_kern;@+break;
+ case glue_val: if (last_glue!=max_halfword) cur_val=last_glue;@+break;
+ case last_node_type_code: cur_val=last_node_type;
+ } /*there are no other cases*/
+ }
+
+@ @<Fetch a font dimension@>=
+{@+find_font_dimen(false);font_info[fmem_ptr].sc=0;
+scanned_result(font_info[cur_val].sc, dimen_val);
+}
+
+@ @<Fetch a font integer@>=
+{@+scan_font_ident();
+if (m==0) scanned_result(hyphen_char[cur_val], int_val)@;
+else scanned_result(skew_char[cur_val], int_val);
+}
+
+@ @<Fetch a register@>=
+{@+if ((m < mem_bot)||(m > lo_mem_stat_max))
+ {@+cur_val_level=sa_type(m);
+ if (cur_val_level < glue_val) cur_val=sa_int(m);
+ else cur_val=sa_ptr(m);
+ }
+else{@+scan_register_num();cur_val_level=m-mem_bot;
+ if (cur_val > 255)
+ {@+find_sa_element(cur_val_level, cur_val, false);
+ if (cur_ptr==null)
+ if (cur_val_level < glue_val) cur_val=0;
+ else cur_val=zero_glue;
+ else if (cur_val_level < glue_val) cur_val=sa_int(cur_ptr);
+ else cur_val=sa_ptr(cur_ptr);
+ }
+ else
+ switch (cur_val_level) {
+case int_val: cur_val=count(cur_val);@+break;
+case dimen_val: cur_val=dimen(cur_val);@+break;
+case glue_val: cur_val=skip(cur_val);@+break;
+case mu_val: cur_val=mu_skip(cur_val);
+} /*there are no other cases*/
+ }
+}
+
+@ @<Complain that \.{\\the} can't do this; give zero result@>=
+{@+print_err("You can't use `");print_cmd_chr(cur_cmd, cur_chr);
+@.You can't use x after ...@>
+print("' after ");print_esc("the");
+help1("I'm forgetting what you said and using zero instead.");
+error();
+if (level!=tok_val) scanned_result(0, dimen_val)@;
+else scanned_result(0, int_val);
+}
+
+@ When a |glue_val| changes to a |dimen_val|, we use the width component
+of the glue; there is no need to decrease the reference count, since it
+has not yet been increased. When a |dimen_val| changes to an |int_val|,
+we use scaled points so that the value doesn't actually change. And when a
+|mu_val| changes to a |glue_val|, the value doesn't change either.
+
+@<Convert \(c)|cur_val| to a lower level@>=
+{@+if (cur_val_level==glue_val) cur_val=width(cur_val);
+else if (cur_val_level==mu_val) mu_error();
+decr(cur_val_level);
+}
+
+@ If |cur_val| points to a glue specification at this point, the reference
+count for the glue does not yet include the reference by |cur_val|.
+If |negative| is |true|, |cur_val_level| is known to be | <= mu_val|.
+
+@<Fix the reference count, if any,...@>=
+if (negative)
+ if (cur_val_level >= glue_val)
+ {@+cur_val=new_spec(cur_val);
+ @<Negate all three glue components of |cur_val|@>;
+ }
+ else negate(cur_val);
+else if ((cur_val_level >= glue_val)&&(cur_val_level <= mu_val))
+ add_glue_ref(cur_val)
+
+@ @<Negate all three...@>=
+{@+negate(width(cur_val));
+negate(stretch(cur_val));
+negate(shrink(cur_val));
+}
+
+@ Our next goal is to write the |scan_int| procedure, which scans anything that
+\TeX\ treats as an integer. But first we might as well look at some simple
+applications of |scan_int| that have already been made inside of
+|scan_something_internal|.
+
+@ @<Declare procedures that scan restricted classes of integers@>=
+static void scan_eight_bit_int(void)
+{@+scan_int();
+if ((cur_val < 0)||(cur_val > 255))
+ {@+print_err("Bad register code");
+@.Bad register code@>
+ help2("A register number must be between 0 and 255.",@/
+ "I changed this one to zero.");int_error(cur_val);cur_val=0;
+ }
+}
+
+@ @<Declare procedures that scan restricted classes of integers@>=
+static void scan_char_num(void)
+{@+scan_int();
+if ((cur_val < 0)||(cur_val > 255))
+ {@+print_err("Bad character code");
+@.Bad character code@>
+ help2("A character number must be between 0 and 255.",@/
+ "I changed this one to zero.");int_error(cur_val);cur_val=0;
+ }
+}
+
+@ While we're at it, we might as well deal with similar routines that
+will be needed later.
+
+@<Declare procedures that scan restricted classes of integers@>=
+static void scan_four_bit_int(void)
+{@+scan_int();
+if ((cur_val < 0)||(cur_val > 15))
+ {@+print_err("Bad number");
+@.Bad number@>
+ help2("Since I expected to read a number between 0 and 15,",@/
+ "I changed this one to zero.");int_error(cur_val);cur_val=0;
+ }
+}
+
+@ @<Declare procedures that scan restricted classes of integers@>=
+static void scan_fifteen_bit_int(void)
+{@+scan_int();
+if ((cur_val < 0)||(cur_val > 077777))
+ {@+print_err("Bad mathchar");
+@.Bad mathchar@>
+ help2("A mathchar number must be between 0 and 32767.",@/
+ "I changed this one to zero.");int_error(cur_val);cur_val=0;
+ }
+}
+
+@ @<Declare procedures that scan restricted classes of integers@>=
+static void scan_twenty_seven_bit_int(void)
+{@+scan_int();
+if ((cur_val < 0)||(cur_val > 0777777777))
+ {@+print_err("Bad delimiter code");
+@.Bad delimiter code@>
+ help2("A numeric delimiter code must be between 0 and 2^{27}-1.",@/
+ "I changed this one to zero.");int_error(cur_val);cur_val=0;
+ }
+}
+
+@ An integer number can be preceded by any number of spaces and `\.+' or
+`\.-' signs. Then comes either a decimal constant (i.e., radix 10), an
+octal constant (i.e., radix 8, preceded by~\.\'), a hexadecimal constant
+(radix 16, preceded by~\."), an alphabetic constant (preceded by~\.\`), or
+an internal variable. After scanning is complete,
+|cur_val| will contain the answer, which must be at most
+$2^{31}-1=2147483647$ in absolute value. The value of |radix| is set to
+10, 8, or 16 in the cases of decimal, octal, or hexadecimal constants,
+otherwise |radix| is set to zero. An optional space follows a constant.
+
+@d octal_token (other_token+'\'') /*apostrophe, indicates an octal constant*/
+@d hex_token (other_token+'"') /*double quote, indicates a hex constant*/
+@d alpha_token (other_token+'`') /*reverse apostrophe, precedes alpha constants*/
+@d point_token (other_token+'.') /*decimal point*/
+@d continental_point_token (other_token+',') /*decimal point, Eurostyle*/
+
+@<Glob...@>=
+static small_number @!radix; /*|scan_int| sets this to 8, 10, 16, or zero*/
+
+@ We initialize the following global variables just in case |expand|
+comes into action before any of the basic scanning routines has assigned
+them a value.
+
+@<Set init...@>=
+cur_val=0;cur_val_level=int_val;radix=0;cur_order=normal;
+
+@ The |scan_int| routine is used also to scan the integer part of a
+fraction; for example, the `\.3' in `\.{3.14159}' will be found by
+|scan_int|. The |scan_dimen| routine assumes that |cur_tok==point_token|
+after the integer part of such a fraction has been scanned by |scan_int|,
+and that the decimal point has been backed up to be scanned again.
+
+@p static void scan_int(void) /*sets |cur_val| to an integer*/
+{@+
+bool negative; /*should the answer be negated?*/
+int @!m; /*|@t$2^{31}$@>/radix|, the threshold of danger*/
+small_number @!d; /*the digit just scanned*/
+bool @!vacuous; /*have no digits appeared?*/
+bool @!OK_so_far; /*has an error message been issued?*/
+radix=0;OK_so_far=true;@/
+@<Get the next non-blank non-sign token; set |negative| appropriately@>;
+if (cur_tok==alpha_token) @<Scan an alphabetic character code into |cur_val|@>@;
+else if ((cur_cmd >= min_internal)&&(cur_cmd <= max_internal))
+ scan_something_internal(int_val, false);
+else@<Scan a numeric constant@>;
+if (negative) negate(cur_val);
+}
+
+@ @<Get the next non-blank non-sign token...@>=
+negative=false;
+@/do@+{@<Get the next non-blank non-call token@>;
+if (cur_tok==other_token+'-')
+ {@+negative=!negative;cur_tok=other_token+'+';
+ }
+}@+ while (!(cur_tok!=other_token+'+'))
+
+@ A space is ignored after an alphabetic character constant, so that
+such constants behave like numeric ones.
+
+@<Scan an alphabetic character code into |cur_val|@>=
+{@+get_token(); /*suppress macro expansion*/
+if (cur_tok < cs_token_flag)
+ {@+cur_val=cur_chr;
+ if (cur_cmd <= right_brace)
+ if (cur_cmd==right_brace) incr(align_state);
+ else decr(align_state);
+ }
+else if (cur_tok < cs_token_flag+single_base)
+ cur_val=cur_tok-cs_token_flag-active_base;
+else cur_val=cur_tok-cs_token_flag-single_base;
+if (cur_val > 255)
+ {@+print_err("Improper alphabetic constant");
+@.Improper alphabetic constant@>
+ help2("A one-character control sequence belongs after a ` mark.",@/
+ "So I'm essentially inserting \\0 here.");
+ cur_val='0';back_error();
+ }
+else@<Scan an optional space@>;
+}
+
+@ @<Scan an optional space@>=
+{@+get_x_token();if (cur_cmd!=spacer) back_input();
+}
+
+@ @<Scan a numeric constant@>=
+{@+radix=10;m=214748364;
+if (cur_tok==octal_token)
+ {@+radix=8;m=02000000000;get_x_token();
+ }
+else if (cur_tok==hex_token)
+ {@+radix=16;m=01000000000;get_x_token();
+ }
+vacuous=true;cur_val=0;@/
+@<Accumulate the constant until |cur_tok| is not a suitable digit@>;
+if (vacuous) @<Express astonishment that no number was here@>@;
+else if (cur_cmd!=spacer) back_input();
+}
+
+@ @d infinity 017777777777 /*the largest positive value that \TeX\ knows*/
+@d zero_token (other_token+'0') /*zero, the smallest digit*/
+@d A_token (letter_token+'A') /*the smallest special hex digit*/
+@d other_A_token (other_token+'A') /*special hex digit of type |other_char|*/
+
+@<Accumulate the constant...@>=
+loop@+{@+if ((cur_tok < zero_token+radix)&&(cur_tok >= zero_token)&&
+ (cur_tok <= zero_token+9)) d=cur_tok-zero_token;
+ else if (radix==16)
+ if ((cur_tok <= A_token+5)&&(cur_tok >= A_token)) d=cur_tok-A_token+10;
+ else if ((cur_tok <= other_A_token+5)&&(cur_tok >= other_A_token))
+ d=cur_tok-other_A_token+10;
+ else goto done;
+ else goto done;
+ vacuous=false;
+ if ((cur_val >= m)&&((cur_val > m)||(d > 7)||(radix!=10)))
+ {@+if (OK_so_far)
+ {@+print_err("Number too big");
+@.Number too big@>
+ help2("I can only go up to 2147483647='17777777777=\"7FFFFFFF,",@/
+ "so I'm using that number instead of yours.");
+ error();cur_val=infinity;OK_so_far=false;
+ }
+ }
+ else cur_val=cur_val*radix+d;
+ get_x_token();
+ }
+done:
+
+@ @<Express astonishment...@>=
+{@+print_err("Missing number, treated as zero");
+@.Missing number...@>
+help3("A number should have been here; I inserted `0'.",@/
+ "(If you can't figure out why I needed to see a number,",@/
+ "look up `weird error' in the index to The TeXbook.)");
+@:TeXbook}{\sl The \TeX book@>
+back_error();
+}
+
+@ The |scan_dimen| routine is similar to |scan_int|, but it sets |cur_val| to
+a |scaled| value, i.e., an integral number of sp. One of its main tasks
+is therefore to interpret the abbreviations for various kinds of units and
+to convert measurements to scaled points.
+
+There are three parameters: |mu| is |true| if the finite units must be
+`\.{mu}', while |mu| is |false| if `\.{mu}' units are disallowed;
+|inf| is |true| if the infinite units `\.{fil}', `\.{fill}', `\.{filll}'
+are permitted; and |shortcut| is |true| if |cur_val| already contains
+an integer and only the units need to be considered.
+
+The order of infinity that was found in the case of infinite glue is returned
+in the global variable |cur_order|.
+
+@<Glob...@>=
+static glue_ord @!cur_order; /*order of infinity found by |scan_dimen|*/
+
+@ Constructions like `\.{-\'77 pt}' are legal dimensions, so |scan_dimen|
+may begin with |scan_int|. This explains why it is convenient to use
+|scan_int| also for the integer part of a decimal fraction.
+
+Several branches of |scan_dimen| work with |cur_val| as an integer and
+with an auxiliary fraction |f|, so that the actual quantity of interest is
+$|cur_val|+|f|/2^{16}$. At the end of the routine, this ``unpacked''
+representation is put into the single word |cur_val|, which suddenly
+switches significance from |int| to |scaled|.
+
+@d scan_normal_dimen scan_dimen(false, false, false)
+
+@p static void scan_dimen(bool @!mu, bool @!inf, bool @!shortcut)
+ /*sets |cur_val| to a dimension*/
+{@+
+bool negative; /*should the answer be negated?*/
+int @!f; /*numerator of a fraction whose denominator is $2^{16}$*/
+@<Local variables for dimension calculations@>@;
+f=0;arith_error=false;cur_order=normal;negative=false;
+if (!shortcut)
+ {@+@<Get the next non-blank non-sign...@>;
+ if ((cur_cmd >= min_internal)&&(cur_cmd <= max_internal))
+ @<Fetch an internal dimension and |goto attach_sign|, or fetch an internal
+integer@>@;
+ else{@+back_input();
+ if (cur_tok==continental_point_token) cur_tok=point_token;
+ if (cur_tok!=point_token) scan_int();
+ else{@+radix=10;cur_val=0;
+ }
+ if (cur_tok==continental_point_token) cur_tok=point_token;
+ if ((radix==10)&&(cur_tok==point_token)) @<Scan decimal fraction@>;
+ }
+ }
+if (cur_val < 0) /*in this case |f==0|*/
+ {@+negative=!negative;negate(cur_val);
+ }
+@<Scan units and set |cur_val| to $x\cdot(|cur_val|+f/2^{16})$, where there
+are |x| sp per unit; |goto attach_sign| if the units are internal@>;
+@<Scan an optional space@>;
+attach_sign: if (arith_error||(abs(cur_val) >= 010000000000))
+ @<Report that this dimension is out of range@>;
+if (negative) negate(cur_val);
+}
+
+@ @<Fetch an internal dimension and |goto attach_sign|...@>=
+if (mu)
+ {@+scan_something_internal(mu_val, false);
+ @<Coerce glue to a dimension@>;
+ if (cur_val_level==mu_val) goto attach_sign;
+ if (cur_val_level!=int_val) mu_error();
+ }
+else{@+scan_something_internal(dimen_val, false);
+ if (cur_val_level==dimen_val) goto attach_sign;
+ }
+
+@ @<Local variables for dimension calculations@>=
+int @!num, @!denom; /*conversion ratio for the scanned units*/
+int @!k, @!kk; /*number of digits in a decimal fraction*/
+pointer @!p, @!q; /*top of decimal digit stack*/
+scaled @!v; /*an internal dimension*/
+int @!save_cur_val; /*temporary storage of |cur_val|*/
+
+@ The following code is executed when |scan_something_internal| was
+called asking for |mu_val|, when we really wanted a ``mudimen'' instead
+of ``muglue.''
+
+@<Coerce glue to a dimension@>=
+if (cur_val_level >= glue_val)
+ {@+v=width(cur_val);delete_glue_ref(cur_val);cur_val=v;
+ }
+
+@ When the following code is executed, we have |cur_tok==point_token|, but this
+token has been backed up using |back_input|; we must first discard it.
+
+It turns out that a decimal point all by itself is equivalent to `\.{0.0}'.
+Let's hope people don't use that fact.
+
+@<Scan decimal fraction@>=
+{@+k=0;p=null;get_token(); /*|point_token| is being re-scanned*/
+loop@+{@+get_x_token();
+ if ((cur_tok > zero_token+9)||(cur_tok < zero_token)) goto done1;
+ if (k < 17) /*digits for |k >= 17| cannot affect the result*/
+ {@+q=get_avail();link(q)=p;info(q)=cur_tok-zero_token;
+ p=q;incr(k);
+ }
+ }
+done1: for (kk=k; kk>=1; kk--)
+ {@+dig[kk-1]=info(p);q=p;p=link(p);free_avail(q);
+ }
+f=round_decimals(k);
+if (cur_cmd!=spacer) back_input();
+}
+
+@ Now comes the harder part: At this point in the program, |cur_val| is a
+nonnegative integer and $f/2^{16}$ is a nonnegative fraction less than 1;
+we want to multiply the sum of these two quantities by the appropriate
+factor, based on the specified units, in order to produce a |scaled|
+result, and we want to do the calculation with fixed point arithmetic that
+does not overflow.
+
+@<Scan units and set |cur_val| to $x\cdot(|cur_val|+f/2^{16})$...@>=
+if (inf) @<Scan for \(f)\.{fil} units; |goto attach_fraction| if found@>;
+@<Scan for \(u)units that are internal dimensions; |goto attach_sign| with
+|cur_val| set if found@>;
+if (mu) @<Scan for \(m)\.{mu} units and |goto attach_fraction|@>;
+if (scan_keyword("true")) @<Adjust \(f)for the magnification ratio@>;
+@.true@>
+if (scan_keyword("pt")) goto attach_fraction; /*the easy case*/
+@.pt@>
+@<Scan for \(a)all other units and adjust |cur_val| and |f| accordingly; |goto
+done| in the case of scaled points@>;
+attach_fraction: if (cur_val >= 040000) arith_error=true;
+else cur_val=cur_val*unity+f;
+done:
+
+@ A specification like `\.{filllll}' or `\.{fill L L L}' will lead to two
+error messages (one for each additional keyword \.{"l"}).
+
+@<Scan for \(f)\.{fil} units...@>=
+if (scan_keyword("fil"))
+@.fil@>
+ {@+cur_order=fil;
+ while (scan_keyword("l"))
+ {@+if (cur_order==filll)
+ {@+print_err("Illegal unit of measure (");
+@.Illegal unit of measure@>
+ print("replaced by filll)");
+ help1("I dddon't go any higher than filll.");error();
+ }
+ else incr(cur_order);
+ }
+ goto attach_fraction;
+ }
+
+@ @<Scan for \(u)units that are internal dimensions...@>=
+save_cur_val=cur_val;
+@<Get the next non-blank non-call...@>;
+if ((cur_cmd < min_internal)||(cur_cmd > max_internal)) back_input();
+else{@+if (mu)
+ {@+scan_something_internal(mu_val, false);@<Coerce glue...@>;
+ if (cur_val_level!=mu_val) mu_error();
+ }
+ else scan_something_internal(dimen_val, false);
+ v=cur_val;goto found;
+ }
+if (mu) goto not_found;
+if (scan_keyword("em")) v=(@<The em width for |cur_font|@>);
+@.em@>
+else if (scan_keyword("ex")) v=(@<The x-height for |cur_font|@>);
+@.ex@>
+else goto not_found;
+@<Scan an optional space@>;
+found: cur_val=nx_plus_y(save_cur_val, v, xn_over_d(v, f, 0200000));
+goto attach_sign;
+not_found:
+
+@ @<Scan for \(m)\.{mu} units and |goto attach_fraction|@>=
+if (scan_keyword("mu")) goto attach_fraction;
+@.mu@>
+else{@+print_err("Illegal unit of measure (");print("mu inserted)");
+@.Illegal unit of measure@>
+ help4("The unit of measurement in math glue must be mu.",@/
+ "To recover gracefully from this error, it's best to",@/
+ "delete the erroneous units; e.g., type `2' to delete",@/
+ "two letters. (See Chapter 27 of The TeXbook.)");
+@:TeXbook}{\sl The \TeX book@>
+ error();goto attach_fraction;
+ }
+
+@ @<Adjust \(f)for the magnification ratio@>=
+{@+prepare_mag();
+if (mag!=1000)
+ {@+cur_val=xn_over_d(cur_val, 1000, mag);
+ f=(1000*f+0200000*rem)/mag;
+ cur_val=cur_val+(f/0200000);f=f%0200000;
+ }
+}
+
+@ The necessary conversion factors can all be specified exactly as
+fractions whose numerator and denominator sum to 32768 or less.
+According to the definitions here, $\rm2660\,dd\approx1000.33297\,mm$;
+this agrees well with the value $\rm1000.333\,mm$ cited by Bosshard
+@^Bosshard, Hans Rudolf@>
+in {\sl Technische Grundlagen zur Satzherstellung\/} (Bern, 1980).
+
+@d set_conversion(A, B) @+{@+num=A;denom=B;}
+
+@<Scan for \(a)all other units and adjust |cur_val| and |f|...@>=
+if (scan_keyword("in")) set_conversion(7227, 100)@;
+@.in@>
+else if (scan_keyword("pc")) set_conversion(12, 1)@;
+@.pc@>
+else if (scan_keyword("cm")) set_conversion(7227, 254)@;
+@.cm@>
+else if (scan_keyword("mm")) set_conversion(7227, 2540)@;
+@.mm@>
+else if (scan_keyword("bp")) set_conversion(7227, 7200)@;
+@.bp@>
+else if (scan_keyword("dd")) set_conversion(1238, 1157)@;
+@.dd@>
+else if (scan_keyword("cc")) set_conversion(14856, 1157)@;
+@.cc@>
+else if (scan_keyword("sp")) goto done;
+@.sp@>
+else@<Complain about unknown unit and |goto done2|@>;
+cur_val=xn_over_d(cur_val, num, denom);
+f=(num*f+0200000*rem)/denom;@/
+cur_val=cur_val+(f/0200000);f=f%0200000;
+done2:
+
+@ @<Complain about unknown unit...@>=
+{@+print_err("Illegal unit of measure (");print("pt inserted)");
+@.Illegal unit of measure@>
+help6("Dimensions can be in units of em, ex, in, pt, pc,",@/
+ "cm, mm, dd, cc, bp, or sp; but yours is a new one!",@/
+ "I'll assume that you meant to say pt, for printer's points.",@/
+ "To recover gracefully from this error, it's best to",@/
+ "delete the erroneous units; e.g., type `2' to delete",@/
+ "two letters. (See Chapter 27 of The TeXbook.)");
+@:TeXbook}{\sl The \TeX book@>
+error();goto done2;
+}
+
+
+@ @<Report that this dimension is out of range@>=
+{@+print_err("Dimension too large");
+@.Dimension too large@>
+help2("I can't work with sizes bigger than about 19 feet.",@/
+ "Continue and I'll use the largest value I can.");@/
+error();cur_val=max_dimen;arith_error=false;
+}
+
+@ The final member of \TeX's value-scanning trio is |scan_glue|, which
+makes |cur_val| point to a glue specification. The reference count of that
+glue spec will take account of the fact that |cur_val| is pointing to~it.
+
+The |level| parameter should be either |glue_val| or |mu_val|.
+
+Since |scan_dimen| was so much more complex than |scan_int|, we might expect
+|scan_glue| to be even worse. But fortunately, it is very simple, since
+most of the work has already been done.
+
+@p static void scan_glue(small_number @!level)
+ /*sets |cur_val| to a glue spec pointer*/
+{@+
+bool negative; /*should the answer be negated?*/
+pointer @!q; /*new glue specification*/
+bool @!mu; /*does |level==mu_val|?*/
+mu=(level==mu_val);@<Get the next non-blank non-sign...@>;
+if ((cur_cmd >= min_internal)&&(cur_cmd <= max_internal))
+ {@+scan_something_internal(level, negative);
+ if (cur_val_level >= glue_val)
+ {@+if (cur_val_level!=level) mu_error();
+ return;
+ }
+ if (cur_val_level==int_val) scan_dimen(mu, false, true);
+ else if (level==mu_val) mu_error();
+ }
+else{@+back_input();scan_dimen(mu, false, false);
+ if (negative) negate(cur_val);
+ }
+@<Create a new glue specification whose width is |cur_val|; scan for its stretch
+and shrink components@>;
+}
+@#
+@<Declare procedures needed for expressions@>@;
+
+@ @<Create a new glue specification whose width is |cur_val|...@>=
+q=new_spec(zero_glue);width(q)=cur_val;
+if (scan_keyword("plus"))
+@.plus@>
+ {@+scan_dimen(mu, true, false);
+ stretch(q)=cur_val;stretch_order(q)=cur_order;
+ }
+if (scan_keyword("minus"))
+@.minus@>
+ {@+scan_dimen(mu, true, false);
+ shrink(q)=cur_val;shrink_order(q)=cur_order;
+ }
+cur_val=q
+
+@ Here's a similar procedure that returns a pointer to a rule node. This
+routine is called just after \TeX\ has seen \.{\\hrule} or \.{\\vrule};
+therefore |cur_cmd| will be either |hrule| or |vrule|. The idea is to store
+the default rule dimensions in the node, then to override them if
+`\.{height}' or `\.{width}' or `\.{depth}' specifications are
+found (in any order).
+
+@d default_rule 26214 /*0.4\thinspace pt*/
+
+@p static pointer scan_rule_spec(void)
+{@+
+pointer q; /*the rule node being created*/
+q=new_rule(); /*|width|, |depth|, and |height| all equal |null_flag| now*/
+if (cur_cmd==vrule) width(q)=default_rule;
+else{@+height(q)=default_rule;depth(q)=0;
+ }
+reswitch: if (scan_keyword("width"))
+@.width@>
+ {@+scan_normal_dimen;width(q)=cur_val;goto reswitch;
+ }
+if (scan_keyword("height"))
+@.height@>
+ {@+scan_normal_dimen;height(q)=cur_val;goto reswitch;
+ }
+if (scan_keyword("depth"))
+@.depth@>
+ {@+scan_normal_dimen;depth(q)=cur_val;goto reswitch;
+ }
+return q;
+}
+
+@* Building token lists.
+The token lists for macros and for other things like \.{\\mark} and \.{\\output}
+and \.{\\write} are produced by a procedure called |scan_toks|.
+
+Before we get into the details of |scan_toks|, let's consider a much
+simpler task, that of converting the current string into a token list.
+The |str_toks| function does this; it classifies spaces as type |spacer|
+and everything else as type |other_char|.
+
+The token list created by |str_toks| begins at |link(temp_head)| and ends
+at the value |p| that is returned. (If |p==temp_head|, the list is empty.)
+
+@p @t\4@>@<Declare \eTeX\ procedures for token lists@>@;@/
+static pointer str_toks(pool_pointer @!b)
+ /*converts |str_pool[b dotdot pool_ptr-1]| to a token list*/
+{@+pointer p; /*tail of the token list*/
+pointer @!q; /*new node being added to the token list via |store_new_token|*/
+halfword @!t; /*token being appended*/
+pool_pointer @!k; /*index into |str_pool|*/
+str_room(1);
+p=temp_head;link(p)=null;k=b;
+while (k < pool_ptr)
+ {@+t=so(str_pool[k]);
+ if (t==' ') t=space_token;
+ else t=other_token+t;
+ fast_store_new_token(t);
+ incr(k);
+ }
+pool_ptr=b;return p;
+}
+
+@ The main reason for wanting |str_toks| is the next function,
+|the_toks|, which has similar input/output characteristics.
+
+This procedure is supposed to scan something like `\.{\\skip\\count12}',
+i.e., whatever can follow `\.{\\the}', and it constructs a token list
+containing something like `\.{-3.0pt minus 0.5fill}'.
+
+@p static pointer the_toks(void)
+{@+
+int old_setting; /*holds |selector| setting*/
+pointer @!p, @!q, @!r; /*used for copying a token list*/
+pool_pointer @!b; /*base of temporary string*/
+small_number @!c; /*value of |cur_chr|*/
+@<Handle \.{\\unexpanded} or \.{\\detokenize} and |return|@>;@/
+get_x_token();scan_something_internal(tok_val, false);
+if (cur_val_level >= ident_val) @<Copy the token list@>@;
+else{@+old_setting=selector;selector=new_string;b=pool_ptr;
+ switch (cur_val_level) {
+ case int_val: print_int(cur_val);@+break;
+ case dimen_val: {@+print_scaled(cur_val);print("pt");
+ } @+break;
+ case glue_val: {@+print_spec(cur_val,"pt");delete_glue_ref(cur_val);
+ } @+break;
+ case mu_val: {@+print_spec(cur_val,"mu");delete_glue_ref(cur_val);
+ }
+ } /*there are no other cases*/
+ selector=old_setting;return str_toks(b);
+ }
+}
+
+@ @<Copy the token list@>=
+{@+p=temp_head;link(p)=null;
+if (cur_val_level==ident_val) store_new_token(cs_token_flag+cur_val)@;
+else if (cur_val!=null)
+ {@+r=link(cur_val); /*do not copy the reference count*/
+ while (r!=null)
+ {@+fast_store_new_token(info(r));r=link(r);
+ }
+ }
+return p;
+}
+
+@ Here's part of the |expand| subroutine that we are now ready to complete:
+
+@p static void ins_the_toks(void)
+{@+link(garbage)=the_toks();ins_list(link(temp_head));
+}
+
+@ The primitives \.{\\number}, \.{\\romannumeral}, \.{\\string}, \.{\\meaning},
+\.{\\fontname}, and \.{\\jobname} are defined as follows.
+
+@d number_code 0 /*command code for \.{\\number}*/
+@d roman_numeral_code 1 /*command code for \.{\\romannumeral}*/
+@d string_code 2 /*command code for \.{\\string}*/
+@d meaning_code 3 /*command code for \.{\\meaning}*/
+@d font_name_code 4 /*command code for \.{\\fontname}*/
+@d etex_convert_base 5 /*base for \eTeX's command codes*/
+@d eTeX_revision_code etex_convert_base /*command code for \.{\\eTeXrevision}*/
+@d etex_convert_codes (etex_convert_base+1) /*end of \eTeX's command codes*/
+@d eTeX_last_convert_cmd_mod etex_convert_codes /* prote codes follow */
+
+
+@#
+@d job_name_code pdftex_convert_codes /*command code for \.{\\jobname}*/
+
+@<Put each...@>=
+primitive("number", convert, number_code);@/
+@!@:number\_}{\.{\\number} primitive@>
+primitive("romannumeral", convert, roman_numeral_code);@/
+@!@:roman\_numeral\_}{\.{\\romannumeral} primitive@>
+primitive("string", convert, string_code);@/
+@!@:string\_}{\.{\\string} primitive@>
+primitive("meaning", convert, meaning_code);@/
+@!@:meaning\_}{\.{\\meaning} primitive@>
+primitive("fontname", convert, font_name_code);@/
+@!@:font\_name\_}{\.{\\fontname} primitive@>
+primitive("jobname", convert, job_name_code);@/
+@!@:job\_name\_}{\.{\\jobname} primitive@>
+
+@ @<Cases of |print_cmd_chr|...@>=
+case convert: switch (chr_code) {
+ case number_code: print_esc("number");@+break;
+ case roman_numeral_code: print_esc("romannumeral");@+break;
+ case string_code: print_esc("string");@+break;
+ case meaning_code: print_esc("meaning");@+break;
+ case font_name_code: print_esc("fontname");@+break;
+ case job_name_code: print_esc("jobname");@+break;
+ case eTeX_revision_code: print_esc("eTeXrevision");@+break;
+ @/@<Cases of |convert| for |print_cmd_chr|@>@/
+ } @+break;
+
+@ The procedure |conv_toks| uses |str_toks| to insert the token list
+for |convert| functions into the scanner; `\.{\\outer}' control sequences
+are allowed to follow `\.{\\string}' and `\.{\\meaning}'.
+
+@p static void scan_pdf_ext_toks(void);
+static void conv_toks(void)
+{@+int old_setting; /*holds |selector| setting*/
+int @!c; /*desired type of conversion*/
+small_number @!save_scanner_status; /*|scanner_status| upon entry*/
+pool_pointer @!b; /*base of temporary string*/
+int @!i, @!k, @!l; /*general purpose index*/
+pool_pointer @!m, @!n; /*general purpose pool pointer*/
+bool @!r; /*general purpose refraction i.e. changing the way*/
+str_number @!s, @!t; /*general purpose; de dicto*/
+c=cur_chr;@<Scan the argument for command |c|@>;
+old_setting=selector;selector=new_string;b=pool_ptr;
+@<Print the result of command |c|@>;
+selector=old_setting;link(garbage)=str_toks(b);ins_list(link(temp_head));
+}
+
+@ @<Scan the argument for command |c|@>=
+switch (c) {
+case number_code: case roman_numeral_code: scan_int();@+break;
+case string_code: case meaning_code: {@+save_scanner_status=scanner_status;
+ scanner_status=normal;get_token();scanner_status=save_scanner_status;
+ } @+break;
+case font_name_code: scan_font_ident();@+break;
+case job_name_code: if (job_name==0) open_log_file();@+break;
+case eTeX_revision_code: do_nothing;@+break;
+@/@<Cases of `Scan the argument for command |c|'@>@/
+} /*there are no other cases*/
+
+@ @<Print the result of command |c|@>=
+switch (c) {
+case number_code: print_int(cur_val);@+break;
+case roman_numeral_code: print_roman_int(cur_val);@+break;
+case string_code: if (cur_cs!=0) sprint_cs(cur_cs);
+ else print_char(cur_chr);@+break;
+case meaning_code: print_meaning();@+break;
+case font_name_code: {@+printn(font_name[cur_val]);
+ if (font_size[cur_val]!=font_dsize[cur_val])
+ {@+print(" at ");print_scaled(font_size[cur_val]);
+ print("pt");
+ }
+ } @+break;
+case eTeX_revision_code: print(eTeX_revision);@+break;
+case job_name_code: printn(job_name);@+break;
+@/@<Cases of `Print the result of command |c|'@>@/
+} /*there are no other cases*/
+
+@ Now we can't postpone the difficulties any longer; we must bravely tackle
+|scan_toks|. This function returns a pointer to the tail of a new token
+list, and it also makes |def_ref| point to the reference count at the
+head of that list.
+
+There are two boolean parameters, |macro_def| and |xpand|. If |macro_def|
+is true, the goal is to create the token list for a macro definition;
+otherwise the goal is to create the token list for some other \TeX\
+primitive: \.{\\mark}, \.{\\output}, \.{\\everypar}, \.{\\lowercase},
+\.{\\uppercase}, \.{\\message}, \.{\\errmessage}, \.{\\write}, or
+\.{\\special}. In the latter cases a left brace must be scanned next; this
+left brace will not be part of the token list, nor will the matching right
+brace that comes at the end. If |xpand| is false, the token list will
+simply be copied from the input using |get_token|. Otherwise all expandable
+tokens will be expanded until unexpandable tokens are left, except that
+the results of expanding `\.{\\the}' are not expanded further.
+If both |macro_def| and |xpand| are true, the expansion applies
+only to the macro body (i.e., to the material following the first
+|left_brace| character).
+
+The value of |cur_cs| when |scan_toks| begins should be the |eqtb|
+address of the control sequence to display in ``runaway'' error
+messages.
+
+@p static pointer scan_toks(bool @!macro_def, bool @!xpand)
+{@+
+halfword t; /*token representing the highest parameter number*/
+halfword @!s; /*saved token*/
+pointer @!p; /*tail of the token list being built*/
+pointer @!q; /*new node being added to the token list via |store_new_token|*/
+halfword @!unbalance; /*number of unmatched left braces*/
+halfword @!hash_brace; /*possible `\.{\#\{}' token*/
+if (macro_def) scanner_status=defining;
+@+else scanner_status=absorbing;
+warning_index=cur_cs;def_ref=get_avail();token_ref_count(def_ref)=null;
+p=def_ref;hash_brace=0;t=zero_token;
+if (macro_def) @<Scan and build the parameter part of the macro definition@>@;
+else scan_left_brace(); /*remove the compulsory left brace*/
+@<Scan and build the body of the token list; |goto found| when finished@>;
+found: scanner_status=normal;
+if (hash_brace!=0) store_new_token(hash_brace);
+return p;
+}
+@t\4@>@<Declare \Prote\ procedures for token lists@>@;@/
+
+@ @<Scan and build the parameter part...@>=
+{@+loop{@+resume: get_token(); /*set |cur_cmd|, |cur_chr|, |cur_tok|*/
+ if (cur_tok < right_brace_limit) goto done1;
+ if (cur_cmd==mac_param)
+ @<If the next character is a parameter number, make |cur_tok| a |match|
+token; but if it is a left brace, store `|left_brace|, |end_match|', set |hash_brace|,
+and |goto done|@>;
+ store_new_token(cur_tok);
+ }
+done1: store_new_token(end_match_token);
+if (cur_cmd==right_brace)
+ @<Express shock at the missing left brace; |goto found|@>;
+done: ;}
+
+@ @<Express shock...@>=
+{@+print_err("Missing { inserted");incr(align_state);
+@.Missing \{ inserted@>
+help2("Where was the left brace? You said something like `\\def\\a}',",@/
+ "which I'm going to interpret as `\\def\\a{}'.");error();goto found;
+}
+
+@ @<If the next character is a parameter number...@>=
+{@+s=match_token+cur_chr;get_token();
+if (cur_tok < left_brace_limit)
+ {@+hash_brace=cur_tok;
+ store_new_token(cur_tok);store_new_token(end_match_token);
+ goto done;
+ }
+if (t==zero_token+9)
+ {@+print_err("You already have nine parameters");
+@.You already have nine...@>
+ help2("I'm going to ignore the # sign you just used,",@/
+ "as well as the token that followed it.");error();goto resume;
+ }
+else{@+incr(t);
+ if (cur_tok!=t)
+ {@+print_err("Parameters must be numbered consecutively");
+@.Parameters...consecutively@>
+ help2("I've inserted the digit you should have used after the #.",@/
+ "Type `1' to delete what you did use.");back_error();
+ }
+ cur_tok=s;
+ }
+}
+
+@ @<Scan and build the body of the token list; |goto found| when finished@>=
+unbalance=1;
+loop@+{@+if (xpand) @<Expand the next part of the input@>@;
+ else get_token();
+ if (cur_tok < right_brace_limit)
+ if (cur_cmd < right_brace) incr(unbalance);
+ else{@+decr(unbalance);
+ if (unbalance==0) goto found;
+ }
+ else if (cur_cmd==mac_param)
+ if (macro_def) @<Look for parameter number or \.{\#\#}@>;
+ store_new_token(cur_tok);
+ }
+
+@ Here we insert an entire token list created by |the_toks| without
+expanding it further.
+
+@<Expand the next part of the input@>=
+{@+loop{@+get_next();
+ if (cur_cmd >= call)
+ if (info(link(cur_chr))==protected_token)
+ {@+cur_cmd=relax;cur_chr=no_expand_flag;
+ }
+ if (cur_cmd <= max_command) goto done2;
+ if (cur_cmd!=the) expand();
+ else{@+q=the_toks();
+ if (link(temp_head)!=null)
+ {@+link(p)=link(temp_head);p=q;
+ }
+ }
+ }
+done2: x_token();
+}
+
+@ @<Look for parameter number...@>=
+{@+s=cur_tok;
+if (xpand) get_x_token();else get_token();
+if (cur_cmd!=mac_param)
+ if ((cur_tok <= zero_token)||(cur_tok > t))
+ {@+print_err("Illegal parameter number in definition of ");
+@.Illegal parameter number...@>
+ sprint_cs(warning_index);
+ help3("You meant to type ## instead of #, right?",@/
+ "Or maybe a } was forgotten somewhere earlier, and things",@/
+ "are all screwed up? I'm going to assume that you meant ##.");
+ back_error();cur_tok=s;
+ }
+ else cur_tok=out_param_token-'0'+cur_chr;
+}
+
+@ Another way to create a token list is via the \.{\\read} command. The
+sixteen files potentially usable for reading appear in the following
+global variables. The value of |read_open[n]| will be |closed| if
+stream number |n| has not been opened or if it has been fully read;
+|just_open| if an \.{\\openin} but not a \.{\\read} has been done;
+and |normal| if it is open and ready to read the next line.
+
+@d closed 2 /*not open, or at end of file*/
+@d just_open 1 /*newly opened, first line not yet read*/
+
+@<Glob...@>=
+static alpha_file @!read_file[16]; /*used for \.{\\read}*/
+static int8_t @!read_open[17]; /*state of |read_file[n]|*/
+
+@ @<Set init...@>=
+for (k=0; k<=16; k++) read_open[k]=closed;
+
+@ The |read_toks| procedure constructs a token list like that for any
+macro definition, and makes |cur_val| point to it. Parameter |r| points
+to the control sequence that will receive this token list.
+
+@p static void read_toks(int @!n, pointer @!r, halfword @!j)
+{@+
+pointer p; /*tail of the token list*/
+pointer @!q; /*new node being added to the token list via |store_new_token|*/
+int @!s; /*saved value of |align_state|*/
+small_number @!m; /*stream number*/
+scanner_status=defining;warning_index=r;
+def_ref=get_avail();token_ref_count(def_ref)=null;
+p=def_ref; /*the reference count*/
+store_new_token(end_match_token);
+if ((n < 0)||(n > 15)) m=16;@+else m=n;
+s=align_state;align_state=1000000; /*disable tab marks, etc.*/
+@/do@+{@<Input and store tokens from the next line of the file@>;
+}@+ while (!(align_state==1000000));
+cur_val=def_ref;scanner_status=normal;align_state=s;
+}
+
+@ @<Input and store tokens from the next line of the file@>=
+begin_file_reading();name=m+1;
+if (read_open[m]==closed) @<Input for \.{\\read} from the terminal@>;
+else if (read_open[m]==just_open) @<Input the first line of |read_file[m]|@>@;
+else@<Input the next line of |read_file[m]|@>;
+limit=last;
+if (end_line_char_inactive) decr(limit);
+else buffer[limit]=end_line_char;
+first=limit+1;loc=start;state=new_line;@/
+@<Handle \.{\\readline} and |goto done|@>;@/
+loop@+{@+get_token();
+ if (cur_tok==0) goto done;
+ /*|cur_cmd==cur_chr==0| will occur at the end of the line*/
+ if (align_state < 1000000) /*unmatched `\.\}' aborts the line*/
+ {@+@/do@+{get_token();}@+ while (!(cur_tok==0));
+ align_state=1000000;goto done;
+ }
+ store_new_token(cur_tok);
+ }
+done: end_file_reading()
+
+@ Here we input on-line into the |buffer| array, prompting the user explicitly
+if |n >= 0|. The value of |n| is set negative so that additional prompts
+will not be given in the case of multi-line input.
+
+@<Input for \.{\\read} from the terminal@>=
+if (interaction > nonstop_mode)
+ if (n < 0) prompt_input("")@;
+ else{@+wake_up_terminal;
+ print_ln();sprint_cs(r);prompt_input("=");n=-1;
+ }
+else fatal_error("*** (cannot \\read from terminal in nonstop modes)")
+@.cannot \\read@>
+
+@ The first line of a file must be treated specially, since |input_ln|
+must be told not to start with |get|.
+@^system dependencies@>
+
+@<Input the first line of |read_file[m]|@>=
+if (input_ln(&read_file[m], false)) read_open[m]=normal;
+else{@+a_close(&read_file[m]);read_open[m]=closed;
+ }
+
+@ An empty line is appended at the end of a |read_file|.
+@^empty line at end of file@>
+
+@<Input the next line of |read_file[m]|@>=
+{@+if (!input_ln(&read_file[m], true))
+ {@+a_close(&read_file[m]);read_open[m]=closed;
+ if (align_state!=1000000)
+ {@+runaway();
+ print_err("File ended within ");print_esc("read");
+@.File ended within \\read@>
+ help1("This \\read has unbalanced braces.");
+ align_state=1000000;limit=0;error();
+ }
+ }
+}
+
+@* Conditional processing.
+We consider now the way \TeX\ handles various kinds of \.{\\if} commands.
+
+@d unless_code 32 /*amount added for `\.{\\unless}' prefix*/
+@#
+@d if_char_code 0 /* `\.{\\if}' */
+@d if_cat_code 1 /* `\.{\\ifcat}' */
+@d if_int_code 2 /* `\.{\\ifnum}' */
+@d if_dim_code 3 /* `\.{\\ifdim}' */
+@d if_odd_code 4 /* `\.{\\ifodd}' */
+@d if_vmode_code 5 /* `\.{\\ifvmode}' */
+@d if_hmode_code 6 /* `\.{\\ifhmode}' */
+@d if_mmode_code 7 /* `\.{\\ifmmode}' */
+@d if_inner_code 8 /* `\.{\\ifinner}' */
+@d if_void_code 9 /* `\.{\\ifvoid}' */
+@d if_hbox_code 10 /* `\.{\\ifhbox}' */
+@d if_vbox_code 11 /* `\.{\\ifvbox}' */
+@d ifx_code 12 /* `\.{\\ifx}' */
+@d if_eof_code 13 /* `\.{\\ifeof}' */
+@d if_true_code 14 /* `\.{\\iftrue}' */
+@d if_false_code 15 /* `\.{\\iffalse}' */
+@d if_case_code 16 /* `\.{\\ifcase}' */
+
+@<Put each...@>=
+primitive("if", if_test, if_char_code);
+@!@:if\_char\_}{\.{\\if} primitive@>
+primitive("ifcat", if_test, if_cat_code);
+@!@:if\_cat\_code\_}{\.{\\ifcat} primitive@>
+primitive("ifnum", if_test, if_int_code);
+@!@:if\_int\_}{\.{\\ifnum} primitive@>
+primitive("ifdim", if_test, if_dim_code);
+@!@:if\_dim\_}{\.{\\ifdim} primitive@>
+primitive("ifodd", if_test, if_odd_code);
+@!@:if\_odd\_}{\.{\\ifodd} primitive@>
+primitive("ifvmode", if_test, if_vmode_code);
+@!@:if\_vmode\_}{\.{\\ifvmode} primitive@>
+primitive("ifhmode", if_test, if_hmode_code);
+@!@:if\_hmode\_}{\.{\\ifhmode} primitive@>
+primitive("ifmmode", if_test, if_mmode_code);
+@!@:if\_mmode\_}{\.{\\ifmmode} primitive@>
+primitive("ifinner", if_test, if_inner_code);
+@!@:if\_inner\_}{\.{\\ifinner} primitive@>
+primitive("ifvoid", if_test, if_void_code);
+@!@:if\_void\_}{\.{\\ifvoid} primitive@>
+primitive("ifhbox", if_test, if_hbox_code);
+@!@:if\_hbox\_}{\.{\\ifhbox} primitive@>
+primitive("ifvbox", if_test, if_vbox_code);
+@!@:if\_vbox\_}{\.{\\ifvbox} primitive@>
+primitive("ifx", if_test, ifx_code);
+@!@:ifx\_}{\.{\\ifx} primitive@>
+primitive("ifeof", if_test, if_eof_code);
+@!@:if\_eof\_}{\.{\\ifeof} primitive@>
+primitive("iftrue", if_test, if_true_code);
+@!@:if\_true\_}{\.{\\iftrue} primitive@>
+primitive("iffalse", if_test, if_false_code);
+@!@:if\_false\_}{\.{\\iffalse} primitive@>
+primitive("ifcase", if_test, if_case_code);
+@!@:if\_case\_}{\.{\\ifcase} primitive@>
+
+@ @<Cases of |print_cmd_chr|...@>=
+case if_test: {@+if (chr_code >= unless_code) print_esc("unless");
+switch (chr_code%unless_code) {
+ case if_cat_code: print_esc("ifcat");@+break;
+ case if_int_code: print_esc("ifnum");@+break;
+ case if_dim_code: print_esc("ifdim");@+break;
+ case if_odd_code: print_esc("ifodd");@+break;
+ case if_vmode_code: print_esc("ifvmode");@+break;
+ case if_hmode_code: print_esc("ifhmode");@+break;
+ case if_mmode_code: print_esc("ifmmode");@+break;
+ case if_inner_code: print_esc("ifinner");@+break;
+ case if_void_code: print_esc("ifvoid");@+break;
+ case if_hbox_code: print_esc("ifhbox");@+break;
+ case if_vbox_code: print_esc("ifvbox");@+break;
+ case ifx_code: print_esc("ifx");@+break;
+ case if_eof_code: print_esc("ifeof");@+break;
+ case if_true_code: print_esc("iftrue");@+break;
+ case if_false_code: print_esc("iffalse");@+break;
+ case if_case_code: print_esc("ifcase");@+break;
+ @/@<Cases of |if_test| for |print_cmd_chr|@>@/
+ default:print_esc("if");
+ }
+} @+break;
+
+@ Conditions can be inside conditions, and this nesting has a stack
+that is independent of the |save_stack|.
+
+Four global variables represent the top of the condition stack:
+|cond_ptr| points to pushed-down entries, if any; |if_limit| specifies
+the largest code of a |fi_or_else| command that is syntactically legal;
+|cur_if| is the name of the current type of conditional; and |if_line|
+is the line number at which it began.
+
+If no conditions are currently in progress, the condition stack has the
+special state |cond_ptr==null|, |if_limit==normal|, |cur_if==0|, |if_line==0|.
+Otherwise |cond_ptr| points to a two-word node; the |type|, |subtype|, and
+|link| fields of the first word contain |if_limit|, |cur_if|, and
+|cond_ptr| at the next level, and the second word contains the
+corresponding |if_line|.
+
+@d if_node_size 2 /*number of words in stack entry for conditionals*/
+@d if_line_field(A) mem[A+1].i
+@d if_code 1 /*code for \.{\\if...} being evaluated*/
+@d fi_code 2 /*code for \.{\\fi}*/
+@d else_code 3 /*code for \.{\\else}*/
+@d or_code 4 /*code for \.{\\or}*/
+
+@<Glob...@>=
+static pointer @!cond_ptr; /*top of the condition stack*/
+static int @!if_limit; /*upper bound on |fi_or_else| codes*/
+static small_number @!cur_if; /*type of conditional being worked on*/
+static int @!if_line; /*line where that conditional began*/
+
+@ @<Set init...@>=
+cond_ptr=null;if_limit=normal;cur_if=0;if_line=0;
+
+@ @<Put each...@>=
+primitive("fi", fi_or_else, fi_code);
+@!@:fi\_}{\.{\\fi} primitive@>
+text(frozen_fi)=text(cur_val);eqtb[frozen_fi]=eqtb[cur_val];
+primitive("or", fi_or_else, or_code);
+@!@:or\_}{\.{\\or} primitive@>
+primitive("else", fi_or_else, else_code);
+@!@:else\_}{\.{\\else} primitive@>
+
+@ @<Cases of |print_cmd_chr|...@>=
+case fi_or_else: if (chr_code==fi_code) print_esc("fi");
+ else if (chr_code==or_code) print_esc("or");
+ else print_esc("else");@+break;
+
+@ When we skip conditional text, we keep track of the line number
+where skipping began, for use in error messages.
+
+@<Glob...@>=
+static int @!skip_line; /*skipping began here*/
+
+@ Here is a procedure that ignores text until coming to an \.{\\or},
+\.{\\else}, or \.{\\fi} at the current level of $\.{\\if}\ldots\.{\\fi}$
+nesting. After it has acted, |cur_chr| will indicate the token that
+was found, but |cur_tok| will not be set (because this makes the
+procedure run faster).
+
+@p static void pass_text(void)
+{@+
+int l; /*level of $\.{\\if}\ldots\.{\\fi}$ nesting*/
+small_number @!save_scanner_status; /*|scanner_status| upon entry*/
+save_scanner_status=scanner_status;scanner_status=skipping;l=0;
+skip_line=line;
+loop@+{@+get_next();
+ if (cur_cmd==fi_or_else)
+ {@+if (l==0) goto done;
+ if (cur_chr==fi_code) decr(l);
+ }
+ else if (cur_cmd==if_test) incr(l);
+ }
+done: scanner_status=save_scanner_status;
+if (tracing_ifs > 0) show_cur_cmd_chr();
+}
+
+@ When we begin to process a new \.{\\if}, we set |if_limit=if_code|; then
+if\/ \.{\\or} or \.{\\else} or \.{\\fi} occurs before the current \.{\\if}
+condition has been evaluated, \.{\\relax} will be inserted.
+For example, a sequence of commands like `\.{\\ifvoid1\\else...\\fi}'
+would otherwise require something after the `\.1'.
+
+@<Push the condition stack@>=
+{@+p=get_node(if_node_size);link(p)=cond_ptr;type(p)=if_limit;
+subtype(p)=cur_if;if_line_field(p)=if_line;
+cond_ptr=p;cur_if=cur_chr;if_limit=if_code;if_line=line;
+}
+
+@ @<Pop the condition stack@>=
+{@+if (if_stack[in_open]==cond_ptr) if_warning();
+ /*conditionals possibly not properly nested with files*/
+p=cond_ptr;if_line=if_line_field(p);
+cur_if=subtype(p);if_limit=type(p);cond_ptr=link(p);
+free_node(p, if_node_size);
+}
+
+@ Here's a procedure that changes the |if_limit| code corresponding to
+a given value of |cond_ptr|.
+
+@p static void change_if_limit(small_number @!l, pointer @!p)
+{@+
+pointer q;
+if (p==cond_ptr) if_limit=l; /*that's the easy case*/
+else{@+q=cond_ptr;
+ loop@+{@+if (q==null) confusion("if");
+@:this can't happen if}{\quad if@>
+ if (link(q)==p)
+ {@+type(q)=l;return;
+ }
+ q=link(q);
+ }
+ }
+}
+
+@ A condition is started when the |expand| procedure encounters
+an |if_test| command; in that case |expand| reduces to |conditional|,
+which is a recursive procedure.
+@^recursion@>
+
+@p static void conditional(void)
+{@+
+bool b; /*is the condition true?*/
+int @!r; /*relation to be evaluated*/
+int @!m, @!n; /*to be tested against the second operand*/
+pointer @!p, @!q; /*for traversing token lists in \.{\\ifx} tests*/
+small_number @!save_scanner_status; /*|scanner_status| upon entry*/
+pointer @!save_cond_ptr; /*|cond_ptr| corresponding to this conditional*/
+small_number @!this_if; /*type of this conditional*/
+bool @!is_unless; /*was this if preceded by `\.{\\unless}' ?*/
+if (tracing_ifs > 0) if (tracing_commands <= 1) show_cur_cmd_chr();
+@<Push the condition stack@>;@+save_cond_ptr=cond_ptr;
+is_unless=(cur_chr >= unless_code);this_if=cur_chr%unless_code;@/
+@<Either process \.{\\ifcase} or set |b| to the value of a boolean condition@>;
+if (is_unless) b=!b;
+if (tracing_commands > 1) @<Display the value of |b|@>;
+if (b)
+ {@+change_if_limit(else_code, save_cond_ptr);
+ return; /*wait for \.{\\else} or \.{\\fi}*/
+ }
+@<Skip to \.{\\else} or \.{\\fi}, then |goto common_ending|@>;
+common_ending: if (cur_chr==fi_code) @<Pop the condition stack@>@;
+else if_limit=fi_code; /*wait for \.{\\fi}*/
+}
+
+@ In a construction like `\.{\\if\\iftrue abc\\else d\\fi}', the first
+\.{\\else} that we come to after learning that the \.{\\if} is false is
+not the \.{\\else} we're looking for. Hence the following curious
+logic is needed.
+
+@ @<Skip to \.{\\else} or \.{\\fi}...@>=
+loop@+{@+pass_text();
+ if (cond_ptr==save_cond_ptr)
+ {@+if (cur_chr!=or_code) goto common_ending;
+ print_err("Extra ");print_esc("or");
+@.Extra \\or@>
+ help1("I'm ignoring this; it doesn't match any \\if.");
+ error();
+ }
+ else if (cur_chr==fi_code) @<Pop the condition stack@>;
+ }
+
+@ @<Either process \.{\\ifcase} or set |b|...@>=
+switch (this_if) {
+case if_char_code: case if_cat_code: @<Test if two characters match@>@;@+break;
+case if_int_code: case if_dim_code: @<Test relation between integers or dimensions@>@;@+break;
+case if_odd_code: @<Test if an integer is odd@>@;@+break;
+case if_vmode_code: b=(abs(mode)==vmode);@+break;
+case if_hmode_code: b=(abs(mode)==hmode);@+break;
+case if_mmode_code: b=(abs(mode)==mmode);@+break;
+case if_inner_code: b=(mode < 0);@+break;
+case if_void_code: case if_hbox_code:
+ case if_vbox_code: @<Test box register status@>@;@+break;
+case ifx_code: @<Test if two tokens match@>@;@+break;
+case if_eof_code: {@+scan_four_bit_int();b=(read_open[cur_val]==closed);
+ } @+break;
+case if_true_code: b=true;@+break;
+case if_false_code: b=false;@+break;
+@/@<Cases for |conditional|@>@/
+case if_case_code: @<Select the appropriate case and |return| or |goto common_ending|@>;
+} /*there are no other cases*/
+
+@ @<Display the value of |b|@>=
+{@+begin_diagnostic();
+if (b) print("{true}");@+else print("{false}");
+end_diagnostic(false);
+}
+
+@ Here we use the fact that |'<'|, |'='|, and |'>'| are consecutive ASCII
+codes.
+@^ASCII code@>
+
+@<Test relation between integers or dimensions@>=
+{@+if (this_if==if_int_code) scan_int();@+else scan_normal_dimen;
+n=cur_val;@<Get the next non-blank non-call...@>;
+if ((cur_tok >= other_token+'<')&&(cur_tok <= other_token+'>'))
+ r=cur_tok-other_token;
+else{@+print_err("Missing = inserted for ");
+@.Missing = inserted@>
+ print_cmd_chr(if_test, this_if);
+ help1("I was expecting to see `<', `=', or `>'. Didn't.");
+ back_error();r='=';
+ }
+if (this_if==if_int_code) scan_int();@+else scan_normal_dimen;
+switch (r) {
+case '<': b=(n < cur_val);@+break;
+case '=': b=(n==cur_val);@+break;
+case '>': b=(n > cur_val);
+}
+}
+
+@ @<Test if an integer is odd@>=
+{@+scan_int();b=odd(cur_val);
+}
+
+@ @<Test box register status@>=
+{@+scan_register_num();fetch_box(p);
+if (this_if==if_void_code) b=(p==null);
+else if (p==null) b=false;
+else if (this_if==if_hbox_code) b=(type(p)==hlist_node);
+else b=(type(p)==vlist_node);
+}
+
+@ An active character will be treated as category 13 following
+\.{\\if\\noexpand} or following \.{\\ifcat\\noexpand}. We use the fact that
+active characters have the smallest tokens, among all control sequences.
+
+@d get_x_token_or_active_char @t@>@;
+ {@+get_x_token();
+ if (cur_cmd==relax) if (cur_chr==no_expand_flag)
+ {@+cur_cmd=active_char;
+ cur_chr=cur_tok-cs_token_flag-active_base;
+ }
+ }
+
+@<Test if two characters match@>=
+{@+get_x_token_or_active_char;
+if ((cur_cmd > active_char)||(cur_chr > 255)) /*not a character*/
+ {@+m=relax;n=256;
+ }
+else{@+m=cur_cmd;n=cur_chr;
+ }
+get_x_token_or_active_char;
+if ((cur_cmd > active_char)||(cur_chr > 255))
+ {@+cur_cmd=relax;cur_chr=256;
+ }
+if (this_if==if_char_code) b=(n==cur_chr);@+else b=(m==cur_cmd);
+}
+
+@ Note that `\.{\\ifx}' will declare two macros different if one is \\{long}
+or \\{outer} and the other isn't, even though the texts of the macros are
+the same.
+
+We need to reset |scanner_status|, since \.{\\outer} control sequences
+are allowed, but we might be scanning a macro definition or preamble.
+
+@<Test if two tokens match@>=
+{@+save_scanner_status=scanner_status;scanner_status=normal;
+get_next();n=cur_cs;p=cur_cmd;q=cur_chr;
+get_next();if (cur_cmd!=p) b=false;
+else if (cur_cmd < call) b=(cur_chr==q);
+else@<Test if two macro texts match@>;
+scanner_status=save_scanner_status;
+}
+
+@ Note also that `\.{\\ifx}' decides that macros \.{\\a} and \.{\\b} are
+different in examples like this:
+$$\vbox{\halign{\.{#}\hfil&\qquad\.{#}\hfil\cr
+ {}\\def\\a\{\\c\}&
+ {}\\def\\c\{\}\cr
+ {}\\def\\b\{\\d\}&
+ {}\\def\\d\{\}\cr}}$$
+
+@<Test if two macro texts match@>=
+{@+p=link(cur_chr);q=link(equiv(n)); /*omit reference counts*/
+if (p==q) b=true;
+else{@+while ((p!=null)&&(q!=null))
+ if (info(p)!=info(q)) p=null;
+ else{@+p=link(p);q=link(q);
+ }
+ b=((p==null)&&(q==null));
+ }
+}
+
+@ @<Select the appropriate case and |return| or |goto common_ending|@>=
+{@+scan_int();n=cur_val; /*|n| is the number of cases to pass*/
+if (tracing_commands > 1)
+ {@+begin_diagnostic();print("{case ");print_int(n);print_char('}');
+ end_diagnostic(false);
+ }
+while (n!=0)
+ {@+pass_text();
+ if (cond_ptr==save_cond_ptr)
+ if (cur_chr==or_code) decr(n);
+ else goto common_ending;
+ else if (cur_chr==fi_code) @<Pop the condition stack@>;
+ }
+change_if_limit(or_code, save_cond_ptr);
+return; /*wait for \.{\\or}, \.{\\else}, or \.{\\fi}*/
+}
+
+@ The processing of conditionals is complete except for the following
+code, which is actually part of |expand|. It comes into play when
+\.{\\or}, \.{\\else}, or \.{\\fi} is scanned.
+
+@<Terminate the current conditional and skip to \.{\\fi}@>=
+{@+if (tracing_ifs > 0) if (tracing_commands <= 1) show_cur_cmd_chr();
+if (cur_chr > if_limit)
+ if (if_limit==if_code) insert_relax(); /*condition not yet evaluated*/
+ else{@+print_err("Extra ");print_cmd_chr(fi_or_else, cur_chr);
+@.Extra \\or@>
+@.Extra \\else@>
+@.Extra \\fi@>
+ help1("I'm ignoring this; it doesn't match any \\if.");
+ error();
+ }
+else{@+while (cur_chr!=fi_code) pass_text(); /*skip to \.{\\fi}*/
+ @<Pop the condition stack@>;
+ }
+}
+
+@* File names.
+It's time now to fret about file names. Besides the fact that different
+operating systems treat files in different ways, we must cope with the
+fact that completely different naming conventions are used by different
+groups of people. The following programs show what is required for one
+particular operating system; similar routines for other systems are not
+difficult to devise.
+@^fingers@>
+@^system dependencies@>
+
+\TeX\ assumes that a file name has three parts: the name proper; its
+``extension''; and a ``file area'' where it is found in an external file
+system. The extension of an input file or a write file is assumed to be
+`\.{.tex}' unless otherwise specified; it is `\.{.log}' on the
+transcript file that records each run of \TeX; it is `\.{.tfm}' on the font
+metric files that describe characters in the fonts \TeX\ uses; it is
+`\.{.dvi}' on the output files that specify typesetting information; and it
+is `\.{.fmt}' on the format files written by \.{INITEX} to initialize \TeX.
+The file area can be arbitrary on input files, but files are usually
+output to the user's current area. If an input file cannot be
+found on the specified area, \TeX\ will look for it on a special system
+area; this special area is intended for commonly used input files like
+\.{webmac.tex}.
+
+Simple uses of \TeX\ refer only to file names that have no explicit
+extension or area. For example, a person usually says `\.{\\input} \.{paper}'
+or `\.{\\font\\tenrm} \.= \.{helvetica}' instead of `\.{\\input}
+\.{paper.new}' or `\.{\\font\\tenrm} \.= \.{<csd.knuth>test}'. Simple file
+names are best, because they make the \TeX\ source files portable;
+whenever a file name consists entirely of letters and digits, it should be
+treated in the same way by all implementations of \TeX. However, users
+need the ability to refer to other files in their environment, especially
+when responding to error messages concerning unopenable files; therefore
+we want to let them use the syntax that appears in their favorite
+operating system.
+
+The following procedures don't allow spaces to be part of
+file names; but some users seem to like names that are spaced-out.
+System-dependent changes to allow such things should probably
+be made with reluctance, and only when an entire file name that
+includes spaces is ``quoted'' somehow.
+
+@ In order to isolate the system-dependent aspects of file names, the
+@^system dependencies@>
+system-independent parts of \TeX\ are expressed in terms
+of three system-dependent
+procedures called |begin_name|, |more_name|, and |end_name|. In
+essence, if the user-specified characters of the file name are $c_1\ldots c_n$,
+the system-independent driver program does the operations
+$$|begin_name|;\,|more_name|(c_1);\,\ldots\,;\,|more_name|(c_n);
+\,|end_name|.$$
+These three procedures communicate with each other via global variables.
+Afterwards the file name will appear in the string pool as three strings
+called |cur_name|\penalty10000\hskip-.05em,
+|cur_area|, and |cur_ext|; the latter two are null (i.e.,
+|""|), unless they were explicitly specified by the user.
+
+Actually the situation is slightly more complicated, because \TeX\ needs
+to know when the file name ends. The |more_name| routine is a function
+(with side effects) that returns |true| on the calls |more_name|$(c_1)$,
+\dots, |more_name|$(c_{n-1})$. The final call |more_name|$(c_n)$
+returns |false|; or, it returns |true| and the token following $c_n$ is
+something like `\.{\\hbox}' (i.e., not a character). In other words,
+|more_name| is supposed to return |true| unless it is sure that the
+file name has been completely scanned; and |end_name| is supposed to be able
+to finish the assembly of |cur_name|, |cur_area|, and |cur_ext| regardless of
+whether $|more_name|(c_n)$ returned |true| or |false|.
+
+@<Glob...@>=
+static str_number @!cur_name; /*name of file just scanned*/
+static str_number @!cur_area; /*file area just scanned, or \.{""}*/
+static str_number @!cur_ext; /*file extension just scanned, or \.{""}*/
+
+@ The file names we shall deal with for illustrative purposes have the
+following structure: If the name contains `\.>' or `\.:', the file area
+consists of all characters up to and including the final such character;
+otherwise the file area is null. If the remaining file name contains
+`\..', the file extension consists of all such characters from the first
+remaining `\..' to the end, otherwise the file extension is null.
+@^system dependencies@>
+
+We can scan such file names easily by using two global variables that keep track
+of the occurrences of area and extension delimiters:
+
+@<Glob...@>=
+static pool_pointer @!area_delimiter; /*the most recent `\.>' or `\.:', if any*/
+static pool_pointer @!ext_delimiter; /*the relevant `\..', if any*/
+
+@ Input files that can't be found in the user's area may appear in a standard
+system area called |TEX_area|. Font metric files whose areas are not given
+explicitly are assumed to appear in a standard system area called
+|TEX_font_area|. These system area names will, of course, vary from place
+to place.
+@^system dependencies@>
+
+@d TEX_area "TeXinputs/"
+@.TeXinputs@>
+@d TEX_font_area "TeXfonts/"
+@.TeXfonts@>
+
+@ Here now is the first of the system-dependent routines for file name scanning.
+@^system dependencies@>
+
+@p static bool quoted_filename;
+static void begin_name(void)
+{@+area_delimiter=0;ext_delimiter=0; quoted_filename=false;
+}
+
+@ And here's the second. The string pool might change as the file name is
+being scanned, since a new \.{\\csname} might be entered; therefore we keep
+|area_delimiter| and |ext_delimiter| relative to the beginning of the current
+string, instead of assigning an absolute address like |pool_ptr| to them.
+@^system dependencies@>
+
+@p static bool more_name(ASCII_code @!c)
+{@+if (c==' ' && !quoted_filename) return false;
+else if (c=='"') {@+quoted_filename=!quoted_filename; return true; }
+else {@+str_room(1);append_char(c); /*contribute |c| to the current string*/
+ if (IS_DIR_SEP(c)) {@+area_delimiter=cur_length;ext_delimiter=0; }
+ else if (c=='.') ext_delimiter=cur_length;
+ return true;
+ }
+}
+
+@ The third.
+@^system dependencies@>
+
+@p static void end_name(void)
+{@+if (str_ptr+3 > max_strings)
+ overflow("number of strings", max_strings-init_str_ptr);
+@:TeX capacity exceeded number of strings}{\quad number of strings@>
+if (area_delimiter==0) cur_area=empty_string;
+else{@+cur_area=str_ptr;
+ str_start[str_ptr+1]=str_start[str_ptr]+area_delimiter;incr(str_ptr);
+ }
+if (ext_delimiter==0)
+ {@+cur_ext=empty_string;cur_name=make_string();
+ }
+else{@+cur_name=str_ptr;
+ str_start[str_ptr+1]=str_start[str_ptr]+ext_delimiter-area_delimiter-1;
+ incr(str_ptr);cur_ext=make_string();
+ }
+}
+
+@ Conversely, here is a routine that takes three strings and prints a file
+name that might have produced them. (The routine is system dependent, because
+some operating systems put the file area last instead of first.)
+@^system dependencies@>
+
+@<Basic printing...@>=
+static void print_file_name(int @!n, int @!a, int @!e)
+{@+slow_print(a);slow_print(n);slow_print(e);
+}
+
+@ Another system-dependent routine is needed to convert three internal
+\TeX\ strings
+into the |name_of_file| value that is used to open files. The present code
+allows both lowercase and uppercase letters in the file name.
+@^system dependencies@>
+
+@d append_to_name(A) {@+c=A;incr(k);
+ if (k <= file_name_size) name_of_file[k]=xchr[c];
+ }
+
+@p static void pack_file_name(str_number @!n, str_number @!a, str_number @!e, char *@!f)
+{@+int k; /*number of positions filled in |name_of_file|*/
+ASCII_code @!c; /*character being packed*/
+int @!j; /*index into |str_pool|*/
+k=0;
+for (j=str_start[a]; j<=str_start[a+1]-1; j++) append_to_name(so(str_pool[j]))@;
+for (j=str_start[n]; j<=str_start[n+1]-1; j++) append_to_name(so(str_pool[j]))@;
+if (f==NULL)
+ for (j=str_start[e]; j<=str_start[e+1]-1; j++) append_to_name(so(str_pool[j]))@;
+else
+ while(*f!=0) append_to_name(so(*f++))@;
+if (k <= file_name_size) name_length=k;@+else name_length=file_name_size;
+name_of_file[name_length+1]=0;
+}
+
+
+
+@ \TeX\ Live does not use the global variable |TEX_format_default|. It is no longer needed
+to supply the text for default system areas
+and extensions related to format files.
+
+@ Consequently \TeX\ Live does not need the initialization of |TEX_format_default| either.
+
+@ And \TeX\ Live does not check the length of |TEX_format_default|.
+
+@ The |format_extension|, however, is needed by \TeX\ Live
+to create the format name from the job name.
+
+@d format_extension ".fmt"
+
+@ This part of the program
+becomes active when a ``virgin'' \TeX\ is trying to get going, just after
+the preliminary initialization, or when the user is substituting another
+format file by typing `\.\&' after the initial `\.{**}' prompt. The buffer
+contains the first line of input in |buffer[loc dotdot(last-1)]|, where
+|loc < last| and |buffer[loc]!=' '|.
+
+\TeX\ Live uses the {\tt kpathsearch} library to implement access to files.
+|open_fmt_file| is declared here and the
+actual implementation is in the section on \TeX\ Live Integration.
+
+@<Declare the function called |open_fmt_file|@>=
+static bool open_fmt_file(void);
+
+@ Operating systems often make it possible to determine the exact name (and
+possible version number) of a file that has been opened. The following routine,
+which simply makes a \TeX\ string from the value of |name_of_file|, should
+ideally be changed to deduce the full name of file~|f|, which is the file
+most recently opened, if it is possible to do this in a \PASCAL\ program.
+@^system dependencies@>
+
+This routine might be called after string memory has overflowed, hence
+we dare not use `|str_room|'.
+
+@p static str_number make_name_string(void)
+{@+int k; /*index into |name_of_file|*/
+if ((pool_ptr+name_length > pool_size)||(str_ptr==max_strings)||
+ (cur_length > 0))
+ return'?';
+else{@+for (k=1; k<=name_length; k++) append_char(xord[name_of_file[k]]);
+ return make_string();
+ }
+}
+static str_number a_make_name_string(alpha_file *f)
+{@+return make_name_string();
+}
+static str_number b_make_name_string(byte_file *f)
+{@+return make_name_string();
+}
+#ifdef @!INIT
+static str_number w_make_name_string(word_file *f)
+{@+return make_name_string();
+}
+#endif
+
+@ Now let's consider the ``driver''
+routines by which \TeX\ deals with file names
+in a system-independent manner. First comes a procedure that looks for a
+file name.
+There are two ways to specify the file name: as a general text argument
+or as a token (after expansion).
+The traditional token delimiter is the space. For a file name, however,
+a double quote is used as the token delimiter if the token starts
+with a double quote.
+
+Once the |area_delimiter| and the |ext_delimiter| are defined,
+the final processing is shared for all variants.
+
+When starting, \.{\\relax} is skipped as well as blanks and non-calls.
+Then a test for the |left_brace| will branch to the code for scanning
+a general text.
+% consider the case where a double quote is made an active character
+% equivalent to a left brace!
+
+@p static void scan_file_name(void)
+{@+
+pool_pointer @!j, k; /*index into |str_pool|*/
+int @!old_setting; /*holds |selector| setting*/
+name_in_progress=true;begin_name();
+@<Get the next non-blank non-relax...@>;
+if (cur_cmd==left_brace)
+ @<Define a general text file name and |goto done|@>@;
+loop@+{@+if ((cur_cmd > other_char)||(cur_chr > 255)) /*not a character*/
+ {@+back_input();goto done;
+ }
+#if 0
+ /* This is from pdftex-final.ch. I don't know these `some cases',
+ and I am not sure whether the name should end even if quoting is on.*/
+ /*If |cur_chr| is a space and we're not scanning a token list, check
+ whether we're at the end of the buffer. Otherwise we end up adding
+ spurious spaces to file names in some cases.*/
+ if (cur_chr==' ' && state!=token_list && loc>limit) goto done;
+#endif
+ if (!more_name(cur_chr)) goto done;
+ get_x_token();
+ }
+done: end_name();name_in_progress=false;
+}
+
+@ The global variable |name_in_progress| is used to prevent recursive
+use of |scan_file_name|, since the |begin_name| and other procedures
+communicate via global variables. Recursion would arise only by
+devious tricks like `\.{\\input\\input f}'; such attempts at sabotage
+must be thwarted. Furthermore, |name_in_progress| prevents \.{\\input}
+@^recursion@>
+from being initiated when a font size specification is being scanned.
+
+Another global variable, |job_name|, contains the file name that was first
+\.{\\input} by the user. This name is extended by `\.{.log}' and `\.{.dvi}'
+and `\.{.fmt}' in the names of \TeX's output files.
+
+@<Glob...@>=
+static bool @!name_in_progress; /*is a file name being scanned?*/
+static str_number @!job_name; /*principal file name*/
+static bool @!log_opened; /*has the transcript file been opened?*/
+
+@ Initially |job_name==0|; it becomes nonzero as soon as the true name is known.
+We have |job_name==0| if and only if the `\.{log}' file has not been opened,
+except of course for a short time just after |job_name| has become nonzero.
+
+@<Initialize the output...@>=
+job_name=0;name_in_progress=false;log_opened=false;
+
+@ Here is a routine that manufactures the output file names, assuming that
+|job_name!=0|. It ignores and changes the current settings of |cur_area|
+and |cur_ext|.
+
+@d pack_cur_name(A) if (cur_ext==empty_string) pack_file_name(cur_name, cur_area, cur_ext,A);
+ else pack_file_name(cur_name, cur_area, cur_ext,NULL)
+
+@p static void pack_job_name(char *@!s) /*|s==".log"|, |".dvi"|, or
+ |format_extension|*/
+{@+cur_area=empty_string;cur_ext=empty_string;
+cur_name=job_name;pack_cur_name(s);
+}
+
+@ If some trouble arises when \TeX\ tries to open a file, the following
+routine calls upon the user to supply another file name. Parameter~|s|
+is used in the error message to identify the type of file; parameter~|e|
+is the default extension if none is given.
+We handle the specification of a file name with possibly spaces in
+double quotes (the last one is optional if this is the end of line
+i.e. the end of the buffer).
+Upon exit from the routine,
+variables |cur_name|, |cur_area|, |cur_ext|, and |name_of_file| are
+ready for another attempt at file opening.
+
+@p static void prompt_file_name(char *@!s, char *@!e)
+{@+
+int k; /*index into |buffer|*/
+if (interaction==scroll_mode) wake_up_terminal;
+if (strcmp(s,"input file name")==0) print_err("I can't find file `");
+@.I can't find file x@>
+else print_err("I can't write on file `");
+@.I can't write on file x@>
+print_file_name(cur_name, cur_area, cur_ext);print("'.");
+if (strcmp(e,".tex")==0) show_context();
+print_nl("Please type another ");print(s);
+@.Please type...@>
+if (interaction < scroll_mode)
+ fatal_error("*** (job aborted, file error in nonstop mode)");
+@.job aborted, file error...@>
+clear_terminal;prompt_input(": ");@<Scan file name in the buffer@>;
+pack_cur_name(e);
+}
+
+@ @<Scan file name in the buffer@>=
+{@+
+begin_name();k=first;
+while ((buffer[k]==' ')&&(k < last)) incr(k);
+loop@+{@+if (k==last) goto done;
+ if (!more_name(buffer[k])) goto done;
+ incr(k);
+ }
+done: end_name();
+}
+
+@ Here's an example of how these conventions are used. Whenever it is time to
+ship out a box of stuff, we shall use the macro |ensure_dvi_open|.
+
+@d ensure_dvi_open if (output_file_name==0)
+ {@+if (job_name==0) open_log_file();
+ pack_job_name(".dvi");
+ while (!b_open_out(&dvi_file))
+ prompt_file_name("file name for output",".dvi");
+ output_file_name=b_make_name_string(&dvi_file);
+ }
+
+@<Glob...@>=
+static byte_file @!dvi_file; /*the device-independent output goes here*/
+static str_number @!output_file_name; /*full name of the output file*/
+static str_number @!log_name; /*full name of the log file*/
+
+@ @<Initialize the output...@>=output_file_name=0;
+
+@ The |open_log_file| routine is used to open the transcript file and to help
+it catch up to what has previously been printed on the terminal.
+
+@p static void open_log_file(void)
+{@+int old_setting; /*previous |selector| setting*/
+int @!k; /*index into |months| and |buffer|*/
+int @!l; /*end of first input line*/
+char @!months[]=" JANFEBMARAPRMAYJUNJULAUGSEPOCTNOVDEC"; /*abbreviations of month names*/
+old_setting=selector;
+if (job_name==0) job_name=s_no(c_job_name?c_job_name:"texput"); /* \TeX\ Live */
+pack_job_name(".fls");
+recorder_change_filename((char *)name_of_file+1);
+@.texput@>
+pack_job_name(".log");
+while (!a_open_out(&log_file)) @<Try to get a different log file name@>;
+log_name=a_make_name_string(&log_file);
+selector=log_only;log_opened=true;
+@<Print the banner line, including the date and time@>;
+input_stack[input_ptr]=cur_input; /*make sure bottom level is in memory*/
+print_nl("**");
+@.**@>
+l=input_stack[0].limit_field; /*last position of first line*/
+if (buffer[l]==end_line_char) decr(l);
+for (k=1; k<=l; k++) printn(buffer[k]);
+print_ln(); /*now the transcript file contains the first line of input*/
+selector=old_setting+2; /*|log_only| or |term_and_log|*/
+}
+
+@ Sometimes |open_log_file| is called at awkward moments when \TeX\ is
+unable to print error messages or even to |show_context|.
+The |prompt_file_name| routine can result in a |fatal_error|, but the |error|
+routine will not be invoked because |log_opened| will be false.
+
+The normal idea of |batch_mode| is that nothing at all should be written
+on the terminal. However, in the unusual case that
+no log file could be opened, we make an exception and allow
+an explanatory message to be seen.
+
+Incidentally, the program always refers to the log file as a `\.{transcript
+file}', because some systems cannot use the extension `\.{.log}' for
+this file.
+
+@<Try to get a different log file name@>=
+{@+selector=term_only;
+prompt_file_name("transcript file name",".log");
+}
+
+@ @<Print the banner...@>=
+{@+wlog("%s",banner);
+slow_print(format_ident);print(" ");
+print_int(sys_day);print_char(' ');
+for (k=3*sys_month-2; k<=3*sys_month; k++) wlog("%c",months[k]);
+print_char(' ');print_int(sys_year);print_char(' ');
+print_two(sys_time/60);print_char(':');print_two(sys_time%60);
+if (eTeX_ex)
+ {@+;wlog_cr;wlog("entering extended mode");
+ }
+if (Prote_ex)
+ {@+;wlog_cr;wlog("entering Prote mode");
+ }
+}
+
+@ Let's turn now to the procedure that is used to initiate file reading
+when an `\.{\\input}' command is being processed.
+Beware: For historic reasons, this code foolishly conserves a tiny bit
+of string pool space; but that can confuse the interactive `\.E' option.
+@^system dependencies@>
+
+@p static void start_input(void) /*\TeX\ will \.{\\input} something*/
+{@+
+scan_file_name(); /*set |cur_name| to desired file name*/
+pack_cur_name("");
+loop@+{@+begin_file_reading(); /*set up |cur_file| and new level of input*/
+ if (kpse_in_name_ok((char*)name_of_file+1) && a_open_in(&cur_file)) goto done;
+ end_file_reading(); /*remove the level that didn't work*/
+ prompt_file_name("input file name",".tex");
+ }
+done: name=a_make_name_string(&cur_file);@/
+if (source_filename_stack[in_open]!=NULL)
+ free(source_filename_stack[in_open]);
+source_filename_stack[in_open]=strdup((char *)name_of_file+1); /*\TeX\ Live*/
+if (full_source_filename_stack[in_open]!=NULL)
+ free(full_source_filename_stack[in_open]);
+full_source_filename_stack[in_open]=strdup(full_name_of_file);
+@<Set new |cur_file_num|@>@;
+/* new entry on the macro stack */
+{ @<additional local variables for |start_input|@>
+ @<update the macro stack@>@;
+}
+if (job_name==0)
+ {@+if (c_job_name==NULL) job_name=cur_name;
+ else job_name=s_no(c_job_name); open_log_file(); /* \TeX\ Live*/
+ } /*|open_log_file| doesn't |show_context|, so |limit|
+ and |loc| needn't be set to meaningful values yet*/
+if (term_offset+strlen(full_source_filename_stack[in_open]) > max_print_line-2)
+ print_ln();
+else if ((term_offset > 0)||(file_offset > 0)) print_char(' ');
+print_char('(');incr(open_parens);
+print(full_source_filename_stack[in_open]);update_terminal;
+state=new_line;
+if (name==str_ptr-1) /*conserve string pool space (but see note above)*/
+ {@+flush_string;name=cur_name;
+ }
+@<Read the first line of the new file@>;
+}
+
+@ Here we have to remember to tell the |input_ln| routine not to
+start with a |get|. If the file is empty, it is considered to
+contain a single blank line.
+@^system dependencies@>
+@^empty line at end of file@>
+
+@<Read the first line...@>=
+{@+line=1;
+if (input_ln(&cur_file, false)) do_nothing;
+firm_up_the_line();
+if (end_line_char_inactive) decr(limit);
+else buffer[limit]=end_line_char;
+first=limit+1;loc=start;
+}
+
+@* Font metric data.
+\TeX\ gets its knowledge about fonts from font metric files, also called
+\.{TFM} files; the `\.T' in `\.{TFM}' stands for \TeX,
+but other programs know about them too.
+@:TFM files}{\.{TFM} files@>
+@^font metric files@>
+
+The information in a \.{TFM} file appears in a sequence of 8-bit bytes.
+Since the number of bytes is always a multiple of 4, we could
+also regard the file as a sequence of 32-bit words, but \TeX\ uses the
+byte interpretation. The format of \.{TFM} files was designed by
+Lyle Ramshaw in 1980. The intent is to convey a lot of different kinds
+@^Ramshaw, Lyle Harold@>
+of information in a compact but useful form.
+
+@<Glob...@>=
+static byte_file @!tfm_file;
+
+@ The first 24 bytes (6 words) of a \.{TFM} file contain twelve 16-bit
+integers that give the lengths of the various subsequent portions
+of the file. These twelve integers are, in order:
+$$\vbox{\halign{\hfil#&$\null=\null$#\hfil\cr
+|lf|&length of the entire file, in words;\cr
+|lh|&length of the header data, in words;\cr
+|bc|&smallest character code in the font;\cr
+|ec|&largest character code in the font;\cr
+|nw|&number of words in the width table;\cr
+|nh|&number of words in the height table;\cr
+|nd|&number of words in the depth table;\cr
+|ni|&number of words in the italic correction table;\cr
+|nl|&number of words in the lig/kern table;\cr
+|nk|&number of words in the kern table;\cr
+|ne|&number of words in the extensible character table;\cr
+|np|&number of font parameter words.\cr}}$$
+They are all nonnegative and less than $2^{15}$. We must have |bc-1 <= ec <= 255|,
+and
+$$\hbox{|lf==6+lh+(ec-bc+1)+nw+nh+nd+ni+nl+nk+ne+np|.}$$
+Note that a font may contain as many as 256 characters (if |bc==0| and |ec==255|),
+and as few as 0 characters (if |bc==ec+1|).
+
+Incidentally, when two or more 8-bit bytes are combined to form an integer of
+16 or more bits, the most significant bytes appear first in the file.
+This is called BigEndian order.
+@!@^BigEndian order@>
+
+@ The rest of the \.{TFM} file may be regarded as a sequence of ten data
+arrays having the informal specification
+$$\def\arr$[#1]#2${\&{array} $[#1]$ \&{of} #2}
+\def\PB#1{\arr#1}
+\vbox{\halign{\hfil\\{#}&$\,:\,$#\hfil\cr
+header&|[0 dotdot lh-1]@t\\{stuff}@>|\cr
+char\_info&|[bc dotdot ec]char_info_word|\cr
+width&|[0 dotdot nw-1]fix_word|\cr
+height&|[0 dotdot nh-1]fix_word|\cr
+depth&|[0 dotdot nd-1]fix_word|\cr
+italic&|[0 dotdot ni-1]fix_word|\cr
+lig\_kern&|[0 dotdot nl-1]lig_kern_command|\cr
+kern&|[0 dotdot nk-1]fix_word|\cr
+exten&|[0 dotdot ne-1]extensible_recipe|\cr
+param&|[1 dotdot np]fix_word|\cr}}$$
+The most important data type used here is a |@!fix_word|, which is
+a 32-bit representation of a binary fraction. A |fix_word| is a signed
+quantity, with the two's complement of the entire word used to represent
+negation. Of the 32 bits in a |fix_word|, exactly 12 are to the left of the
+binary point; thus, the largest |fix_word| value is $2048-2^{-20}$, and
+the smallest is $-2048$. We will see below, however, that all but two of
+the |fix_word| values must lie between $-16$ and $+16$.
+
+@ The first data array is a block of header information, which contains
+general facts about the font. The header must contain at least two words,
+|header[0]| and |header[1]|, whose meaning is explained below.
+Additional header information of use to other software routines might
+also be included, but \TeX82 does not need to know about such details.
+For example, 16 more words of header information are in use at the Xerox
+Palo Alto Research Center; the first ten specify the character coding
+scheme used (e.g., `\.{XEROX text}' or `\.{TeX math symbols}'), the next five
+give the font identifier (e.g., `\.{HELVETICA}' or `\.{CMSY}'), and the
+last gives the ``face byte.'' The program that converts \.{DVI} files
+to Xerox printing format gets this information by looking at the \.{TFM}
+file, which it needs to read anyway because of other information that
+is not explicitly repeated in \.{DVI}~format.
+
+\yskip\hang|header[0]| is a 32-bit check sum that \TeX\ will copy into
+the \.{DVI} output file. Later on when the \.{DVI} file is printed,
+possibly on another computer, the actual font that gets used is supposed
+to have a check sum that agrees with the one in the \.{TFM} file used by
+\TeX. In this way, users will be warned about potential incompatibilities.
+(However, if the check sum is zero in either the font file or the \.{TFM}
+file, no check is made.) The actual relation between this check sum and
+the rest of the \.{TFM} file is not important; the check sum is simply an
+identification number with the property that incompatible fonts almost
+always have distinct check sums.
+@^check sum@>
+
+\yskip\hang|header[1]| is a |fix_word| containing the design size of
+the font, in units of \TeX\ points. This number must be at least 1.0; it is
+fairly arbitrary, but usually the design size is 10.0 for a ``10 point''
+font, i.e., a font that was designed to look best at a 10-point size,
+whatever that really means. When a \TeX\ user asks for a font
+`\.{at} $\delta$ \.{pt}', the effect is to override the design size
+and replace it by $\delta$, and to multiply the $x$ and~$y$ coordinates
+of the points in the font image by a factor of $\delta$ divided by the
+design size. {\sl All other dimensions in the\/ \.{TFM} file are
+|fix_word|\kern-1pt\ numbers in design-size units}, with the exception of
+|param[1]| (which denotes the slant ratio). Thus, for example, the value
+of |param[6]|, which defines the \.{em} unit, is often the |fix_word| value
+$2^{20}=1.0$, since many fonts have a design size equal to one em.
+The other dimensions must be less than 16 design-size units in absolute
+value; thus, |header[1]| and |param[1]| are the only |fix_word|
+entries in the whole \.{TFM} file whose first byte might be something
+besides 0 or 255.
+
+@ Next comes the |char_info| array, which contains one |@!char_info_word|
+per character. Each word in this part of the file contains six fields
+packed into four bytes as follows.
+
+\yskip\hang first byte: |@!width_index| (8 bits)\par
+\hang second byte: |@!height_index| (4 bits) times 16, plus |@!depth_index|
+ (4~bits)\par
+\hang third byte: |@!italic_index| (6 bits) times 4, plus |@!tag|
+ (2~bits)\par
+\hang fourth byte: |@!rem| (8 bits)\par
+\yskip\noindent
+The actual width of a character is \\{width}|[width_index]|, in design-size
+units; this is a device for compressing information, since many characters
+have the same width. Since it is quite common for many characters
+to have the same height, depth, or italic correction, the \.{TFM} format
+imposes a limit of 16 different heights, 16 different depths, and
+64 different italic corrections.
+
+@!@^italic correction@>
+The italic correction of a character has two different uses.
+(a)~In ordinary text, the italic correction is added to the width only if
+the \TeX\ user specifies `\.{\\/}' after the character.
+(b)~In math formulas, the italic correction is always added to the width,
+except with respect to the positioning of subscripts.
+
+Incidentally, the relation $\\{width}[0]=\\{height}[0]=\\{depth}[0]=
+\\{italic}[0]=0$ should always hold, so that an index of zero implies a
+value of zero. The |width_index| should never be zero unless the
+character does not exist in the font, since a character is valid if and
+only if it lies between |bc| and |ec| and has a nonzero |width_index|.
+
+@ The |tag| field in a |char_info_word| has four values that explain how to
+interpret the |rem| field.
+
+\yskip\hangg|tag==0| (|no_tag|) means that |rem| is unused.\par
+\hangg|tag==1| (|lig_tag|) means that this character has a ligature/kerning
+program starting at position |rem| in the |lig_kern| array.\par
+\hangg|tag==2| (|list_tag|) means that this character is part of a chain of
+characters of ascending sizes, and not the largest in the chain. The
+|rem| field gives the character code of the next larger character.\par
+\hangg|tag==3| (|ext_tag|) means that this character code represents an
+extensible character, i.e., a character that is built up of smaller pieces
+so that it can be made arbitrarily large. The pieces are specified in
+|@!exten[rem]|.\par
+\yskip\noindent
+Characters with |tag==2| and |tag==3| are treated as characters with |tag==0|
+unless they are used in special circumstances in math formulas. For example,
+the \.{\\sum} operation looks for a |list_tag|, and the \.{\\left}
+operation looks for both |list_tag| and |ext_tag|.
+
+@d no_tag 0 /*vanilla character*/
+@d lig_tag 1 /*character has a ligature/kerning program*/
+@d list_tag 2 /*character has a successor in a charlist*/
+@d ext_tag 3 /*character is extensible*/
+
+@ The |lig_kern| array contains instructions in a simple programming language
+that explains what to do for special letter pairs. Each word in this array is a
+|@!lig_kern_command| of four bytes.
+
+\yskip\hang first byte: |skip_byte|, indicates that this is the final program
+ step if the byte is 128 or more, otherwise the next step is obtained by
+ skipping this number of intervening steps.\par
+\hang second byte: |next_char|, ``if |next_char| follows the current character,
+ then perform the operation and stop, otherwise continue.''\par
+\hang third byte: |op_byte|, indicates a ligature step if less than~128,
+ a kern step otherwise.\par
+\hang fourth byte: |rem|.\par
+\yskip\noindent
+In a kern step, an
+additional space equal to |kern[256*(op_byte-128)+rem]| is inserted
+between the current character and |next_char|. This amount is
+often negative, so that the characters are brought closer together
+by kerning; but it might be positive.
+
+There are eight kinds of ligature steps, having |op_byte| codes $4a+2b+c$ where
+$0\le a\le b+c$ and $0\le b,c\le1$. The character whose code is
+|rem| is inserted between the current character and |next_char|;
+then the current character is deleted if $b=0$, and |next_char| is
+deleted if $c=0$; then we pass over $a$~characters to reach the next
+current character (which may have a ligature/kerning program of its own).
+
+If the very first instruction of the |lig_kern| array has |skip_byte==255|,
+the |next_char| byte is the so-called boundary character of this font;
+the value of |next_char| need not lie between |bc| and~|ec|.
+If the very last instruction of the |lig_kern| array has |skip_byte==255|,
+there is a special ligature/kerning program for a boundary character at the
+left, beginning at location |256*op_byte+rem|.
+The interpretation is that \TeX\ puts implicit boundary characters
+before and after each consecutive string of characters from the same font.
+These implicit characters do not appear in the output, but they can affect
+ligatures and kerning.
+
+If the very first instruction of a character's |lig_kern| program has
+|skip_byte > 128|, the program actually begins in location
+|256*op_byte+rem|. This feature allows access to large |lig_kern|
+arrays, because the first instruction must otherwise
+appear in a location | <= 255|.
+
+Any instruction with |skip_byte > 128| in the |lig_kern| array must satisfy
+the condition
+$$\hbox{|256*op_byte+rem < nl|.}$$
+If such an instruction is encountered during
+normal program execution, it denotes an unconditional halt; no ligature
+or kerning command is performed.
+
+@d stop_flag qi(128) /*value indicating `\.{STOP}' in a lig/kern program*/
+@d kern_flag qi(128) /*op code for a kern step*/
+@d skip_byte(A) A.b0
+@d next_char(A) A.b1
+@d op_byte(A) A.b2
+@d rem_byte(A) A.b3
+
+@ Extensible characters are specified by an |@!extensible_recipe|, which
+consists of four bytes called |@!top|, |@!mid|, |@!bot|, and |@!rep| (in this
+order). These bytes are the character codes of individual pieces used to
+build up a large symbol. If |top|, |mid|, or |bot| are zero, they are not
+present in the built-up result. For example, an extensible vertical line is
+like an extensible bracket, except that the top and bottom pieces are missing.
+
+Let $T$, $M$, $B$, and $R$ denote the respective pieces, or an empty box
+if the piece isn't present. Then the extensible characters have the form
+$TR^kMR^kB$ from top to bottom, for some |k >= 0|, unless $M$ is absent;
+in the latter case we can have $TR^kB$ for both even and odd values of~|k|.
+The width of the extensible character is the width of $R$; and the
+height-plus-depth is the sum of the individual height-plus-depths of the
+components used, since the pieces are butted together in a vertical list.
+
+@d ext_top(A) A.b0 /*|top| piece in a recipe*/
+@d ext_mid(A) A.b1 /*|mid| piece in a recipe*/
+@d ext_bot(A) A.b2 /*|bot| piece in a recipe*/
+@d ext_rep(A) A.b3 /*|rep| piece in a recipe*/
+
+@ The final portion of a \.{TFM} file is the |param| array, which is another
+sequence of |fix_word| values.
+
+\yskip\hang|param[1]==slant| is the amount of italic slant, which is used
+to help position accents. For example, |slant==.25| means that when you go
+up one unit, you also go .25 units to the right. The |slant| is a pure
+number; it's the only |fix_word| other than the design size itself that is
+not scaled by the design size.
+
+\hang|param[2]==space| is the normal spacing between words in text.
+Note that character |' '| in the font need not have anything to do with
+blank spaces.
+
+\hang|param[3]==space_stretch| is the amount of glue stretching between words.
+
+\hang|param[4]==space_shrink| is the amount of glue shrinking between words.
+
+\hang|param[5]==x_height| is the size of one ex in the font; it is also
+the height of letters for which accents don't have to be raised or lowered.
+
+\hang|param[6]==quad| is the size of one em in the font.
+
+\hang|param[7]==extra_space| is the amount added to |param[2]| at the
+ends of sentences.
+
+\yskip\noindent
+If fewer than seven parameters are present, \TeX\ sets the missing parameters
+to zero. Fonts used for math symbols are required to have
+additional parameter information, which is explained later.
+
+@d slant_code 1
+@d space_code 2
+@d space_stretch_code 3
+@d space_shrink_code 4
+@d x_height_code 5
+@d quad_code 6
+@d extra_space_code 7
+
+@ So that is what \.{TFM} files hold. Since \TeX\ has to absorb such information
+about lots of fonts, it stores most of the data in a large array called
+|font_info|. Each item of |font_info| is a |memory_word|; the |fix_word|
+data gets converted into |scaled| entries, while everything else goes into
+words of type |four_quarters|.
+
+When the user defines \.{\\font\\f}, say, \TeX\ assigns an internal number
+to the user's font~\.{\\f}. Adding this number to |font_id_base| gives the
+|eqtb| location of a ``frozen'' control sequence that will always select
+the font.
+
+@<Types...@>=
+typedef uint8_t internal_font_number; /*|font| in a |char_node|*/
+typedef int32_t font_index; /*index into |font_info|*/
+
+@ Here now is the (rather formidable) array of font arrays.
+
+@d non_char qi(256) /*a |halfword| code that can't match a real character*/
+@d non_address 0 /*a spurious |bchar_label|*/
+
+@<Glob...@>=
+static memory_word @!font_info[font_mem_size+1];
+ /*the big collection of font data*/
+static font_index @!fmem_ptr; /*first unused word of |font_info|*/
+static internal_font_number @!font_ptr; /*largest internal font number in use*/
+static four_quarters @!font_check0[font_max-font_base+1],
+ *const @!font_check = @!font_check0-font_base; /*check sum*/
+static scaled @!font_size0[font_max-font_base+1],
+ *const @!font_size = @!font_size0-font_base; /*``at'' size*/
+static scaled @!font_dsize0[font_max-font_base+1],
+ *const @!font_dsize = @!font_dsize0-font_base; /*``design'' size*/
+static font_index @!font_params0[font_max-font_base+1],
+ *const @!font_params = @!font_params0-font_base; /*how many font
+ parameters are present*/
+static str_number @!font_name0[font_max-font_base+1],
+ *const @!font_name = @!font_name0-font_base; /*name of the font*/
+static str_number @!font_area0[font_max-font_base+1],
+ *const @!font_area = @!font_area0-font_base; /*area of the font*/
+static eight_bits @!font_bc0[font_max-font_base+1],
+ *const @!font_bc = @!font_bc0-font_base;
+ /*beginning (smallest) character code*/
+static eight_bits @!font_ec0[font_max-font_base+1],
+ *const @!font_ec = @!font_ec0-font_base;
+ /*ending (largest) character code*/
+static pointer @!font_glue0[font_max-font_base+1],
+ *const @!font_glue = @!font_glue0-font_base;
+ /*glue specification for interword space, |null| if not allocated*/
+static bool @!font_used0[font_max-font_base+1],
+ *const @!font_used = @!font_used0-font_base;
+ /*has a character from this font actually appeared in the output?*/
+static int @!hyphen_char0[font_max-font_base+1],
+ *const @!hyphen_char = @!hyphen_char0-font_base;
+ /*current \.{\\hyphenchar} values*/
+static int @!skew_char0[font_max-font_base+1],
+ *const @!skew_char = @!skew_char0-font_base;
+ /*current \.{\\skewchar} values*/
+static font_index @!bchar_label0[font_max-font_base+1],
+ *const @!bchar_label = @!bchar_label0-font_base;
+ /*start of |lig_kern| program for left boundary character,
+ |non_address| if there is none*/
+static int16_t @!font_bchar0[font_max-font_base+1],
+ *const @!font_bchar = @!font_bchar0-font_base;
+ /*boundary character, |non_char| if there is none*/
+static int16_t @!font_false_bchar0[font_max-font_base+1],
+ *const @!font_false_bchar = @!font_false_bchar0-font_base;
+ /*|font_bchar| if it doesn't exist in the font, otherwise |non_char|*/
+
+@ Besides the arrays just enumerated, we have directory arrays that make it
+easy to get at the individual entries in |font_info|. For example, the
+|char_info| data for character |c| in font |f| will be in
+|font_info[char_base[f]+c].qqqq|; and if |w| is the |width_index|
+part of this word (the |b0| field), the width of the character is
+|font_info[width_base[f]+w].sc|. (These formulas assume that
+|min_quarterword| has already been added to |c| and to |w|, since \TeX\
+stores its quarterwords that way.)
+
+@<Glob...@>=
+static int @!char_base0[font_max-font_base+1],
+ *const @!char_base = @!char_base0-font_base;
+ /*base addresses for |char_info|*/
+static int @!width_base0[font_max-font_base+1],
+ *const @!width_base = @!width_base0-font_base;
+ /*base addresses for widths*/
+static int @!height_base0[font_max-font_base+1],
+ *const @!height_base = @!height_base0-font_base;
+ /*base addresses for heights*/
+static int @!depth_base0[font_max-font_base+1],
+ *const @!depth_base = @!depth_base0-font_base;
+ /*base addresses for depths*/
+static int @!italic_base0[font_max-font_base+1],
+ *const @!italic_base = @!italic_base0-font_base;
+ /*base addresses for italic corrections*/
+static int @!lig_kern_base0[font_max-font_base+1],
+ *const @!lig_kern_base = @!lig_kern_base0-font_base;
+ /*base addresses for ligature/kerning programs*/
+static int @!kern_base0[font_max-font_base+1],
+ *const @!kern_base = @!kern_base0-font_base;
+ /*base addresses for kerns*/
+static int @!exten_base0[font_max-font_base+1],
+ *const @!exten_base = @!exten_base0-font_base;
+ /*base addresses for extensible recipes*/
+static int @!param_base0[font_max-font_base+1],
+ *const @!param_base = @!param_base0-font_base;
+ /*base addresses for font parameters*/
+
+@ @<Set init...@>=
+for (k=font_base; k<=font_max; k++) font_used[k]=false;
+
+@ \TeX\ always knows at least one font, namely the null font. It has no
+characters, and its seven parameters are all equal to zero.
+
+@<Initialize table...@>=
+font_ptr=null_font;fmem_ptr=7;
+font_name[null_font]=s_no("nullfont");font_area[null_font]=empty_string;
+hyphen_char[null_font]='-';skew_char[null_font]=-1;
+bchar_label[null_font]=non_address;
+font_bchar[null_font]=non_char;font_false_bchar[null_font]=non_char;
+font_bc[null_font]=1;font_ec[null_font]=0;
+font_size[null_font]=0;font_dsize[null_font]=0;
+char_base[null_font]=0;width_base[null_font]=0;
+height_base[null_font]=0;depth_base[null_font]=0;
+italic_base[null_font]=0;lig_kern_base[null_font]=0;
+kern_base[null_font]=0;exten_base[null_font]=0;
+font_glue[null_font]=null;font_params[null_font]=7;
+param_base[null_font]=-1;
+for (k=0; k<=6; k++) font_info[k].sc=0;
+
+@ @<Put each...@>=
+primitive("nullfont", set_font, null_font);
+@!@:null\_font\_}{\.{\\nullfont} primitive@>
+text(frozen_null_font)=text(cur_val);eqtb[frozen_null_font]=eqtb[cur_val];
+
+@ Of course we want to define macros that suppress the detail of how font
+information is actually packed, so that we don't have to write things like
+$$\hbox{|font_info[width_base[f]+font_info[char_base[f]+c].qqqq.b0].sc|}$$
+too often. The \.{WEB} definitions here make |char_info(f)(c)| the
+|four_quarters| word of font information corresponding to character
+|c| of font |f|. If |q| is such a word, |char_width(f)(q)| will be
+the character's width; hence the long formula above is at least
+abbreviated to
+$$\hbox{|char_width(f)(char_info(f)(c))|.}$$
+Usually, of course, we will fetch |q| first and look at several of its
+fields at the same time.
+
+The italic correction of a character will be denoted by
+|char_italic(f)(q)|, so it is analogous to |char_width|. But we will get
+at the height and depth in a slightly different way, since we usually want
+to compute both height and depth if we want either one. The value of
+|height_depth(q)| will be the 8-bit quantity
+$$b=|height_index|\times16+|depth_index|,$$ and if |b| is such a byte we
+will write |char_height(f)(b)| and |char_depth(f)(b)| for the height and
+depth of the character |c| for which |q==char_info(f)(c)|. Got that?
+
+The tag field will be called |char_tag(q)|; the remainder byte will be
+called |rem_byte(q)|, using a macro that we have already defined above.
+
+Access to a character's |width|, |height|, |depth|, and |tag| fields is
+part of \TeX's inner loop, so we want these macros to produce code that is
+as fast as possible under the circumstances.
+@^inner loop@>
+
+@d char_info(A, B) font_info[char_base[A]+B].qqqq
+@d char_width(A, B) font_info[width_base[A]+B.b0].sc
+@d char_exists(A) (A.b0 > min_quarterword)
+@d char_italic(A, B) font_info[italic_base[A]+(qo(B.b2))/4].sc
+@d height_depth(A) qo(A.b1)
+@d char_height(A, B) font_info[height_base[A]+(B)/16].sc
+@d char_depth(A, B) font_info[depth_base[A]+(B)%16].sc
+@d char_tag(A) ((qo(A.b2))%4)
+
+@ The global variable |null_character| is set up to be a word of
+|char_info| for a character that doesn't exist. Such a word provides a
+convenient way to deal with erroneous situations.
+
+@<Glob...@>=
+static four_quarters @!null_character; /*nonexistent character information*/
+
+@ @<Set init...@>=
+null_character.b0=min_quarterword;null_character.b1=min_quarterword;
+null_character.b2=min_quarterword;null_character.b3=min_quarterword;
+
+@ Here are some macros that help process ligatures and kerns.
+We write |char_kern(f)(j)| to find the amount of kerning specified by
+kerning command~|j| in font~|f|. If |j| is the |char_info| for a character
+with a ligature/kern program, the first instruction of that program is either
+|i==font_info[lig_kern_start(f)(j)]| or |font_info[lig_kern_restart(f)(i)]|,
+depending on whether or not |skip_byte(i) <= stop_flag|.
+
+The constant |kern_base_offset| should be simplified, for \PASCAL\ compilers
+that do not do local optimization.
+@^system dependencies@>
+
+@d char_kern(A, B) font_info[kern_base[A]+256*op_byte(B)+rem_byte(B)].sc
+@d kern_base_offset 256*(128+min_quarterword)
+@d lig_kern_start(A, B) lig_kern_base[A]+B.b3 /*beginning of lig/kern program*/
+@d lig_kern_restart(A, B) lig_kern_base[A]+256*op_byte(B)+rem_byte(B)+32768-kern_base_offset
+
+@ Font parameters are referred to as |slant(f)|, |space(f)|, etc.
+
+@d param_end(A) param_base[A]].sc
+@d param(A) font_info[A+param_end
+@d slant param(slant_code) /*slant to the right, per unit distance upward*/
+@d space param(space_code) /*normal space between words*/
+@d space_stretch param(space_stretch_code) /*stretch between words*/
+@d space_shrink param(space_shrink_code) /*shrink between words*/
+@d x_height param(x_height_code) /*one ex*/
+@d quad param(quad_code) /*one em*/
+@d extra_space param(extra_space_code) /*additional space at end of sentence*/
+
+@<The em width for |cur_font|@>=quad(cur_font)
+
+@ @<The x-height for |cur_font|@>=x_height(cur_font)
+
+@ \TeX\ checks the information of a \.{TFM} file for validity as the
+file is being read in, so that no further checks will be needed when
+typesetting is going on. The somewhat tedious subroutine that does this
+is called |read_font_info|. It has four parameters: the user font
+identifier~|u|, the file name and area strings |nom| and |aire|, and the
+``at'' size~|s|. If |s|~is negative, it's the negative of a scale factor
+to be applied to the design size; |s==-1000| is the normal case.
+Otherwise |s| will be substituted for the design size; in this
+case, |s| must be positive and less than $2048\rm\,pt$
+(i.e., it must be less than $2^{27}$ when considered as an integer).
+
+The subroutine opens and closes a global file variable called |tfm_file|.
+It returns the value of the internal font number that was just loaded.
+If an error is detected, an error message is issued and no font
+information is stored; |null_font| is returned in this case.
+
+@d abort goto bad_tfm /*do this when the \.{TFM} data is wrong*/
+
+@p static internal_font_number read_font_info(pointer @!u, str_number @!nom, str_number @!aire,
+ scaled @!s) /*input a \.{TFM} file*/
+{@+
+int k; /*index into |font_info|*/
+bool @!file_opened; /*was |tfm_file| successfully opened?*/
+halfword @!lf, @!lh, @!bc, @!ec, @!nw, @!nh, @!nd, @!ni, @!nl, @!nk, @!ne, @!np;
+ /*sizes of subfiles*/
+internal_font_number @!f; /*the new font's number*/
+internal_font_number @!g; /*the number to return*/
+eight_bits @!a, @!b, @!c, @!d; /*byte variables*/
+four_quarters @!qw;scaled @!sw; /*accumulators*/
+int @!bch_label; /*left boundary start location, or infinity*/
+int @!bchar; /*boundary character, or 256*/
+scaled @!z; /*the design size or the ``at'' size*/
+int @!alpha;int @!beta;
+ /*auxiliary quantities used in fixed-point multiplication*/
+g=null_font;@/
+@<Read and check the font data; |abort| if the \.{TFM} file is malformed;
+if there's no room for this font, say so and |goto done|; otherwise |incr(font_ptr)|
+and |goto done|@>;
+bad_tfm: @<Report that the font won't be loaded@>;
+done: if (file_opened) b_close(&tfm_file);
+return g;
+}
+
+@ There are programs called \.{TFtoPL} and \.{PLtoTF} that convert
+between the \.{TFM} format and a symbolic property-list format
+that can be easily edited. These programs contain extensive
+diagnostic information, so \TeX\ does not have to bother giving
+precise details about why it rejects a particular \.{TFM} file.
+@.TFtoPL@> @.PLtoTF@>
+
+@d start_font_error_message print_err("Font ");sprint_cs(u);
+ print_char('=');print_file_name(nom, aire, empty_string);
+ if (s >= 0)
+ {@+print(" at ");print_scaled(s);print("pt");
+ }
+ else if (s!=-1000)
+ {@+print(" scaled ");print_int(-s);
+ }
+
+@<Report that the font won't be loaded@>=
+start_font_error_message;
+@.Font x=xx not loadable...@>
+if (file_opened) print(" not loadable: Bad metric (TFM) file");
+else print(" not loadable: Metric (TFM) file not found");
+help5("I wasn't able to read the size data for this font,",@/
+"so I will ignore the font specification.",@/
+"[Wizards can fix TFM files using TFtoPL/PLtoTF.]",@/
+"You might try inserting a different font spec;",@/
+"e.g., type `I\\font<same font id>=<substitute font name>'.");
+error()
+
+@ @<Read and check...@>=
+@<Open |tfm_file| for input@>;
+@<Read the {\.{TFM}} size fields@>;
+@<Use size fields to allocate font information@>;
+@<Read the {\.{TFM}} header@>;
+@<Read character data@>;
+@<Read box dimensions@>;
+@<Read ligature/kern program@>;
+@<Read extensible character recipes@>;
+@<Read font parameters@>;
+@<Make final adjustments and |goto done|@>@;
+
+@ @<Open |tfm_file| for input@>=
+file_opened=false;
+pack_file_name(nom, empty_string,empty_string,".tfm"); /* \TeX\ Live */
+if (!b_open_in(&tfm_file)) abort;
+file_opened=true
+
+@ Note: A malformed \.{TFM} file might be shorter than it claims to be;
+thus |eof(tfm_file)| might be true when |read_font_info| refers to
+|tfm_file.d| or when it says |get(tfm_file)|. If such circumstances
+cause system error messages, you will have to defeat them somehow,
+for example by defining |fget| to be `\ignorespaces|{@+get(tfm_file);|
+|if (eof(tfm_file)) abort;} |\unskip'.
+@^system dependencies@>
+
+@d fget get(tfm_file)
+@d fbyte tfm_file.d
+@d read_sixteen(A) {@+A=fbyte;
+ if (A > 127) abort;
+ fget;A=A*0400+fbyte;
+ }
+@d store_four_quarters(A) {@+fget;a=fbyte;qw.b0=qi(a);
+ fget;b=fbyte;qw.b1=qi(b);
+ fget;c=fbyte;qw.b2=qi(c);
+ fget;d=fbyte;qw.b3=qi(d);
+ A=qw;
+ }
+
+@ @<Read the {\.{TFM}} size fields@>=
+{@+read_sixteen(lf);
+fget;read_sixteen(lh);
+fget;read_sixteen(bc);
+fget;read_sixteen(ec);
+if ((bc > ec+1)||(ec > 255)) abort;
+if (bc > 255) /*|bc==256| and |ec==255|*/
+ {@+bc=1;ec=0;
+ }
+fget;read_sixteen(nw);
+fget;read_sixteen(nh);
+fget;read_sixteen(nd);
+fget;read_sixteen(ni);
+fget;read_sixteen(nl);
+fget;read_sixteen(nk);
+fget;read_sixteen(ne);
+fget;read_sixteen(np);
+if (lf!=6+lh+(ec-bc+1)+nw+nh+nd+ni+nl+nk+ne+np) abort;
+if ((nw==0)||(nh==0)||(nd==0)||(ni==0)) abort;
+}
+
+@ The preliminary settings of the index-offset variables |char_base|,
+|width_base|, |lig_kern_base|, |kern_base|, and |exten_base| will be
+corrected later by subtracting |min_quarterword| from them; and we will
+subtract 1 from |param_base| too. It's best to forget about such anomalies
+until later.
+
+@<Use size fields to allocate font information@>=
+lf=lf-6-lh; /*|lf| words should be loaded into |font_info|*/
+if (np < 7) lf=lf+7-np; /*at least seven parameters will appear*/
+if ((font_ptr==font_max)||(fmem_ptr+lf > font_mem_size))
+ @<Apologize for not loading the font, |goto done|@>;
+f=font_ptr+1;
+char_base[f]=fmem_ptr-bc;
+width_base[f]=char_base[f]+ec+1;
+height_base[f]=width_base[f]+nw;
+depth_base[f]=height_base[f]+nh;
+italic_base[f]=depth_base[f]+nd;
+lig_kern_base[f]=italic_base[f]+ni;
+kern_base[f]=lig_kern_base[f]+nl-kern_base_offset;
+exten_base[f]=kern_base[f]+kern_base_offset+nk;
+param_base[f]=exten_base[f]+ne
+
+@ @<Apologize for not loading...@>=
+{@+start_font_error_message;
+print(" not loaded: Not enough room left");
+@.Font x=xx not loaded...@>
+help4("I'm afraid I won't be able to make use of this font,",@/
+"because my memory for character-size data is too small.",@/
+"If you're really stuck, ask a wizard to enlarge me.",@/
+"Or maybe try `I\\font<same font id>=<name of loaded font>'.");
+error();goto done;
+}
+
+@ Only the first two words of the header are needed by \TeX82.
+
+@<Read the {\.{TFM}} header@>=
+{@+if (lh < 2) abort;
+store_four_quarters(font_check[f]);
+fget;read_sixteen(z); /*this rejects a negative design size*/
+fget;z=z*0400+fbyte;fget;z=(z*020)+(fbyte/020);
+if (z < unity) abort;
+while (lh > 2)
+ {@+fget;fget;fget;fget;decr(lh); /*ignore the rest of the header*/
+ }
+font_dsize[f]=z;
+if (s!=-1000)
+ if (s >= 0) z=s;
+ else z=xn_over_d(z,-s, 1000);
+font_size[f]=z;
+}
+
+@ @<Read character data@>=
+for (k=fmem_ptr; k<=width_base[f]-1; k++)
+ {@+store_four_quarters(font_info[k].qqqq);
+ if ((a >= nw)||(b/020 >= nh)||(b%020 >= nd)||
+ (c/4 >= ni)) abort;
+ switch (c%4) {
+ case lig_tag: if (d >= nl) abort;@+break;
+ case ext_tag: if (d >= ne) abort;@+break;
+ case list_tag: @<Check for charlist cycle@>@;@+break;
+ default:do_nothing; /*|no_tag|*/
+ }
+ }
+
+@ We want to make sure that there is no cycle of characters linked together
+by |list_tag| entries, since such a cycle would get \TeX\ into an endless
+loop. If such a cycle exists, the routine here detects it when processing
+the largest character code in the cycle.
+
+@d check_byte_range(A) {@+if ((A < bc)||(A > ec)) abort;@+}
+@d current_character_being_worked_on k+bc-fmem_ptr
+
+@<Check for charlist cycle@>=
+{@+check_byte_range(d);
+while (d < current_character_being_worked_on)
+ {@+qw=char_info(f, d);
+ /*N.B.: not |qi(d)|, since |char_base[f]| hasn't been adjusted yet*/
+ if (char_tag(qw)!=list_tag) goto not_found;
+ d=qo(rem_byte(qw)); /*next character on the list*/
+ }
+if (d==current_character_being_worked_on) abort; /*yes, there's a cycle*/
+not_found: ;}
+
+@ A |fix_word| whose four bytes are $(a,b,c,d)$ from left to right represents
+the number
+$$x=\left\{\vcenter{\halign{$#$,\hfil\qquad&if $#$\hfil\cr
+b\cdot2^{-4}+c\cdot2^{-12}+d\cdot2^{-20}&a=0;\cr
+-16+b\cdot2^{-4}+c\cdot2^{-12}+d\cdot2^{-20}&a=255.\cr}}\right.$$
+(No other choices of |a| are allowed, since the magnitude of a number in
+design-size units must be less than 16.) We want to multiply this
+quantity by the integer~|z|, which is known to be less than $2^{27}$.
+If $|z|<2^{23}$, the individual multiplications $b\cdot z$,
+$c\cdot z$, $d\cdot z$ cannot overflow; otherwise we will divide |z| by 2,
+4, 8, or 16, to obtain a multiplier less than $2^{23}$, and we can
+compensate for this later. If |z| has thereby been replaced by
+$|z|^\prime=|z|/2^e$, let $\beta=2^{4-e}$; we shall compute
+$$\lfloor(b+c\cdot2^{-8}+d\cdot2^{-16})\,z^\prime/\beta\rfloor$$
+if $a=0$, or the same quantity minus $\alpha=2^{4+e}z^\prime$ if $a=255$.
+This calculation must be done exactly, in order to guarantee portability
+of \TeX\ between computers.
+
+@d store_scaled(A) {@+fget;a=fbyte;fget;b=fbyte;
+ fget;c=fbyte;fget;d=fbyte;@/
+ sw=(((((d*z)/0400)+(c*z))/0400)+(b*z))/beta;
+ if (a==0) A=sw;@+else if (a==255) A=sw-alpha;@+else abort;
+ }
+
+@<Read box dimensions@>=
+{@+@<Replace |z| by $|z|^\prime$ and compute $\alpha,\beta$@>;
+for (k=width_base[f]; k<=lig_kern_base[f]-1; k++)
+ store_scaled(font_info[k].sc);
+if (font_info[width_base[f]].sc!=0) abort; /*\\{width}[0] must be zero*/
+if (font_info[height_base[f]].sc!=0) abort; /*\\{height}[0] must be zero*/
+if (font_info[depth_base[f]].sc!=0) abort; /*\\{depth}[0] must be zero*/
+if (font_info[italic_base[f]].sc!=0) abort; /*\\{italic}[0] must be zero*/
+}
+
+@ @<Replace |z|...@>=
+{@+alpha=16;
+while (z >= 040000000)
+ {@+z=z/2;alpha=alpha+alpha;
+ }
+beta=256/alpha;alpha=alpha*z;
+}
+
+@ @d check_existence(A) @t@>@;@/
+ {@+check_byte_range(A);
+ qw=char_info(f, A); /*N.B.: not |qi(A)|*/
+ if (!char_exists(qw)) abort;
+ }
+
+@<Read ligature/kern program@>=
+bch_label=077777;bchar=256;
+if (nl > 0)
+ {@+for (k=lig_kern_base[f]; k<=kern_base[f]+kern_base_offset-1; k++)
+ {@+store_four_quarters(font_info[k].qqqq);
+ if (a > 128)
+ {@+if (256*c+d >= nl) abort;
+ if (a==255) if (k==lig_kern_base[f]) bchar=b;
+ }
+ else{@+if (b!=bchar) check_existence(b);
+ if (c < 128) check_existence(d)@; /*check ligature*/
+ else if (256*(c-128)+d >= nk) abort; /*check kern*/
+ if (a < 128) if (k-lig_kern_base[f]+a+1 >= nl) abort;
+ }
+ }
+ if (a==255) bch_label=256*c+d;
+ }
+for (k=kern_base[f]+kern_base_offset; k<=exten_base[f]-1; k++)
+ store_scaled(font_info[k].sc);
+
+@ @<Read extensible character recipes@>=
+for (k=exten_base[f]; k<=param_base[f]-1; k++)
+ {@+store_four_quarters(font_info[k].qqqq);
+ if (a!=0) check_existence(a);
+ if (b!=0) check_existence(b);
+ if (c!=0) check_existence(c);
+ check_existence(d);
+ }
+
+@ We check to see that the \.{TFM} file doesn't end prematurely; but
+no error message is given for files having more than |lf| words.
+
+@<Read font parameters@>=
+{@+for (k=1; k<=np; k++)
+ if (k==1) /*the |slant| parameter is a pure number*/
+ {@+fget;sw=fbyte;if (sw > 127) sw=sw-256;
+ fget;sw=sw*0400+fbyte;fget;sw=sw*0400+fbyte;
+ fget;font_info[param_base[f]].sc=
+ (sw*020)+(fbyte/020);
+ }
+ else store_scaled(font_info[param_base[f]+k-1].sc);
+if (eof(tfm_file)) abort;
+for (k=np+1; k<=7; k++) font_info[param_base[f]+k-1].sc=0;
+}
+
+@ Now to wrap it up, we have checked all the necessary things about the \.{TFM}
+file, and all we need to do is put the finishing touches on the data for
+the new font.
+
+@d adjust(A) A[f]=qo(A[f])
+ /*correct for the excess |min_quarterword| that was added*/
+
+@<Make final adjustments...@>=
+if (np >= 7) font_params[f]=np;@+else font_params[f]=7;
+hyphen_char[f]=default_hyphen_char;skew_char[f]=default_skew_char;
+if (bch_label < nl) bchar_label[f]=bch_label+lig_kern_base[f];
+else bchar_label[f]=non_address;
+font_bchar[f]=qi(bchar);
+font_false_bchar[f]=qi(bchar);
+if (bchar <= ec) if (bchar >= bc)
+ {@+qw=char_info(f, bchar); /*N.B.: not |qi(bchar)|*/
+ if (char_exists(qw)) font_false_bchar[f]=non_char;
+ }
+font_name[f]=nom;
+font_area[f]=aire;
+font_bc[f]=bc;font_ec[f]=ec;font_glue[f]=null;
+adjust(char_base);adjust(width_base);adjust(lig_kern_base);
+adjust(kern_base);adjust(exten_base);
+decr(param_base[f]);
+fmem_ptr=fmem_ptr+lf;font_ptr=f;g=f;goto done
+
+@ Before we forget about the format of these tables, let's deal with two
+of \TeX's basic scanning routines related to font information.
+
+@<Declare procedures that scan font-related stuff@>=
+static void scan_font_ident(void)
+{@+internal_font_number f;
+halfword @!m;
+@<Get the next non-blank non-call...@>;
+if (cur_cmd==def_font) f=cur_font;
+else if (cur_cmd==set_font) f=cur_chr;
+else if (cur_cmd==def_family)
+ {@+m=cur_chr;scan_four_bit_int();f=equiv(m+cur_val);
+ }
+else{@+print_err("Missing font identifier");
+@.Missing font identifier@>
+ help2("I was looking for a control sequence whose",@/
+ "current meaning has been defined by \\font.");
+ back_error();f=null_font;
+ }
+cur_val=f;
+}
+
+@ The following routine is used to implement `\.{\\fontdimen} |n| |f|'.
+The boolean parameter |writing| is set |true| if the calling program
+intends to change the parameter value.
+
+@<Declare procedures that scan font-related stuff@>=
+static void find_font_dimen(bool @!writing)
+ /*sets |cur_val| to |font_info| location*/
+{@+internal_font_number f;
+int @!n; /*the parameter number*/
+scan_int();n=cur_val;scan_font_ident();f=cur_val;
+if (n <= 0) cur_val=fmem_ptr;
+else{@+if (writing&&(n <= space_shrink_code)&&@|
+ (n >= space_code)&&(font_glue[f]!=null))
+ {@+delete_glue_ref(font_glue[f]);
+ font_glue[f]=null;
+ }
+ if (n > font_params[f])
+ if (f < font_ptr) cur_val=fmem_ptr;
+ else@<Increase the number of parameters in the last font@>@;
+ else cur_val=n+param_base[f];
+ }
+@<Issue an error message if |cur_val=fmem_ptr|@>;
+}
+
+@ @<Issue an error message if |cur_val=fmem_ptr|@>=
+if (cur_val==fmem_ptr)
+ {@+print_err("Font ");printn_esc(font_id_text(f));
+ print(" has only ");print_int(font_params[f]);
+ print(" fontdimen parameters");
+@.Font x has only...@>
+ help2("To increase the number of font parameters, you must",@/
+ "use \\fontdimen immediately after the \\font is loaded.");
+ error();
+ }
+
+@ @<Increase the number of parameters...@>=
+{@+@/do@+{if (fmem_ptr==font_mem_size)
+ overflow("font memory", font_mem_size);
+@:TeX capacity exceeded font memory}{\quad font memory@>
+font_info[fmem_ptr].sc=0;incr(fmem_ptr);incr(font_params[f]);
+}@+ while (!(n==font_params[f]));
+cur_val=fmem_ptr-1; /*this equals |param_base[f]+font_params[f]|*/
+}
+
+@ When \TeX\ wants to typeset a character that doesn't exist, the
+character node is not created; thus the output routine can assume
+that characters exist when it sees them. The following procedure
+prints a warning message unless the user has suppressed it.
+
+@p static void char_warning(internal_font_number @!f, eight_bits @!c)
+{@+int old_setting; /*saved value of |tracing_online|*/
+if (tracing_lost_chars > 0)
+ {@+old_setting=tracing_online;
+ if (eTeX_ex&&(tracing_lost_chars > 1)) tracing_online=1;
+ {@+begin_diagnostic();
+ print_nl("Missing character: There is no ");
+@.Missing character@>
+ print_ASCII(c);print(" in font ");
+ slow_print(font_name[f]);print_char('!');end_diagnostic(false);
+ }
+ tracing_online=old_setting;
+ }
+}
+
+@ Here is a function that returns a pointer to a character node for a
+given character in a given font. If that character doesn't exist,
+|null| is returned instead.
+
+@p static pointer new_character(internal_font_number @!f, eight_bits @!c)
+{@+
+pointer p; /*newly allocated node*/
+if (font_bc[f] <= c) if (font_ec[f] >= c)
+ if (char_exists(char_info(f, qi(c))))
+ {@+p=get_avail();font(p)=f;character(p)=qi(c);
+ return p;
+ }
+char_warning(f, c);
+return null;
+}
+
+@* Device-independent file format.
+The most important output produced by a run of \TeX\ is the ``device
+independent'' (\.{DVI}) file that specifies where characters and rules
+are to appear on printed pages. The form of these files was designed by
+David R. Fuchs in 1979. Almost any reasonable typesetting device can be
+@^Fuchs, David Raymond@>
+@:DVI\_files}{\.{DVI} files@>
+driven by a program that takes \.{DVI} files as input, and dozens of such
+\.{DVI}-to-whatever programs have been written. Thus, it is possible to
+print the output of \TeX\ on many different kinds of equipment, using \TeX\
+as a device-independent ``front end.''
+
+A \.{DVI} file is a stream of 8-bit bytes, which may be regarded as a
+series of commands in a machine-like language. The first byte of each command
+is the operation code, and this code is followed by zero or more bytes
+that provide parameters to the command. The parameters themselves may consist
+of several consecutive bytes; for example, the `|set_rule|' command has two
+parameters, each of which is four bytes long. Parameters are usually
+regarded as nonnegative integers; but four-byte-long parameters,
+and shorter parameters that denote distances, can be
+either positive or negative. Such parameters are given in two's complement
+notation. For example, a two-byte-long distance parameter has a value between
+$-2^{15}$ and $2^{15}-1$. As in \.{TFM} files, numbers that occupy
+more than one byte position appear in BigEndian order.
+
+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
+file; this must come first. Each ``page'' consists of a |bop| command,
+followed by any number of other commands that tell where characters are to
+be placed on a physical page, followed by an |eop| command. The pages
+appear in the order that \TeX\ generated them. If we ignore |nop| commands
+and \\{fnt\_def} commands (which are allowed between any two commands in
+the file), each |eop| command is immediately followed by a |bop| command,
+or by a |post| command; in the latter case, there are no more pages in the
+file, and the remaining bytes form the postamble. Further details about
+the postamble will be explained later.
+
+Some parameters in \.{DVI} commands are ``pointers.'' These are four-byte
+quantities that give the location number of some other byte in the file;
+the first byte is number~0, then comes number~1, and so on. For example,
+one of the parameters of a |bop| command points to the previous |bop|;
+this makes it feasible to read the pages in backwards order, in case the
+results are being directed to a device that stacks its output face up.
+Suppose the preamble of a \.{DVI} file occupies bytes 0 to 99. Now if the
+first page occupies bytes 100 to 999, say, and if the second
+page occupies bytes 1000 to 1999, then the |bop| that starts in byte 1000
+points to 100 and the |bop| that starts in byte 2000 points to 1000. (The
+very first |bop|, i.e., the one starting in byte 100, has a pointer of~$-1$.)
+
+@ The \.{DVI} format is intended to be both compact and easily interpreted
+by a machine. Compactness is achieved by making most of the information
+implicit instead of explicit. When a \.{DVI}-reading program reads the
+commands for a page, it keeps track of several quantities: (a)~The current
+font |f| is an integer; this value is changed only
+by \\{fnt} and \\{fnt\_num} commands. (b)~The current position on the page
+is given by two numbers called the horizontal and vertical coordinates,
+|h| and |v|. Both coordinates are zero at the upper left corner of the page;
+moving to the right corresponds to increasing the horizontal coordinate, and
+moving down corresponds to increasing the vertical coordinate. Thus, the
+coordinates are essentially Cartesian, except that vertical directions are
+flipped; the Cartesian version of |(h, v)| would be |(h,-v)|. (c)~The
+current spacing amounts are given by four numbers |w|, |x|, |y|, and |z|,
+where |w| and~|x| are used for horizontal spacing and where |y| and~|z|
+are used for vertical spacing. (d)~There is a stack containing
+|(h, v, w, x, y, z)| values; the \.{DVI} commands |push| and |pop| are used to
+change the current level of operation. Note that the current font~|f| is
+not pushed and popped; the stack contains only information about
+positioning.
+
+The values of |h|, |v|, |w|, |x|, |y|, and |z| are signed integers having up
+to 32 bits, including the sign. Since they represent physical distances,
+there is a small unit of measurement such that increasing |h| by~1 means
+moving a certain tiny distance to the right. The actual unit of
+measurement is variable, as explained below; \TeX\ sets things up so that
+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
+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,
+`|p[4]|' means that parameter |p| is four bytes long.
+
+\yskip\hang|set_char_0| 0. Typeset character number~0 from font~|f|
+such that the reference point of the character is at |(h, v)|. Then
+increase |h| by the width of that character. Note that a character may
+have zero or negative width, so one cannot be sure that |h| will advance
+after this command; but |h| usually does increase.
+
+\yskip\hang\\{set\_char\_1} through \\{set\_char\_127} (opcodes 1 to 127).
+Do the operations of |set_char_0|; but use the character whose number
+matches the opcode, instead of character~0.
+
+\yskip\hang|set1| 128 |c[1]|. Same as |set_char_0|, except that character
+number~|c| is typeset. \TeX82 uses this command for characters in the
+range |128 <= c < 256|.
+
+\yskip\hang|@!set2| 129 |c[2]|. Same as |set1|, except that |c|~is two
+bytes long, so it is in the range |0 <= c < 65536|. \TeX82 never uses this
+command, but it should come in handy for extensions of \TeX\ that deal
+with oriental languages.
+@^oriental characters@>@^Chinese characters@>@^Japanese characters@>
+
+\yskip\hang|@!set3| 130 |c[3]|. Same as |set1|, except that |c|~is three
+bytes long, so it can be as large as $2^{24}-1$. Not even the Chinese
+language has this many characters, but this command might prove useful
+in some yet unforeseen extension.
+
+\yskip\hang|@!set4| 131 |c[4]|. Same as |set1|, except that |c|~is four
+bytes long. Imagine that.
+
+\yskip\hang|set_rule| 132 |a[4]| |b[4]|. Typeset a solid black rectangle
+of height~|a| and width~|b|, with its bottom left corner at |(h, v)|. Then
+set |h=h+b|. If either |a <= 0| or |b <= 0|, nothing should be typeset. Note
+that if |b < 0|, the value of |h| will decrease even though nothing else happens.
+See below for details about how to typeset rules so that consistency with
+\MF\ is guaranteed.
+
+\yskip\hang|@!put1| 133 |c[1]|. Typeset character number~|c| from font~|f|
+such that the reference point of the character is at |(h, v)|. (The `put'
+commands are exactly like the `set' commands, except that they simply put out a
+character or a rule without moving the reference point afterwards.)
+
+\yskip\hang|@!put2| 134 |c[2]|. Same as |set2|, except that |h| is not changed.
+
+\yskip\hang|@!put3| 135 |c[3]|. Same as |set3|, except that |h| is not changed.
+
+\yskip\hang|@!put4| 136 |c[4]|. Same as |set4|, except that |h| is not changed.
+
+\yskip\hang|put_rule| 137 |a[4]| |b[4]|. Same as |set_rule|, except that
+|h| is not changed.
+
+\yskip\hang|nop| 138. No operation, do nothing. Any number of |nop|'s
+may occur between \.{DVI} commands, but a |nop| cannot be inserted between
+a command and its parameters or between two parameters.
+
+\yskip\hang|bop| 139 $c_0[4]$ $c_1[4]$ $\ldots$ $c_9[4]$ $p[4]$. Beginning
+of a page: Set |(h, v, w, x, y, z)=(0, 0, 0, 0, 0, 0)| and set the stack empty. Set
+the current font |f| to an undefined value. The ten $c_i$ parameters hold
+the values of \.{\\count0} $\ldots$ \.{\\count9} in \TeX\ at the time
+\.{\\shipout} was invoked for this page; they can be used to identify
+pages, if a user wants to print only part of a \.{DVI} file. The parameter
+|p| points to the previous |bop| in the file; the first
+|bop| has $p=-1$.
+
+\yskip\hang|eop| 140. End of page: Print what you have read since the
+previous |bop|. At this point the stack should be empty. (The \.{DVI}-reading
+programs that drive most output devices will have kept a buffer of the
+material that appears on the page that has just ended. This material is
+largely, but not entirely, in order by |v| coordinate and (for fixed |v|) by
+|h|~coordinate; so it usually needs to be sorted into some order that is
+appropriate for the device in question.)
+
+\yskip\hang|push| 141. Push the current values of |(h, v, w, x, y, z)| onto the
+top of the stack; do not change any of these values. Note that |f| is
+not pushed.
+
+\yskip\hang|pop| 142. Pop the top six values off of the stack and assign
+them respectively to |(h, v, w, x, y, z)|. The number of pops should never
+exceed the number of pushes, since it would be highly embarrassing if the
+stack were empty at the time of a |pop| command.
+
+\yskip\hang|right1| 143 |b[1]|. Set |h=h+b|, i.e., move right |b| units.
+The parameter is a signed number in two's complement notation, |-128 <= b < 128|;
+if |b < 0|, the reference point moves left.
+
+\yskip\hang|@!right2| 144 |b[2]|. Same as |right1|, except that |b| is a
+two-byte quantity in the range |-32768 <= b < 32768|.
+
+\yskip\hang|@!right3| 145 |b[3]|. Same as |right1|, except that |b| is a
+three-byte quantity in the range |@t$-2^{23}$@> <= b < @t$2^{23}$@>|.
+
+\yskip\hang|@!right4| 146 |b[4]|. Same as |right1|, except that |b| is a
+four-byte quantity in the range |@t$-2^{31}$@> <= b < @t$2^{31}$@>|.
+
+\yskip\hang|w0| 147. Set |h=h+w|; i.e., move right |w| units. With luck,
+this parameterless command will usually suffice, because the same kind of motion
+will occur several times in succession; the following commands explain how
+|w| gets particular values.
+
+\yskip\hang|w1| 148 |b[1]|. Set |w=b| and |h=h+b|. The value of |b| is a
+signed quantity in two's complement notation, |-128 <= b < 128|. This command
+changes the current |w|~spacing and moves right by |b|.
+
+\yskip\hang|@!w2| 149 |b[2]|. Same as |w1|, but |b| is two bytes long,
+|-32768 <= b < 32768|.
+
+\yskip\hang|@!w3| 150 |b[3]|. Same as |w1|, but |b| is three bytes long,
+|@t$-2^{23}$@> <= b < @t$2^{23}$@>|.
+
+\yskip\hang|@!w4| 151 |b[4]|. Same as |w1|, but |b| is four bytes long,
+|@t$-2^{31}$@> <= b < @t$2^{31}$@>|.
+
+\yskip\hang|x0| 152. Set |h=h+x|; i.e., move right |x| units. The `|x|'
+commands are like the `|w|' commands except that they involve |x| instead
+of |w|.
+
+\yskip\hang|x1| 153 |b[1]|. Set |x=b| and |h=h+b|. The value of |b| is a
+signed quantity in two's complement notation, |-128 <= b < 128|. This command
+changes the current |x|~spacing and moves right by |b|.
+
+\yskip\hang|@!x2| 154 |b[2]|. Same as |x1|, but |b| is two bytes long,
+|-32768 <= b < 32768|.
+
+\yskip\hang|@!x3| 155 |b[3]|. Same as |x1|, but |b| is three bytes long,
+|@t$-2^{23}$@> <= b < @t$2^{23}$@>|.
+
+\yskip\hang|@!x4| 156 |b[4]|. Same as |x1|, but |b| is four bytes long,
+|@t$-2^{31}$@> <= b < @t$2^{31}$@>|.
+
+\yskip\hang|down1| 157 |a[1]|. Set |v=v+a|, i.e., move down |a| units.
+The parameter is a signed number in two's complement notation, |-128 <= a < 128|;
+if |a < 0|, the reference point moves up.
+
+\yskip\hang|@!down2| 158 |a[2]|. Same as |down1|, except that |a| is a
+two-byte quantity in the range |-32768 <= a < 32768|.
+
+\yskip\hang|@!down3| 159 |a[3]|. Same as |down1|, except that |a| is a
+three-byte quantity in the range |@t$-2^{23}$@> <= a < @t$2^{23}$@>|.
+
+\yskip\hang|@!down4| 160 |a[4]|. Same as |down1|, except that |a| is a
+four-byte quantity in the range |@t$-2^{31}$@> <= a < @t$2^{31}$@>|.
+
+\yskip\hang|y0| 161. Set |v=v+y|; i.e., move down |y| units. With luck,
+this parameterless command will usually suffice, because the same kind of motion
+will occur several times in succession; the following commands explain how
+|y| gets particular values.
+
+\yskip\hang|y1| 162 |a[1]|. Set |y=a| and |v=v+a|. The value of |a| is a
+signed quantity in two's complement notation, |-128 <= a < 128|. This command
+changes the current |y|~spacing and moves down by |a|.
+
+\yskip\hang|@!y2| 163 |a[2]|. Same as |y1|, but |a| is two bytes long,
+|-32768 <= a < 32768|.
+
+\yskip\hang|@!y3| 164 |a[3]|. Same as |y1|, but |a| is three bytes long,
+|@t$-2^{23}$@> <= a < @t$2^{23}$@>|.
+
+\yskip\hang|@!y4| 165 |a[4]|. Same as |y1|, but |a| is four bytes long,
+|@t$-2^{31}$@> <= a < @t$2^{31}$@>|.
+
+\yskip\hang|z0| 166. Set |v=v+z|; i.e., move down |z| units. The `|z|' commands
+are like the `|y|' commands except that they involve |z| instead of |y|.
+
+\yskip\hang|z1| 167 |a[1]|. Set |z=a| and |v=v+a|. The value of |a| is a
+signed quantity in two's complement notation, |-128 <= a < 128|. This command
+changes the current |z|~spacing and moves down by |a|.
+
+\yskip\hang|@!z2| 168 |a[2]|. Same as |z1|, but |a| is two bytes long,
+|-32768 <= a < 32768|.
+
+\yskip\hang|@!z3| 169 |a[3]|. Same as |z1|, but |a| is three bytes long,
+|@t$-2^{23}$@> <= a < @t$2^{23}$@>|.
+
+\yskip\hang|@!z4| 170 |a[4]|. Same as |z1|, but |a| is four bytes long,
+|@t$-2^{31}$@> <= a < @t$2^{31}$@>|.
+
+\yskip\hang|fnt_num_0| 171. Set |f=0|. Font 0 must previously have been
+defined by a \\{fnt\_def} instruction, as explained below.
+
+\yskip\hang\\{fnt\_num\_1} through \\{fnt\_num\_63} (opcodes 172 to 234). Set
+|f=1|, \dots, \hbox{|f=63|}, respectively.
+
+\yskip\hang|fnt1| 235 |k[1]|. Set |f=k|. \TeX82 uses this command for font
+numbers in the range |64 <= k < 256|.
+
+\yskip\hang|@!fnt2| 236 |k[2]|. Same as |fnt1|, except that |k|~is two
+bytes long, so it is in the range |0 <= k < 65536|. \TeX82 never generates this
+command, but large font numbers may prove useful for specifications of
+color or texture, or they may be used for special fonts that have fixed
+numbers in some external coding scheme.
+
+\yskip\hang|@!fnt3| 237 |k[3]|. Same as |fnt1|, except that |k|~is three
+bytes long, so it can be as large as $2^{24}-1$.
+
+\yskip\hang|@!fnt4| 238 |k[4]|. Same as |fnt1|, except that |k|~is four
+bytes long; this is for the really big font numbers (and for the negative ones).
+
+\yskip\hang|xxx1| 239 |k[1]| |x[k]|. This command is undefined in
+general; it functions as a $(k+2)$-byte |nop| unless special \.{DVI}-reading
+programs are being used. \TeX82 generates |xxx1| when a short enough
+\.{\\special} appears, setting |k| to the number of bytes being sent. It
+is recommended that |x| be a string having the form of a keyword followed
+by possible parameters relevant to that keyword.
+
+\yskip\hang|@!xxx2| 240 |k[2]| |x[k]|. Like |xxx1|, but |0 <= k < 65536|.
+
+\yskip\hang|@!xxx3| 241 |k[3]| |x[k]|. Like |xxx1|, but |0 <= k < @t$2^{24}$@>|.
+
+\yskip\hang|xxx4| 242 |k[4]| |x[k]|. Like |xxx1|, but |k| can be ridiculously
+large. \TeX82 uses |xxx4| when sending a string of length 256 or more.
+
+\yskip\hang|fnt_def1| 243 |k[1]| |c[4]| |s[4]| |d[4]| |a[1]| |l[1]| |n[a+l]|.
+Define font |k|, where |0 <= k < 256|; font definitions will be explained shortly.
+
+\yskip\hang|@!fnt_def2| 244 |k[2]| |c[4]| |s[4]| |d[4]| |a[1]| |l[1]| |n[a+l]|.
+Define font |k|, where |0 <= k < 65536|.
+
+\yskip\hang|@!fnt_def3| 245 |k[3]| |c[4]| |s[4]| |d[4]| |a[1]| |l[1]| |n[a+l]|.
+Define font |k|, where |0 <= k < @t$2^{24}$@>|.
+
+\yskip\hang|@!fnt_def4| 246 |k[4]| |c[4]| |s[4]| |d[4]| |a[1]| |l[1]| |n[a+l]|.
+Define font |k|, where |@t$-2^{31}$@> <= k < @t$2^{31}$@>|.
+
+\yskip\hang|pre| 247 |i[1]| |num[4]| |den[4]| |mag[4]| |k[1]| |x[k]|.
+Beginning of the preamble; this must come at the very beginning of the
+file. Parameters |i|, |num|, |den|, |mag|, |k|, and |x| are explained below.
+
+\yskip\hang|post| 248. Beginning of the postamble, see below.
+
+\yskip\hang|post_post| 249. Ending of the postamble, see below.
+
+\yskip\noindent Commands 250--255 are undefined at the present time.
+
+@ @d set_char_0 0 /*typeset character 0 and move right*/
+@d set1 128 /*typeset a character and move right*/
+@d set_rule 132 /*typeset a rule and move right*/
+@d put_rule 137 /*typeset a rule*/
+@d nop 138 /*no operation*/
+@d bop 139 /*beginning of page*/
+@d eop 140 /*ending of page*/
+@d push 141 /*save the current positions*/
+@d pop 142 /*restore previous positions*/
+@d right1 143 /*move right*/
+@d w0 147 /*move right by |w|*/
+@d w1 148 /*move right and set |w|*/
+@d x0 152 /*move right by |x|*/
+@d x1 153 /*move right and set |x|*/
+@d down1 157 /*move down*/
+@d y0 161 /*move down by |y|*/
+@d y1 162 /*move down and set |y|*/
+@d z0 166 /*move down by |z|*/
+@d z1 167 /*move down and set |z|*/
+@d fnt_num_0 171 /*set current font to 0*/
+@d fnt1 235 /*set current font*/
+@d xxx1 239 /*extension to \.{DVI} primitives*/
+@d xxx4 242 /*potentially long extension to \.{DVI} primitives*/
+@d fnt_def1 243 /*define the meaning of a font number*/
+@d pre 247 /*preamble*/
+@d post 248 /*postamble beginning*/
+@d post_post 249 /*postamble ending*/
+
+@ The preamble contains basic information about the file as a whole. As
+stated above, there are six parameters:
+$$\hbox{|@!i[1]| |@!num[4]| |@!den[4]| |@!mag[4]| |@!k[1]| |@!x[k]|.}$$
+The |i| byte identifies \.{DVI} format; currently this byte is always set
+to~2. (The value |i==3| is currently used for an extended format that
+allows a mixture of right-to-left and left-to-right typesetting.
+Some day we will set |i==4|, when \.{DVI} format makes another
+incompatible change---perhaps in the year 2048.)
+
+The next two parameters, |num| and |den|, are positive integers that define
+the units of measurement; they are the numerator and denominator of a
+fraction by which all dimensions in the \.{DVI} file could be multiplied
+in order to get lengths in units of $10^{-7}$ meters. Since $\rm 7227{pt} =
+254{cm}$, and since \TeX\ works with scaled points where there are $2^{16}$
+sp in a point, \TeX\ sets
+$|num|/|den|=(254\cdot10^5)/(7227\cdot2^{16})=25400000/473628672$.
+@^sp@>
+
+The |mag| parameter is what \TeX\ calls \.{\\mag}, i.e., 1000 times the
+desired magnification. The actual fraction by which dimensions are
+multiplied is therefore $|mag|\cdot|num|/1000|den|$. Note that if a \TeX\
+source document does not call for any `\.{true}' dimensions, and if you
+change it only by specifying a different \.{\\mag} setting, the \.{DVI}
+file that \TeX\ creates will be completely unchanged except for the value
+of |mag| in the preamble and postamble. (Fancy \.{DVI}-reading programs allow
+users to override the |mag|~setting when a \.{DVI} file is being printed.)
+
+Finally, |k| and |x| allow the \.{DVI} writer to include a comment, which is not
+interpreted further. The length of comment |x| is |k|, where |0 <= k < 256|.
+
+@d id_byte 2 /*identifies the kind of \.{DVI} files described here*/
+
+@ Font definitions for a given font number |k| contain further parameters
+$$\hbox{|c[4]| |s[4]| |d[4]| |a[1]| |l[1]| |n[a+l]|.}$$
+The four-byte value |c| is the check sum that \TeX\ found in the \.{TFM}
+file for this font; |c| should match the check sum of the font found by
+programs that read this \.{DVI} file.
+@^check sum@>
+
+Parameter |s| contains a fixed-point scale factor that is applied to
+the character widths in font |k|; font dimensions in \.{TFM} files and
+other font files are relative to this quantity, which is called the
+``at size'' elsewhere in this documentation. The value of |s| is
+always positive and less than $2^{27}$. It is given in the same units
+as the other \.{DVI} dimensions, i.e., in sp when \TeX82 has made the
+file. Parameter |d| is similar to |s|; it is the ``design size,'' and
+(like~|s|) it is given in \.{DVI} units. Thus, font |k| is to be used
+at $|mag|\cdot s/1000d$ times its normal size.
+
+The remaining part of a font definition gives the external name of the font,
+which is an ASCII string of length |a+l|. The number |a| is the length
+of the ``area'' or directory, and |l| is the length of the font name itself;
+the standard local system font area is supposed to be used when |a==0|.
+The |n| field contains the area in its first |a| bytes.
+
+Font definitions must appear before the first use of a particular font number.
+Once font |k| is defined, it must not be defined again; however, we
+shall see below that font definitions appear in the postamble as well as
+in the pages, so in this sense each font number is defined exactly twice,
+if at all. Like |nop| commands, font definitions can
+appear before the first |bop|, or between an |eop| and a |bop|.
+
+@ Sometimes it is desirable to make horizontal or vertical rules line up
+precisely with certain features in characters of a font. It is possible to
+guarantee the correct matching between \.{DVI} output and the characters
+generated by \MF\ by adhering to the following principles: (1)~The \MF\
+characters should be positioned so that a bottom edge or left edge that is
+supposed to line up with the bottom or left edge of a rule appears at the
+reference point, i.e., in row~0 and column~0 of the \MF\ raster. This
+ensures that the position of the rule will not be rounded differently when
+the pixel size is not a perfect multiple of the units of measurement in
+the \.{DVI} file. (2)~A typeset rule of height $a>0$ and width $b>0$
+should be equivalent to a \MF-generated character having black pixels in
+precisely those raster positions whose \MF\ coordinates satisfy
+|0 <= x < @t$\alpha$@>b| and |0 <= y < @t$\alpha$@>a|, where $\alpha$ is the number
+of pixels per \.{DVI} unit.
+@:METAFONT}{\MF@>
+@^alignment of rules with characters@>
+@^rules aligning with characters@>
+
+@ The last page in a \.{DVI} file is followed by `|post|'; this command
+introduces the postamble, which summarizes important facts that \TeX\ has
+accumulated about the file, making it possible to print subsets of the data
+with reasonable efficiency. The postamble has the form
+$$\vbox{\halign{\hbox{#\hfil}\cr
+ |post| |p[4]| |num[4]| |den[4]| |mag[4]| |l[4]| |u[4]| |s[2]| |t[2]|\cr
+ $\langle\,$font definitions$\,\rangle$\cr
+ |post_post| |q[4]| |i[1]| 223's$[{\G}4]$\cr}}$$
+Here |p| is a pointer to the final |bop| in the file. The next three
+parameters, |num|, |den|, and |mag|, are duplicates of the quantities that
+appeared in the preamble.
+
+Parameters |l| and |u| give respectively the height-plus-depth of the tallest
+page and the width of the widest page, in the same units as other dimensions
+of the file. These numbers might be used by a \.{DVI}-reading program to
+position individual ``pages'' on large sheets of film or paper; however,
+the standard convention for output on normal size paper is to position each
+page so that the upper left-hand corner is exactly one inch from the left
+and the top. Experience has shown that it is unwise to design \.{DVI}-to-printer
+software that attempts cleverly to center the output; a fixed position of
+the upper left corner is easiest for users to understand and to work with.
+Therefore |l| and~|u| are often ignored.
+
+Parameter |s| is the maximum stack depth (i.e., the largest excess of
+|push| commands over |pop| commands) needed to process this file. Then
+comes |t|, the total number of pages (|bop| commands) present.
+
+The postamble continues with font definitions, which are any number of
+\\{fnt\_def} commands as described above, possibly interspersed with |nop|
+commands. Each font number that is used in the \.{DVI} file must be defined
+exactly twice: Once before it is first selected by a \\{fnt} command, and once
+in the postamble.
+
+@ The last part of the postamble, following the |post_post| byte that
+signifies the end of the font definitions, contains |q|, a pointer to the
+|post| command that started the postamble. An identification byte, |i|,
+comes next; this currently equals~2, as in the preamble.
+
+The |i| byte is followed by four or more bytes that are all equal to
+the decimal number 223 (i.e., 0337 in octal). \TeX\ puts out four to seven of
+these trailing bytes, until the total length of the file is a multiple of
+four bytes, since this works out best on machines that pack four bytes per
+word; but any number of 223's is allowed, as long as there are at least four
+of them. In effect, 223 is a sort of signature that is added at the very end.
+@^Fuchs, David Raymond@>
+
+This curious way to finish off a \.{DVI} file makes it feasible for
+\.{DVI}-reading programs to find the postamble first, on most computers,
+even though \TeX\ wants to write the postamble last. Most operating
+systems permit random access to individual words or bytes of a file, so
+the \.{DVI} reader can start at the end and skip backwards over the 223's
+until finding the identification byte. Then it can back up four bytes, read
+|q|, and move to byte |q| of the file. This byte should, of course,
+contain the value 248 (|post|); now the postamble can be read, so the
+\.{DVI} reader can discover all the information needed for typesetting the
+pages. Note that it is also possible to skip through the \.{DVI} file at
+reasonably high speed to locate a particular page, if that proves
+desirable. This saves a lot of time, since \.{DVI} files used in production
+jobs tend to be large.
+
+Unfortunately, however, standard \PASCAL\ does not include the ability to
+@^system dependencies@>
+access a random position in a file, or even to determine the length of a file.
+Almost all systems nowadays provide the necessary capabilities, so \.{DVI}
+format has been designed to work most efficiently with modern operating systems.
+But if \.{DVI} files have to be processed under the restrictions of standard
+\PASCAL, one can simply read them from front to back, since the necessary
+header information is present in the preamble and in the font definitions.
+(The |l| and |u| and |s| and |t| parameters, which appear only in the
+postamble, are ``frills'' that are handy but not absolutely necessary.)
+
+@* Shipping pages out.
+After considering \TeX's eyes and stomach, we come now to the bowels.
+@^bowels@>
+
+The |ship_out| procedure is given a pointer to a box; its mission is
+to describe that box in \.{DVI} form, outputting a ``page'' to |dvi_file|.
+The \.{DVI} coordinates $(h,v)=(0,0)$ should correspond to the upper left
+corner of the box being shipped.
+
+Since boxes can be inside of boxes inside of boxes, the main work of
+|ship_out| is done by two mutually recursive routines, |hlist_out|
+and |vlist_out|, which traverse the hlists and vlists inside of horizontal
+and vertical boxes.
+
+As individual pages are being processed, we need to accumulate
+information about the entire set of pages, since such statistics must be
+reported in the postamble. The global variables |total_pages|, |max_v|,
+|max_h|, |max_push|, and |last_bop| are used to record this information.
+
+The variable |doing_leaders| is |true| while leaders are being output.
+The variable |dead_cycles| contains the number of times an output routine
+has been initiated since the last |ship_out|.
+
+A few additional global variables are also defined here for use in
+|vlist_out| and |hlist_out|. They could have been local variables, but
+that would waste stack space when boxes are deeply nested, since the
+values of these variables are not needed during recursive calls.
+@^recursion@>
+
+@<Glob...@>=
+static int @!total_pages; /*the number of pages that have been shipped out*/
+static scaled @!max_v; /*maximum height-plus-depth of pages shipped so far*/
+static scaled @!max_h; /*maximum width of pages shipped so far*/
+static int @!max_push; /*deepest nesting of |push| commands encountered so far*/
+static int @!last_bop; /*location of previous |bop| in the \.{DVI} output*/
+static int @!dead_cycles; /*recent outputs that didn't ship anything out*/
+static bool @!doing_leaders; /*are we inside a leader box?*/
+@#
+static quarterword @!c, @!f; /*character and font in current |char_node|*/
+static scaled @!rule_ht, @!rule_dp, @!rule_wd; /*size of current rule being output*/
+static pointer @!g; /*current glue specification*/
+static int @!lq, @!lr; /*quantities used in calculations for leaders*/
+
+@ @<Set init...@>=
+total_pages=0;max_v=0;max_h=0;max_push=0;last_bop=-1;
+doing_leaders=false;dead_cycles=0;cur_s=-1;
+
+@ The \.{DVI} bytes are output to a buffer instead of being written directly
+to the output file. This makes it possible to reduce the overhead of
+subroutine calls, thereby measurably speeding up the computation, since
+output of \.{DVI} bytes is part of \TeX's inner loop. And it has another
+advantage as well, since we can change instructions in the buffer in order to
+make the output more compact. For example, a `|down2|' command can be
+changed to a `|y2|', thereby making a subsequent `|y0|' command possible,
+saving two bytes.
+
+The output buffer is divided into two parts of equal size; the bytes found
+in |dvi_buf[0 dotdot half_buf-1]| constitute the first half, and those in
+|dvi_buf[half_buf dotdot dvi_buf_size-1]| constitute the second. The global
+variable |dvi_ptr| points to the position that will receive the next
+output byte. When |dvi_ptr| reaches |dvi_limit|, which is always equal
+to one of the two values |half_buf| or |dvi_buf_size|, the half buffer that
+is about to be invaded next is sent to the output and |dvi_limit| is
+changed to its other value. Thus, there is always at least a half buffer's
+worth of information present, except at the very beginning of the job.
+
+Bytes of the \.{DVI} file are numbered sequentially starting with 0;
+the next byte to be generated will be number |dvi_offset+dvi_ptr|.
+A byte is present in the buffer only if its number is | >= dvi_gone|.
+
+@<Types...@>=
+typedef int16_t dvi_index; /*an index into the output buffer*/
+
+@ Some systems may find it more efficient to make |dvi_buf| a ||
+array, since output of four bytes at once may be facilitated.
+@^system dependencies@>
+
+@<Glob...@>=
+static eight_bits @!dvi_buf[dvi_buf_size+1]; /*buffer for \.{DVI} output*/
+static dvi_index @!half_buf; /*half of |dvi_buf_size|*/
+static dvi_index @!dvi_limit; /*end of the current half buffer*/
+static dvi_index @!dvi_ptr; /*the next available buffer address*/
+static int @!dvi_offset; /*|dvi_buf_size| times the number of times the
+ output buffer has been fully emptied*/
+static int @!dvi_gone; /*the number of bytes already output to |dvi_file|*/
+
+@ Initially the buffer is all in one piece; we will output half of it only
+after it first fills up.
+
+@<Set init...@>=
+half_buf=dvi_buf_size/2;dvi_limit=dvi_buf_size;dvi_ptr=0;
+dvi_offset=0;dvi_gone=0;
+
+@ The actual output of |dvi_buf[a dotdot b]| to |dvi_file| is performed by calling
+|write_dvi(a, b)|. For best results, this procedure should be optimized to
+run as fast as possible on each particular system, since it is part of
+\TeX's inner loop. It is safe to assume that |a| and |b+1| will both be
+multiples of 4 when |write_dvi(a, b)| is called; therefore it is possible on
+many machines to use efficient methods to pack four bytes per word and to
+output an array of words with one system call.
+@^system dependencies@>
+@^inner loop@>
+@^defecation@>
+
+@p static void write_dvi(dvi_index @!a, dvi_index @!b)
+{@+int k;
+for (k=a; k<=b; k++) pascal_write(dvi_file, "%c", dvi_buf[k]);
+}
+
+@ To put a byte in the buffer without paying the cost of invoking a procedure
+each time, we use the macro |dvi_out|.
+
+@d dvi_out(A) @+{@+dvi_buf[dvi_ptr]=A;incr(dvi_ptr);
+ if (dvi_ptr==dvi_limit) dvi_swap();
+ }
+
+@p static void dvi_swap(void) /*outputs half of the buffer*/
+{@+if (dvi_limit==dvi_buf_size)
+ {@+write_dvi(0, half_buf-1);dvi_limit=half_buf;
+ dvi_offset=dvi_offset+dvi_buf_size;dvi_ptr=0;
+ }
+else{@+write_dvi(half_buf, dvi_buf_size-1);dvi_limit=dvi_buf_size;
+ }
+dvi_gone=dvi_gone+half_buf;
+}
+
+@ Here is how we clean out the buffer when \TeX\ is all through; |dvi_ptr|
+will be a multiple of~4.
+
+@<Empty the last bytes out of |dvi_buf|@>=
+if (dvi_limit==half_buf) write_dvi(half_buf, dvi_buf_size-1);
+if (dvi_ptr > 0) write_dvi(0, dvi_ptr-1)
+
+@ The |dvi_four| procedure outputs four bytes in two's complement notation,
+without risking arithmetic overflow.
+
+@p static void dvi_four(int @!x)
+{@+if (x >= 0) dvi_out(x/0100000000)@;
+else{@+x=x+010000000000;
+ x=x+010000000000;
+ dvi_out((x/0100000000)+128);
+ }
+x=x%0100000000;dvi_out(x/0200000);
+x=x%0200000;dvi_out(x/0400);
+dvi_out(x%0400);
+}
+
+@ A mild optimization of the output is performed by the |dvi_pop|
+routine, which issues a |pop| unless it is possible to cancel a
+`|push| |pop|' pair. The parameter to |dvi_pop| is the byte address
+following the old |push| that matches the new |pop|.
+
+@p static void dvi_pop(int @!l)
+{@+if ((l==dvi_offset+dvi_ptr)&&(dvi_ptr > 0)) decr(dvi_ptr);
+else dvi_out(pop);
+}
+
+@ Here's a procedure that outputs a font definition. Since \TeX82 uses at
+most 256 different fonts per job, |fnt_def1| is always used as the command code.
+
+@p static void dvi_font_def(internal_font_number @!f)
+{@+int k; /*index into |str_pool|*/
+dvi_out(fnt_def1);
+dvi_out(f-font_base-1);@/
+dvi_out(qo(font_check[f].b0));
+dvi_out(qo(font_check[f].b1));
+dvi_out(qo(font_check[f].b2));
+dvi_out(qo(font_check[f].b3));@/
+dvi_four(font_size[f]);
+dvi_four(font_dsize[f]);@/
+dvi_out(length(font_area[f]));
+dvi_out(length(font_name[f]));
+@<Output the font name whose internal number is |f|@>;
+}
+
+@ @<Output the font name whose internal number is |f|@>=
+for (k=str_start[font_area[f]]; k<=str_start[font_area[f]+1]-1; k++)
+ dvi_out(so(str_pool[k]));
+for (k=str_start[font_name[f]]; k<=str_start[font_name[f]+1]-1; k++)
+ dvi_out(so(str_pool[k]))
+
+@ Versions of \TeX\ intended for small computers might well choose to omit
+the ideas in the next few parts of this program, since it is not really
+necessary to optimize the \.{DVI} code by making use of the |w0|, |x0|,
+|y0|, and |z0| commands. Furthermore, the algorithm that we are about to
+describe does not pretend to give an optimum reduction in the length
+of the \.{DVI} code; after all, speed is more important than compactness.
+But the method is surprisingly effective, and it takes comparatively little
+time.
+
+We can best understand the basic idea by first considering a simpler problem
+that has the same essential characteristics. Given a sequence of digits,
+say $3\,1\,4\,1\,5\,9\,2\,6\,5\,3\,5\,8\,9$, we want to assign subscripts
+$d$, $y$, or $z$ to each digit so as to maximize the number of ``$y$-hits''
+and ``$z$-hits''; a $y$-hit is an instance of two appearances of the same
+digit with the subscript $y$, where no $y$'s intervene between the two
+appearances, and a $z$-hit is defined similarly. For example, the sequence
+above could be decorated with subscripts as follows:
+$$3_z\,1_y\,4_d\,1_y\,5_y\,9_d\,2_d\,6_d\,5_y\,3_z\,5_y\,8_d\,9_d.$$
+There are three $y$-hits ($1_y\ldots1_y$ and $5_y\ldots5_y\ldots5_y$) and
+one $z$-hit ($3_z\ldots3_z$); there are no $d$-hits, since the two appearances
+of $9_d$ have $d$'s between them, but we don't count $d$-hits so it doesn't
+matter how many there are. These subscripts are analogous to the \.{DVI}
+commands called \\{down}, $y$, and $z$, and the digits are analogous to
+different amounts of vertical motion; a $y$-hit or $z$-hit corresponds to
+the opportunity to use the one-byte commands |y0| or |z0| in a \.{DVI} file.
+
+\TeX's method of assigning subscripts works like this: Append a new digit,
+say $\delta$, to the right of the sequence. Now look back through the
+sequence until one of the following things happens: (a)~You see
+$\delta_y$ or $\delta_z$, and this was the first time you encountered a
+$y$ or $z$ subscript, respectively. Then assign $y$ or $z$ to the new
+$\delta$; you have scored a hit. (b)~You see $\delta_d$, and no $y$
+subscripts have been encountered so far during this search. Then change
+the previous $\delta_d$ to $\delta_y$ (this corresponds to changing a
+command in the output buffer), and assign $y$ to the new $\delta$; it's
+another hit. (c)~You see $\delta_d$, and a $y$ subscript has been seen
+but not a $z$. Change the previous $\delta_d$ to $\delta_z$ and assign
+$z$ to the new $\delta$. (d)~You encounter both $y$ and $z$ subscripts
+before encountering a suitable $\delta$, or you scan all the way to the
+front of the sequence. Assign $d$ to the new $\delta$; this assignment may
+be changed later.
+
+The subscripts $3_z\,1_y\,4_d\ldots\,$ in the example above were, in fact,
+produced by this procedure, as the reader can verify. (Go ahead and try it.)
+
+@ In order to implement such an idea, \TeX\ maintains a stack of pointers
+to the \\{down}, $y$, and $z$ commands that have been generated for the
+current page. And there is a similar stack for \\{right}, |w|, and |x|
+commands. These stacks are called the down stack and right stack, and their
+top elements are maintained in the variables |down_ptr| and |right_ptr|.
+
+Each entry in these stacks contains four fields: The |width| field is
+the amount of motion down or to the right; the |location| field is the
+byte number of the \.{DVI} command in question (including the appropriate
+|dvi_offset|); the |link| field points to the next item below this one
+on the stack; and the |info| field encodes the options for possible change
+in the \.{DVI} command.
+
+@d movement_node_size 3 /*number of words per entry in the down and right stacks*/
+@d location(A) mem[A+2].i /*\.{DVI} byte number for a movement command*/
+
+@<Glob...@>=
+static pointer @!down_ptr, @!right_ptr; /*heads of the down and right stacks*/
+
+@ @<Set init...@>=
+down_ptr=null;right_ptr=null;
+
+@ Here is a subroutine that produces a \.{DVI} command for some specified
+downward or rightward motion. It has two parameters: |w| is the amount
+of motion, and |o| is either |down1| or |right1|. We use the fact that
+the command codes have convenient arithmetic properties: |y1-down1==w1-right1|
+and |z1-down1==x1-right1|.
+
+@p static void movement(scaled @!w, eight_bits @!o)
+{@+
+small_number mstate; /*have we seen a |y| or |z|?*/
+pointer @!p, @!q; /*current and top nodes on the stack*/
+int @!k; /*index into |dvi_buf|, modulo |dvi_buf_size|*/
+q=get_node(movement_node_size); /*new node for the top of the stack*/
+width(q)=w;location(q)=dvi_offset+dvi_ptr;
+if (o==down1)
+ {@+link(q)=down_ptr;down_ptr=q;
+ }
+else{@+link(q)=right_ptr;right_ptr=q;
+ }
+@<Look at the other stack entries until deciding what sort of \.{DVI} command
+to generate; |goto found| if node |p| is a ``hit''@>;
+@<Generate a |down| or |right| command for |w| and |return|@>;
+found: @<Generate a |y0| or |z0| command in order to reuse a previous appearance
+of~|w|@>;
+}
+
+@ The |info| fields in the entries of the down stack or the right stack
+have six possible settings: |y_here| or |z_here| mean that the \.{DVI}
+command refers to |y| or |z|, respectively (or to |w| or |x|, in the
+case of horizontal motion); |yz_OK| means that the \.{DVI} command is
+\\{down} (or \\{right}) but can be changed to either |y| or |z| (or
+to either |w| or |x|); |y_OK| means that it is \\{down} and can be changed
+to |y| but not |z|; |z_OK| is similar; and |d_fixed| means it must stay
+\\{down}.
+
+The four settings |yz_OK|, |y_OK|, |z_OK|, |d_fixed| would not need to
+be distinguished from each other if we were simply solving the
+digit-subscripting problem mentioned above. But in \TeX's case there is
+a complication because of the nested structure of |push| and |pop|
+commands. Suppose we add parentheses to the digit-subscripting problem,
+redefining hits so that $\delta_y\ldots \delta_y$ is a hit if all $y$'s between
+the $\delta$'s are enclosed in properly nested parentheses, and if the
+parenthesis level of the right-hand $\delta_y$ is deeper than or equal to
+that of the left-hand one. Thus, `(' and `)' correspond to `|push|'
+and `|pop|'. Now if we want to assign a subscript to the final 1 in the
+sequence
+$$2_y\,7_d\,1_d\,(\,8_z\,2_y\,8_z\,)\,1$$
+we cannot change the previous $1_d$ to $1_y$, since that would invalidate
+the $2_y\ldots2_y$ hit. But we can change it to $1_z$, scoring a hit
+since the intervening $8_z$'s are enclosed in parentheses.
+
+The program below removes movement nodes that are introduced after a |push|,
+before it outputs the corresponding |pop|.
+
+@d y_here 1 /*|info| when the movement entry points to a |y| command*/
+@d z_here 2 /*|info| when the movement entry points to a |z| command*/
+@d yz_OK 3 /*|info| corresponding to an unconstrained \\{down} command*/
+@d y_OK 4 /*|info| corresponding to a \\{down} that can't become a |z|*/
+@d z_OK 5 /*|info| corresponding to a \\{down} that can't become a |y|*/
+@d d_fixed 6 /*|info| corresponding to a \\{down} that can't change*/
+
+@ When the |movement| procedure gets to the label |found|, the value of
+|info(p)| will be either |y_here| or |z_here|. If it is, say, |y_here|,
+the procedure generates a |y0| command (or a |w0| command), and marks
+all |info| fields between |q| and |p| so that |y| is not OK in that range.
+
+@<Generate a |y0| or |z0| command...@>=
+info(q)=info(p);
+if (info(q)==y_here)
+ {@+dvi_out(o+y0-down1); /*|y0| or |w0|*/
+ while (link(q)!=p)
+ {@+q=link(q);
+ switch (info(q)) {
+ case yz_OK: info(q)=z_OK;@+break;
+ case y_OK: info(q)=d_fixed;@+break;
+ default:do_nothing;
+ }
+ }
+ }
+else{@+dvi_out(o+z0-down1); /*|z0| or |x0|*/
+ while (link(q)!=p)
+ {@+q=link(q);
+ switch (info(q)) {
+ case yz_OK: info(q)=y_OK;@+break;
+ case z_OK: info(q)=d_fixed;@+break;
+ default:do_nothing;
+ }
+ }
+ }
+
+@ @<Generate a |down| or |right|...@>=
+info(q)=yz_OK;
+if (abs(w) >= 040000000)
+ {@+dvi_out(o+3); /*|down4| or |right4|*/
+ dvi_four(w);return;
+ }
+if (abs(w) >= 0100000)
+ {@+dvi_out(o+2); /*|down3| or |right3|*/
+ if (w < 0) w=w+0100000000;
+ dvi_out(w/0200000);w=w%0200000;goto label2;
+ }
+if (abs(w) >= 0200)
+ {@+dvi_out(o+1); /*|down2| or |right2|*/
+ if (w < 0) w=w+0200000;
+ goto label2;
+ }
+dvi_out(o); /*|down1| or |right1|*/
+if (w < 0) w=w+0400;
+goto label1;
+label2: dvi_out(w/0400);
+label1: dvi_out(w%0400);return
+
+@ As we search through the stack, we are in one of three states,
+|y_seen|, |z_seen|, or |none_seen|, depending on whether we have
+encountered |y_here| or |z_here| nodes. These states are encoded as
+multiples of 6, so that they can be added to the |info| fields for quick
+decision-making.
+@^inner loop@>
+
+@d none_seen 0 /*no |y_here| or |z_here| nodes have been encountered yet*/
+@d y_seen 6 /*we have seen |y_here| but not |z_here|*/
+@d z_seen 12 /*we have seen |z_here| but not |y_here|*/
+
+@<Look at the other stack entries until deciding...@>=
+p=link(q);mstate=none_seen;
+while (p!=null)
+ {@+if (width(p)==w) @<Consider a node with matching width; |goto found|
+if it's a hit@>@;
+ else switch (mstate+info(p)) {
+ case none_seen+y_here: mstate=y_seen;@+break;
+ case none_seen+z_here: mstate=z_seen;@+break;
+ case y_seen+z_here: case z_seen+y_here: goto not_found;
+ default:do_nothing;
+ }
+ p=link(p);
+ }
+not_found:
+
+@ We might find a valid hit in a |y| or |z| byte that is already gone
+from the buffer. But we can't change bytes that are gone forever; ``the
+moving finger writes, $\ldots\,\,$.''
+
+@<Consider a node with matching width...@>=
+switch (mstate+info(p)) {
+case none_seen+yz_OK: case none_seen+y_OK:
+ case z_seen+yz_OK: case z_seen+y_OK: @t@>@;@/
+ if (location(p) < dvi_gone) goto not_found;
+ else@<Change buffered instruction to |y| or |w| and |goto found|@>@;@+break;
+case none_seen+z_OK: case y_seen+yz_OK:
+ case y_seen+z_OK: @t@>@;@/
+ if (location(p) < dvi_gone) goto not_found;
+ else@<Change buffered instruction to |z| or |x| and |goto found|@>@;@+break;
+case none_seen+y_here: case none_seen+z_here:
+ case y_seen+z_here: case z_seen+y_here: goto found;
+default:do_nothing;
+}
+
+@ @<Change buffered instruction to |y| or |w| and |goto found|@>=
+{@+k=location(p)-dvi_offset;
+if (k < 0) k=k+dvi_buf_size;
+dvi_buf[k]=dvi_buf[k]+y1-down1;
+info(p)=y_here;goto found;
+}
+
+@ @<Change buffered instruction to |z| or |x| and |goto found|@>=
+{@+k=location(p)-dvi_offset;
+if (k < 0) k=k+dvi_buf_size;
+dvi_buf[k]=dvi_buf[k]+z1-down1;
+info(p)=z_here;goto found;
+}
+
+@ In case you are wondering when all the movement nodes are removed from
+\TeX's memory, the answer is that they are recycled just before
+|hlist_out| and |vlist_out| finish outputting a box. This restores the
+down and right stacks to the state they were in before the box was output,
+except that some |info|'s may have become more restrictive.
+
+@p static void prune_movements(int @!l)
+ /*delete movement nodes with |location >= l|*/
+{@+
+pointer p; /*node being deleted*/
+while (down_ptr!=null)
+ {@+if (location(down_ptr) < l) goto done;
+ p=down_ptr;down_ptr=link(p);free_node(p, movement_node_size);
+ }
+done: while (right_ptr!=null)
+ {@+if (location(right_ptr) < l) return;
+ p=right_ptr;right_ptr=link(p);free_node(p, movement_node_size);
+ }
+}
+
+@ The actual distances by which we want to move might be computed as the
+sum of several separate movements. For example, there might be several
+glue nodes in succession, or we might want to move right by the width of
+some box plus some amount of glue. More importantly, the baselineskip
+distances are computed in terms of glue together with the depth and
+height of adjacent boxes, and we want the \.{DVI} file to lump these
+three quantities together into a single motion.
+
+Therefore, \TeX\ maintains two pairs of global variables: |dvi_h| and |dvi_v|
+are the |h| and |v| coordinates corresponding to the commands actually
+output to the \.{DVI} file, while |cur_h| and |cur_v| are the coordinates
+corresponding to the current state of the output routines. Coordinate
+changes will accumulate in |cur_h| and |cur_v| without being reflected
+in the output, until such a change becomes necessary or desirable; we
+can call the |movement| procedure whenever we want to make |dvi_h==cur_h|
+or |dvi_v==cur_v|.
+
+The current font reflected in the \.{DVI} output is called |dvi_f|;
+there is no need for a `\\{cur\_f}' variable.
+
+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.
+
+@d synch_h if (cur_h!=dvi_h)
+ {@+movement(cur_h-dvi_h, right1);dvi_h=cur_h;
+ }
+@d synch_v if (cur_v!=dvi_v)
+ {@+movement(cur_v-dvi_v, down1);dvi_v=cur_v;
+ }
+
+@<Glob...@>=
+static scaled @!dvi_h, @!dvi_v; /*a \.{DVI} reader program thinks we are here*/
+static scaled @!cur_h, @!cur_v; /*\TeX\ thinks we are here*/
+static internal_font_number @!dvi_f; /*the current font*/
+static int @!cur_s; /*current depth of output box nesting, initially $-1$*/
+
+@ @<Initialize variables as |ship_out| begins@>=
+dvi_h=0;dvi_v=0;cur_h=h_offset;dvi_f=null_font;
+ensure_dvi_open;
+if (total_pages==0)
+ {@+dvi_out(pre);dvi_out(id_byte); /*output the preamble*/
+@^preamble of \.{DVI} file@>
+ dvi_four(25400000);dvi_four(473628672); /*conversion ratio for sp*/
+ prepare_mag();dvi_four(mag); /*magnification factor is frozen*/
+ old_setting=selector;selector=new_string;
+ print(" TeX output ");print_int(year);print_char('.');
+ print_two(month);print_char('.');print_two(day);
+ print_char(':');print_two(time/60);
+ print_two(time%60);
+ selector=old_setting;dvi_out(cur_length);
+ for (s=str_start[str_ptr]; s<=pool_ptr-1; s++) dvi_out(so(str_pool[s]));
+ pool_ptr=str_start[str_ptr]; /*flush the current string*/
+ }
+
+@ When |hlist_out| is called, its duty is to output the box represented
+by the |hlist_node| pointed to by |temp_ptr|. The reference point of that
+box has coordinates |(cur_h, cur_v)|.
+
+Similarly, when |vlist_out| is called, its duty is to output the box represented
+by the |vlist_node| pointed to by |temp_ptr|. The reference point of that
+box has coordinates |(cur_h, cur_v)|.
+@^recursion@>
+
+@p static void vlist_out(void); /*|hlist_out| and |vlist_out| are mutually
+ recursive*/
+
+@ The recursive procedures |hlist_out| and |vlist_out| each have local variables
+|save_h| and |save_v| to hold the values of |dvi_h| and |dvi_v| just before
+entering a new level of recursion. In effect, the values of |save_h| and
+|save_v| on \TeX's run-time stack correspond to the values of |h| and |v|
+that a \.{DVI}-reading program will push onto its coordinate stack.
+
+@p @t\4@>@<Declare procedures needed in |hlist_out|, |vlist_out|@>@t@>@/
+static void hlist_out(void) /*output an |hlist_node| box*/
+{@+
+scaled base_line; /*the baseline coordinate for this box*/
+scaled @!left_edge; /*the left coordinate for this box*/
+scaled @!save_h, @!save_v; /*what |dvi_h| and |dvi_v| should pop to*/
+pointer @!this_box; /*pointer to containing box*/
+glue_ord @!g_order; /*applicable order of infinity for glue*/
+int @!g_sign; /*selects type of glue*/
+pointer @!p; /*current position in the hlist*/
+int @!save_loc; /*\.{DVI} byte location upon entry*/
+pointer @!leader_box; /*the leader box being replicated*/
+scaled @!leader_wd; /*width of leader box being replicated*/
+scaled @!lx; /*extra space between leader boxes*/
+bool @!outer_doing_leaders; /*were we doing leaders?*/
+scaled @!edge; /*left edge of sub-box, or right edge of leader space*/
+double @!glue_temp; /*glue value before rounding*/
+double @!cur_glue; /*glue seen so far*/
+scaled @!cur_g; /*rounded equivalent of |cur_glue| times the glue ratio*/
+cur_g=0;cur_glue=float_constant(0);
+this_box=temp_ptr;g_order=glue_order(this_box);
+g_sign=glue_sign(this_box);p=list_ptr(this_box);
+incr(cur_s);
+if (cur_s > 0) dvi_out(push);
+if (cur_s > max_push) max_push=cur_s;
+save_loc=dvi_offset+dvi_ptr;base_line=cur_v;left_edge=cur_h;
+while (p!=null) @<Output node |p| for |hlist_out| and move to the next node,
+maintaining the condition |cur_v=base_line|@>;
+prune_movements(save_loc);
+if (cur_s > 0) dvi_pop(save_loc);
+decr(cur_s);
+}
+
+@ We ought to give special care to the efficiency of one part of |hlist_out|,
+since it belongs to \TeX's inner loop. When a |char_node| is encountered,
+we save a little time by processing several nodes in succession until
+reaching a non-|char_node|. The program uses the fact that |set_char_0==0|.
+@^inner loop@>
+
+@<Output node |p| for |hlist_out|...@>=
+reswitch: if (is_char_node(p))
+ {@+synch_h;synch_v;
+ @/do@+{f=font(p);c=character(p);
+ if (f!=dvi_f) @<Change font |dvi_f| to |f|@>;
+ if (c >= qi(128)) dvi_out(set1);
+ dvi_out(qo(c));@/
+ cur_h=cur_h+char_width(f, char_info(f, c));
+ p=link(p);
+ }@+ while (!(!is_char_node(p)));
+ dvi_h=cur_h;
+ }
+else@<Output the non-|char_node| |p| for |hlist_out| and move to the next
+node@>@;
+
+@ @<Change font |dvi_f| to |f|@>=
+{@+if (!font_used[f])
+ {@+dvi_font_def(f);font_used[f]=true;
+ }
+if (f <= 64+font_base) dvi_out(f-font_base-1+fnt_num_0)@;
+else{@+dvi_out(fnt1);dvi_out(f-font_base-1);
+ }
+dvi_f=f;
+}
+
+@ @<Output the non-|char_node| |p| for |hlist_out|...@>=
+{@+switch (type(p)) {
+case hlist_node: case vlist_node: @<Output a box in an hlist@>@;@+break;
+case rule_node: {@+rule_ht=height(p);rule_dp=depth(p);rule_wd=width(p);
+ goto fin_rule;
+ }
+case whatsit_node: @<Output the whatsit node |p| in an hlist@>;@+break;
+case glue_node: @<Move right or output leaders@>@;
+case kern_node: case math_node: cur_h=cur_h+width(p);@+break;
+case ligature_node: @<Make node |p| look like a |char_node| and |goto reswitch|@>@;
+default:do_nothing;
+} @/
+goto next_p;
+fin_rule: @<Output a rule in an hlist@>;
+move_past: cur_h=cur_h+rule_wd;
+next_p: p=link(p);
+}
+
+@ @<Output a box in an hlist@>=
+if (list_ptr(p)==null) cur_h=cur_h+width(p);
+else{@+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;
+ if (type(p)==vlist_node) vlist_out();@+else hlist_out();
+ dvi_h=save_h;dvi_v=save_v;
+ cur_h=edge+width(p);cur_v=base_line;
+ }
+
+@ @<Output a rule in an hlist@>=
+if (is_running(rule_ht)) rule_ht=height(this_box);
+if (is_running(rule_dp)) rule_dp=depth(this_box);
+rule_ht=rule_ht+rule_dp; /*this is the rule thickness*/
+if ((rule_ht > 0)&&(rule_wd > 0)) /*we don't output empty rules*/
+ {@+synch_h;cur_v=base_line+rule_dp;synch_v;
+ dvi_out(set_rule);dvi_four(rule_ht);dvi_four(rule_wd);
+ cur_v=base_line;dvi_h=dvi_h+rule_wd;
+ }
+
+@ @d billion float_constant(1000000000)
+@d vet_glue(A) glue_temp=A;
+ if (glue_temp > billion)
+ glue_temp=billion;
+ else if (glue_temp < -billion)
+ glue_temp=-billion
+
+@<Move right or output leaders@>=
+{@+g=glue_ptr(p);rule_wd=width(g)-cur_g;
+if (g_sign!=normal)
+ {@+if (g_sign==stretching)
+ {@+if (stretch_order(g)==g_order)
+ {@+cur_glue=cur_glue+stretch(g);
+ vet_glue(unfix(glue_set(this_box))*cur_glue);
+@^real multiplication@>
+ cur_g=round(glue_temp);
+ }
+ }
+ else if (shrink_order(g)==g_order)
+ {@+cur_glue=cur_glue-shrink(g);
+ vet_glue(unfix(glue_set(this_box))*cur_glue);
+ cur_g=round(glue_temp);
+ }
+ }
+rule_wd=rule_wd+cur_g;
+if (subtype(p) >= a_leaders)
+ @<Output leaders in an hlist, |goto fin_rule| if a rule or to |next_p| if
+done@>;
+goto move_past;
+}
+
+@ @<Output leaders in an hlist...@>=
+{@+leader_box=leader_ptr(p);
+if (type(leader_box)==rule_node)
+ {@+rule_ht=height(leader_box);rule_dp=depth(leader_box);
+ goto fin_rule;
+ }
+leader_wd=width(leader_box);
+if ((leader_wd > 0)&&(rule_wd > 0))
+ {@+rule_wd=rule_wd+10; /*compensate for floating-point rounding*/
+ 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)
+ @<Output a leader box at |cur_h|, then advance |cur_h| by |leader_wd+lx|@>;
+ cur_h=edge-10;goto next_p;
+ }
+}
+
+@ The calculations related to leaders require a bit of care. First, in the
+case of |a_leaders| (aligned leaders), we want to move |cur_h| to
+|left_edge| plus the smallest multiple of |leader_wd| for which the result
+is not less than the current value of |cur_h|; i.e., |cur_h| should become
+$|left_edge|+|leader_wd|\times\lceil
+(|cur_h|-|left_edge|)/|leader_wd|\rceil$. The program here should work in
+all cases even though some implementations of \PASCAL\ give nonstandard
+results for the |/| operation when |cur_h| is less than |left_edge|.
+
+In the case of |c_leaders| (centered leaders), we want to increase |cur_h|
+by half of the excess space not occupied by the leaders; and in the
+case of |x_leaders| (expanded leaders) we increase |cur_h|
+by $1/(q+1)$ of this excess space, where $q$ is the number of times the
+leader box will be replicated. Slight inaccuracies in the division might
+accumulate; half of this rounding error is placed at each end of the leaders.
+
+@<Let |cur_h| be the position of the first box,...@>=
+if (subtype(p)==a_leaders)
+ {@+save_h=cur_h;
+ cur_h=left_edge+leader_wd*((cur_h-left_edge)/leader_wd);
+ if (cur_h < save_h) cur_h=cur_h+leader_wd;
+ }
+else{@+lq=rule_wd/leader_wd; /*the number of box copies*/
+ lr=rule_wd%leader_wd; /*the remaining space*/
+ if (subtype(p)==c_leaders) cur_h=cur_h+(lr/2);
+ else{@+lx=lr/(lq+1);
+ cur_h=cur_h+((lr-(lq-1)*lx)/2);
+ }
+ }
+
+@ The `\\{synch}' operations here are intended to decrease the number of
+bytes needed to specify horizontal and vertical motion in the \.{DVI} output.
+
+@<Output a leader box at |cur_h|,...@>=
+{@+cur_v=base_line+shift_amount(leader_box);synch_v;save_v=dvi_v;@/
+synch_h;save_h=dvi_h;temp_ptr=leader_box;
+outer_doing_leaders=doing_leaders;doing_leaders=true;
+if (type(leader_box)==vlist_node) vlist_out();@+else hlist_out();
+doing_leaders=outer_doing_leaders;
+dvi_v=save_v;dvi_h=save_h;cur_v=base_line;
+cur_h=save_h+leader_wd+lx;
+}
+
+@ The |vlist_out| routine is similar to |hlist_out|, but a bit simpler.
+
+@p static void vlist_out(void) /*output a |vlist_node| box*/
+{@+
+scaled left_edge; /*the left coordinate for this box*/
+scaled @!top_edge; /*the top coordinate for this box*/
+scaled @!save_h, @!save_v; /*what |dvi_h| and |dvi_v| should pop to*/
+pointer @!this_box; /*pointer to containing box*/
+glue_ord @!g_order; /*applicable order of infinity for glue*/
+int @!g_sign; /*selects type of glue*/
+pointer @!p; /*current position in the vlist*/
+int @!save_loc; /*\.{DVI} byte location upon entry*/
+pointer @!leader_box; /*the leader box being replicated*/
+scaled @!leader_ht; /*height of leader box being replicated*/
+scaled @!lx; /*extra space between leader boxes*/
+bool @!outer_doing_leaders; /*were we doing leaders?*/
+scaled @!edge; /*bottom boundary of leader space*/
+double @!glue_temp; /*glue value before rounding*/
+double @!cur_glue; /*glue seen so far*/
+scaled @!cur_g; /*rounded equivalent of |cur_glue| times the glue ratio*/
+cur_g=0;cur_glue=float_constant(0);
+this_box=temp_ptr;g_order=glue_order(this_box);
+g_sign=glue_sign(this_box);p=list_ptr(this_box);
+incr(cur_s);
+if (cur_s > 0) dvi_out(push);
+if (cur_s > max_push) max_push=cur_s;
+save_loc=dvi_offset+dvi_ptr;left_edge=cur_h;cur_v=cur_v-height(this_box);
+top_edge=cur_v;
+while (p!=null) @<Output node |p| for |vlist_out| and move to the next node,
+maintaining the condition |cur_h=left_edge|@>;
+prune_movements(save_loc);
+if (cur_s > 0) dvi_pop(save_loc);
+decr(cur_s);
+}
+
+@ @<Output node |p| for |vlist_out|...@>=
+{@+if (is_char_node(p)) confusion("vlistout");
+@:this can't happen vlistout}{\quad vlistout@>
+else@<Output the non-|char_node| |p| for |vlist_out|@>;
+next_p: p=link(p);
+}
+
+@ @<Output the non-|char_node| |p| for |vlist_out|@>=
+{@+switch (type(p)) {
+case hlist_node: case vlist_node: @<Output a box in a vlist@>@;@+break;
+case rule_node: {@+rule_ht=height(p);rule_dp=depth(p);rule_wd=width(p);
+ goto fin_rule;
+ }
+case whatsit_node: @<Output the whatsit node |p| in a vlist@>;@+break;
+case glue_node: @<Move down or output leaders@>@;
+case kern_node: cur_v=cur_v+width(p);@+break;
+default:do_nothing;
+} @/
+goto next_p;
+fin_rule: @<Output a rule in a vlist, |goto next_p|@>;
+move_past: cur_v=cur_v+rule_ht;
+}
+
+@ The |synch_v| here allows the \.{DVI} output to use one-byte commands
+for adjusting |v| in most cases, since the baselineskip distance will
+usually be constant.
+
+@<Output a box in a vlist@>=
+if (list_ptr(p)==null) cur_v=cur_v+height(p)+depth(p);
+else{@+cur_v=cur_v+height(p);synch_v;
+ save_h=dvi_h;save_v=dvi_v;
+ cur_h=left_edge+shift_amount(p); /*shift the box right*/
+ temp_ptr=p;
+ if (type(p)==vlist_node) vlist_out();@+else hlist_out();
+ dvi_h=save_h;dvi_v=save_v;
+ cur_v=save_v+depth(p);cur_h=left_edge;
+ }
+
+@ @<Output a rule in a vlist...@>=
+if (is_running(rule_wd)) rule_wd=width(this_box);
+rule_ht=rule_ht+rule_dp; /*this is the rule thickness*/
+cur_v=cur_v+rule_ht;
+if ((rule_ht > 0)&&(rule_wd > 0)) /*we don't output empty rules*/
+ {@+synch_h;synch_v;
+ dvi_out(put_rule);dvi_four(rule_ht);dvi_four(rule_wd);
+ }
+goto next_p
+
+@ @<Move down or output leaders@>=
+{@+g=glue_ptr(p);rule_ht=width(g)-cur_g;
+if (g_sign!=normal)
+ {@+if (g_sign==stretching)
+ {@+if (stretch_order(g)==g_order)
+ {@+cur_glue=cur_glue+stretch(g);
+ vet_glue(unfix(glue_set(this_box))*cur_glue);
+@^real multiplication@>
+ cur_g=round(glue_temp);
+ }
+ }
+ else if (shrink_order(g)==g_order)
+ {@+cur_glue=cur_glue-shrink(g);
+ vet_glue(unfix(glue_set(this_box))*cur_glue);
+ cur_g=round(glue_temp);
+ }
+ }
+rule_ht=rule_ht+cur_g;
+if (subtype(p) >= a_leaders)
+ @<Output leaders in a vlist, |goto fin_rule| if a rule or to |next_p| if
+done@>;
+goto move_past;
+}
+
+@ @<Output leaders in a vlist...@>=
+{@+leader_box=leader_ptr(p);
+if (type(leader_box)==rule_node)
+ {@+rule_wd=width(leader_box);rule_dp=0;
+ goto fin_rule;
+ }
+leader_ht=height(leader_box)+depth(leader_box);
+if ((leader_ht > 0)&&(rule_ht > 0))
+ {@+rule_ht=rule_ht+10; /*compensate for floating-point rounding*/
+ edge=cur_v+rule_ht;lx=0;
+ @<Let |cur_v| be the position of the first box, and set |leader_ht+lx| to
+the spacing between corresponding parts of boxes@>;
+ while (cur_v+leader_ht <= edge)
+ @<Output a leader box at |cur_v|, then advance |cur_v| by |leader_ht+lx|@>;
+ cur_v=edge-10;goto next_p;
+ }
+}
+
+@ @<Let |cur_v| be the position of the first box,...@>=
+if (subtype(p)==a_leaders)
+ {@+save_v=cur_v;
+ cur_v=top_edge+leader_ht*((cur_v-top_edge)/leader_ht);
+ if (cur_v < save_v) cur_v=cur_v+leader_ht;
+ }
+else{@+lq=rule_ht/leader_ht; /*the number of box copies*/
+ lr=rule_ht%leader_ht; /*the remaining space*/
+ if (subtype(p)==c_leaders) cur_v=cur_v+(lr/2);
+ else{@+lx=lr/(lq+1);
+ cur_v=cur_v+((lr-(lq-1)*lx)/2);
+ }
+ }
+
+@ When we reach this part of the program, |cur_v| indicates the top of a
+leader box, not its baseline.
+
+@<Output a leader box at |cur_v|,...@>=
+{@+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;
+if (type(leader_box)==vlist_node) vlist_out();@+else hlist_out();
+doing_leaders=outer_doing_leaders;
+dvi_v=save_v;dvi_h=save_h;cur_h=left_edge;
+cur_v=save_v-height(leader_box)+leader_ht+lx;
+}
+
+@ The |hlist_out| and |vlist_out| procedures are now complete, so we are
+ready for the |ship_out| routine that gets them started in the first place.
+
+@p static void ship_out(pointer @!p) /*output the box |p|*/
+{@+
+int page_loc; /*location of the current |bop|*/
+int @!j, @!k; /*indices to first ten count registers*/
+int @!s; /*index into |str_pool|*/
+int @!old_setting; /*saved |selector| setting*/
+@<Local variables to save the profiling context@>@;
+@<Charge the time used here on |ship_out|@>@;
+if (tracing_output > 0)
+ {@+print_nl("");print_ln();
+ print("Completed box being shipped out");
+@.Completed box...@>
+ }
+if (term_offset > max_print_line-9) print_ln();
+else if ((term_offset > 0)||(file_offset > 0)) print_char(' ');
+print_char('[');j=9;
+while ((count(j)==0)&&(j > 0)) decr(j);
+for (k=0; k<=j; k++)
+ {@+print_int(count(k));
+ if (k < j) print_char('.');
+ }
+update_terminal;
+if (tracing_output > 0)
+ {@+print_char(']');
+ begin_diagnostic();show_box(p);end_diagnostic(true);
+ }
+@<Ship box |p| out@>;
+if (tracing_output <= 0) print_char(']');
+dead_cycles=0;
+update_terminal; /*progress report*/
+@<Flush the box from memory, showing statistics if requested@>;
+@<restore the previous current file, line, and command@>@;
+}
+
+@ @<Flush the box from memory, showing statistics if requested@>=
+#ifdef @!STAT
+if (tracing_stats > 1)
+ {@+print_nl("Memory usage before: ");
+@.Memory usage...@>
+ print_int(var_used);print_char('&');
+ print_int(dyn_used);print_char(';');
+ }
+#endif
+flush_node_list(p);
+#ifdef @!STAT
+if (tracing_stats > 1)
+ {@+print(" after: ");
+ print_int(var_used);print_char('&');
+ print_int(dyn_used);print("; still untouched: ");
+ print_int(hi_mem_min-lo_mem_max-1);print_ln();
+ }
+#endif
+
+@ @<Ship box |p| out@>=
+@<Update the values of |max_h| and |max_v|; but if the page is too large,
+|goto done|@>;
+@<Initialize variables as |ship_out| begins@>;
+page_loc=dvi_offset+dvi_ptr;
+dvi_out(bop);
+for (k=0; k<=9; k++) dvi_four(count(k));
+dvi_four(last_bop);last_bop=page_loc;
+cur_v=height(p)+v_offset;temp_ptr=p;
+if (type(p)==vlist_node) vlist_out();@+else hlist_out();
+dvi_out(eop);incr(total_pages);cur_s=-1;
+done:
+
+@ Sometimes the user will generate a huge page because other error messages
+are being ignored. Such pages are not output to the \.{dvi} file, since they
+may confuse the printing software.
+
+@<Update the values of |max_h| and |max_v|; but if the page is too large...@>=
+if ((height(p) > max_dimen)||@|(depth(p) > max_dimen)||@|
+ (height(p)+depth(p)+v_offset > max_dimen)||@|
+ (width(p)+h_offset > max_dimen))
+ {@+print_err("Huge page cannot be shipped out");
+@.Huge page...@>
+ help2("The page just created is more than 18 feet tall or",@/
+ "more than 18 feet wide, so I suspect something went wrong.");
+ error();
+ if (tracing_output <= 0)
+ {@+begin_diagnostic();
+ print_nl("The following box has been deleted:");
+@.The following...deleted@>
+ show_box(p);
+ end_diagnostic(true);
+ }
+ goto done;
+ }
+if (height(p)+depth(p)+v_offset > max_v) max_v=height(p)+depth(p)+v_offset;
+if (width(p)+h_offset > max_h) max_h=width(p)+h_offset
+
+@ At the end of the program, we must finish things off by writing the
+post\-amble. If |total_pages==0|, the \.{DVI} file was never opened.
+If |total_pages >= 65536|, the \.{DVI} file will lie. And if
+|max_push >= 65536|, the user deserves whatever chaos might ensue.
+
+An integer variable |k| will be declared for use by this routine.
+
+@<Finish the \.{DVI} file@>=
+while (cur_s > -1)
+ {@+if (cur_s > 0) dvi_out(pop)@;
+ else{@+dvi_out(eop);incr(total_pages);
+ }
+ decr(cur_s);
+ }
+if (total_pages==0) print_nl("No pages of output.");
+@.No pages of output@>
+else{@+dvi_out(post); /*beginning of the postamble*/
+ dvi_four(last_bop);last_bop=dvi_offset+dvi_ptr-5; /*|post| location*/
+ dvi_four(25400000);dvi_four(473628672); /*conversion ratio for sp*/
+ prepare_mag();dvi_four(mag); /*magnification factor*/
+ dvi_four(max_v);dvi_four(max_h);@/
+ dvi_out(max_push/256);dvi_out(max_push%256);@/
+ dvi_out((total_pages/256)%256);dvi_out(total_pages%256);@/
+ @<Output the font definitions for all fonts that were used@>;
+ dvi_out(post_post);dvi_four(last_bop);dvi_out(id_byte);@/
+ k=4+((dvi_buf_size-dvi_ptr)%4); /*the number of 223's*/
+ while (k > 0)
+ {@+dvi_out(223);decr(k);
+ }
+ @<Empty the last bytes out of |dvi_buf|@>;
+ print_nl("Output written on ");slow_print(output_file_name);
+@.Output written on x@>
+ print(" (");print_int(total_pages);print(" page");
+ if (total_pages!=1) print_char('s');
+ print(", ");print_int(dvi_offset+dvi_ptr);print(" bytes).");
+ b_close(&dvi_file);
+ }
+
+@ @<Output the font definitions...@>=
+while (font_ptr > font_base)
+ {@+if (font_used[font_ptr]) dvi_font_def(font_ptr);
+ decr(font_ptr);
+ }
+
+@* Packaging.
+We're essentially done with the parts of \TeX\ that are concerned with
+the input (|get_next|) and the output (|ship_out|). So it's time to
+get heavily into the remaining part, which does the real work of typesetting.
+
+After lists are constructed, \TeX\ wraps them up and puts them into boxes.
+Two major subroutines are given the responsibility for this task: |hpack|
+applies to horizontal lists (hlists) and |vpack| applies to vertical lists
+(vlists). The main duty of |hpack| and |vpack| is to compute the dimensions
+of the resulting boxes, and to adjust the glue if one of those dimensions
+is pre-specified. The computed sizes normally enclose all of the material
+inside the new box; but some items may stick out if negative glue is used,
+if the box is overfull, or if a \.{\\vbox} includes other boxes that have
+been shifted left.
+
+The subroutine call |hpack(p, w, m)| returns a pointer to an |hlist_node|
+for a box containing the hlist that starts at |p|. Parameter |w| specifies
+a width; and parameter |m| is either `|exactly|' or `|additional|'. Thus,
+|hpack(p, w, exactly)| produces a box whose width is exactly |w|, while
+|hpack(p, w, additional)| yields a box whose width is the natural width plus
+|w|. It is convenient to define a macro called `|natural|' to cover the
+most common case, so that we can say |hpack(p, natural)| to get a box that
+has the natural width of list |p|.
+
+Similarly, |vpack(p, w, m)| returns a pointer to a |vlist_node| for a
+box containing the vlist that starts at |p|. In this case |w| represents
+a height instead of a width; the parameter |m| is interpreted as in |hpack|.
+
+@d exactly 0 /*a box dimension is pre-specified*/
+@d additional 1 /*a box dimension is increased from the natural one*/
+@d natural 0, additional /*shorthand for parameters to |hpack| and |vpack|*/
+
+@ The parameters to |hpack| and |vpack| correspond to \TeX's primitives
+like `\.{\\hbox} \.{to} \.{300pt}', `\.{\\hbox} \.{spread} \.{10pt}'; note
+that `\.{\\hbox}' with no dimension following it is equivalent to
+`\.{\\hbox} \.{spread} \.{0pt}'. The |scan_spec| subroutine scans such
+constructions in the user's input, including the mandatory left brace that
+follows them, and it puts the specification onto |save_stack| so that the
+desired box can later be obtained by executing the following code:
+$$\vbox{\halign{#\hfil\cr
+|save_ptr=save_ptr-2;|\cr
+|hpack(p, saved(1), saved(0)).|\cr}}$$
+Special care is necessary to ensure that the special |save_stack| codes
+are placed just below the new group code, because scanning can change
+|save_stack| when \.{\\csname} appears.
+
+@p static void scan_spec(group_code @!c, bool @!three_codes)
+ /*scans a box specification and left brace*/
+{@+
+int @!s; /*temporarily saved value*/
+int @!spec_code;
+if (three_codes) s=saved(0);
+if (scan_keyword("to")) spec_code=exactly;
+@.to@>
+else if (scan_keyword("spread")) spec_code=additional;
+@.spread@>
+else{@+spec_code=additional;cur_val=0;
+ goto found;
+ }
+scan_normal_dimen;
+found: if (three_codes)
+ {@+saved(0)=s;incr(save_ptr);
+ }
+saved(0)=spec_code;saved(1)=cur_val;save_ptr=save_ptr+2;
+new_save_level(c);scan_left_brace();
+}
+
+@ To figure out the glue setting, |hpack| and |vpack| determine how much
+stretchability and shrinkability are present, considering all four orders
+of infinity. The highest order of infinity that has a nonzero coefficient
+is then used as if no other orders were present.
+
+For example, suppose that the given list contains six glue nodes with
+the respective stretchabilities 3pt, 8fill, 5fil, 6pt, $-3$fil, $-8$fill.
+Then the total is essentially 2fil; and if a total additional space of 6pt
+is to be achieved by stretching, the actual amounts of stretch will be
+0pt, 0pt, 15pt, 0pt, $-9$pt, and 0pt, since only `fil' glue will be
+considered. (The `fill' glue is therefore not really stretching infinitely
+with respect to `fil'; nobody would actually want that to happen.)
+
+The arrays |total_stretch| and |total_shrink| are used to determine how much
+glue of each kind is present. A global variable |last_badness| is used
+to implement \.{\\badness}.
+
+@<Glob...@>=
+static scaled @!total_stretch0[filll-normal+1],
+ *const @!total_stretch = @!total_stretch0-normal,
+ @!total_shrink0[filll-normal+1], *const @!total_shrink = @!total_shrink0-normal;
+ /*glue found by |hpack| or |vpack|*/
+static int @!last_badness; /*badness of the most recently packaged box*/
+
+@ If the global variable |adjust_tail| is non-null, the |hpack| routine
+also removes all occurrences of |ins_node|, |mark_node|, and |adjust_node|
+items and appends the resulting material onto the list that ends at
+location |adjust_tail|.
+
+@<Glob...@>=
+static pointer @!adjust_tail; /*tail of adjustment list*/
+
+@ @<Set init...@>=adjust_tail=null;last_badness=0;
+
+@ Here now is |hpack|, which contains few if any surprises.
+
+@p static pointer hpack(pointer @!p, scaled @!w, small_number @!m)
+{@+
+pointer r; /*the box node that will be returned*/
+pointer @!q; /*trails behind |p|*/
+scaled @!h, @!d, @!x; /*height, depth, and natural width*/
+scaled @!s; /*shift amount*/
+pointer @!g; /*points to a glue specification*/
+glue_ord @!o; /*order of infinity*/
+internal_font_number @!f; /*the font in a |char_node|*/
+four_quarters @!i; /*font information about a |char_node|*/
+eight_bits @!hd; /*height and depth indices for a character*/
+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@>;
+while (p!=null) @<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@>;
+if (adjust_tail!=null) link(adjust_tail)=null;
+height(r)=h;depth(r)=d;@/
+@<Determine the value of |width(r)| and the appropriate glue setting; then
+|return| or |goto common_ending|@>;
+common_ending: @<Finish issuing a diagnostic message for an overfull or underfull
+hbox@>;
+end: return r;
+}
+
+@ @<Clear dimensions to zero@>=
+d=0;x=0;
+total_stretch[normal]=0;total_shrink[normal]=0;
+total_stretch[fil]=0;total_shrink[fil]=0;
+total_stretch[fill]=0;total_shrink[fill]=0;
+total_stretch[filll]=0;total_shrink[filll]=0
+
+@ @<Examine node |p| in the hlist, taking account of its effect...@>=
+@^inner loop@>
+{@+reswitch: while (is_char_node(p))
+ @<Incorporate character dimensions into the dimensions of the hbox that
+will contain~it, then move to the next node@>;
+if (p!=null)
+ {@+switch (type(p)) {
+ case hlist_node: case vlist_node: case rule_node:
+ case unset_node:
+ @<Incorporate box dimensions into the dimensions of the hbox that will
+contain~it@>@;@+break;
+ case ins_node: case mark_node: case adjust_node: if (adjust_tail!=null)
+ @<Transfer node |p| to the adjustment list@>@;@+break;
+ case whatsit_node: @<Incorporate a whatsit node into an hbox@>;@+break;
+ case glue_node: @<Incorporate glue into the horizontal totals@>@;@+break;
+ case kern_node: case math_node: x=x+width(p);@+break;
+ case ligature_node: @<Make node |p| look like a |char_node| and |goto reswitch|@>@;
+ default:do_nothing;
+ } @/
+ p=link(p);
+ }
+}
+
+
+@ @<Make node |p| look like a |char_node| and |goto reswitch|@>=
+{@+mem[lig_trick]=mem[lig_char(p)];link(lig_trick)=link(p);
+p=lig_trick;goto reswitch;
+}
+
+@ The code here implicitly uses the fact that running dimensions are
+indicated by |null_flag|, which will be ignored in the calculations
+because it is a highly negative number.
+
+@<Incorporate box dimensions into the dimensions of the hbox...@>=
+{@+x=x+width(p);
+if (type(p) >= rule_node) s=0;@+else s=shift_amount(p);
+if (height(p)-s > h) h=height(p)-s;
+if (depth(p)+s > d) d=depth(p)+s;
+}
+
+@ The following code is part of \TeX's inner loop; i.e., adding another
+character of text to the user's input will cause each of these instructions
+to be exercised one more time.
+@^inner loop@>
+
+@<Incorporate character dimensions into the dimensions of the hbox...@>=
+{@+f=font(p);i=char_info(f, character(p));hd=height_depth(i);
+x=x+char_width(f, i);@/
+s=char_height(f, hd);@+if (s > h) h=s;
+s=char_depth(f, hd);@+if (s > d) d=s;
+p=link(p);
+}
+
+@ Although node |q| is not necessarily the immediate predecessor of node |p|,
+it always points to some node in the list preceding |p|. Thus, we can delete
+nodes by moving |q| when necessary. The algorithm takes linear time, and the
+extra computation does not intrude on the inner loop unless it is necessary
+to make a deletion.
+@^inner loop@>
+
+@<Transfer node |p| to the adjustment list@>=
+{@+while (link(q)!=p) q=link(q);
+if (type(p)==adjust_node)
+ {@+link(adjust_tail)=adjust_ptr(p);
+ while (link(adjust_tail)!=null) adjust_tail=link(adjust_tail);
+ p=link(p);free_node(link(q), small_node_size);
+ }
+else{@+link(adjust_tail)=p;adjust_tail=p;p=link(p);
+ }
+link(q)=p;p=q;
+}
+
+@ @<Incorporate glue into the horizontal totals@>=
+{@+g=glue_ptr(p);x=x+width(g);@/
+o=stretch_order(g);total_stretch[o]=total_stretch[o]+stretch(g);
+o=shrink_order(g);total_shrink[o]=total_shrink[o]+shrink(g);
+if (subtype(p) >= a_leaders)
+ {@+g=leader_ptr(p);
+ if (height(g) > h) h=height(g);
+ if (depth(g) > d) d=depth(g);
+ }
+}
+
+@ When we get to the present part of the program, |x| is the natural width
+of the box being packaged.
+
+@<Determine the value of |width(r)| and the appropriate glue setting...@>=
+if (m==additional) w=x+w;
+width(r)=w;x=w-x; /*now |x| is the excess to be made up*/
+if (x==0)
+ {@+glue_sign(r)=normal;glue_order(r)=normal;
+ set_glue_ratio_zero(glue_set(r));
+ goto end;
+ }
+else if (x > 0) @<Determine horizontal glue stretch setting, then |return|
+or \hbox{|goto common_ending|}@>@;
+else@<Determine horizontal glue shrink setting, then |return| or \hbox{|goto
+common_ending|}@>@;
+
+@ @<Determine horizontal glue stretch setting...@>=
+{@+@<Determine the stretch order@>;
+glue_order(r)=o;glue_sign(r)=stretching;
+if (total_stretch[o]!=0) glue_set(r)=fix(x/(double)total_stretch[o]);
+@^real division@>
+else{@+glue_sign(r)=normal;
+ set_glue_ratio_zero(glue_set(r)); /*there's nothing to stretch*/
+ }
+if (o==normal) if (list_ptr(r)!=null)
+ @<Report an underfull hbox and |goto common_ending|, if this box is sufficiently
+bad@>;
+goto end;
+}
+
+@ @<Determine the stretch order@>=
+if (total_stretch[filll]!=0) o=filll;
+else if (total_stretch[fill]!=0) o=fill;
+else if (total_stretch[fil]!=0) o=fil;
+else o=normal
+
+@ @<Report an underfull hbox and |goto common_ending|, if...@>=
+{@+last_badness=badness(x, total_stretch[normal]);
+if (last_badness > hbadness)
+ {@+print_ln();
+ if (last_badness > 100) print_nl("Underfull");@+else print_nl("Loose");
+ print(" \\hbox (badness ");print_int(last_badness);
+@.Underfull \\hbox...@>
+@.Loose \\hbox...@>
+ goto common_ending;
+ }
+}
+
+@ In order to provide a decent indication of where an overfull or underfull
+box originated, we use a global variable |pack_begin_line| that is
+set nonzero only when |hpack| is being called by the paragraph builder
+or the alignment finishing routine.
+
+@<Glob...@>=
+static int @!pack_begin_line; /*source file line where the current paragraph
+ or alignment began; a negative value denotes alignment*/
+
+@ @<Set init...@>=
+pack_begin_line=0;
+
+@ @<Finish issuing a diagnostic message for an overfull or underfull hbox@>=
+if (output_active) print(") has occurred while \\output is active");
+else{@+if (pack_begin_line!=0)
+ {@+if (pack_begin_line > 0) print(") in paragraph at lines ");
+ else print(") in alignment at lines ");
+ print_int(abs(pack_begin_line));
+ print("--");
+ }
+ else print(") detected at line ");
+ print_int(line);
+ }
+print_ln();@/
+font_in_short_display=null_font;short_display(list_ptr(r));print_ln();@/
+begin_diagnostic();show_box(r);end_diagnostic(true)
+
+@ @<Determine horizontal glue shrink setting...@>=
+{@+@<Determine the shrink order@>;
+glue_order(r)=o;glue_sign(r)=shrinking;
+if (total_shrink[o]!=0) glue_set(r)=fix((-x)/(double)total_shrink[o]);
+@^real division@>
+else{@+glue_sign(r)=normal;
+ set_glue_ratio_zero(glue_set(r)); /*there's nothing to shrink*/
+ }
+if ((total_shrink[o] < -x)&&(o==normal)&&(list_ptr(r)!=null))
+ {@+last_badness=1000000;
+ set_glue_ratio_one(glue_set(r)); /*use the maximum shrinkage*/
+ @<Report an overfull hbox and |goto common_ending|, if this box is sufficiently
+bad@>;
+ }
+else if (o==normal) if (list_ptr(r)!=null)
+ @<Report a tight hbox and |goto common_ending|, if this box is sufficiently
+bad@>;
+goto end;
+}
+
+@ @<Determine the shrink order@>=
+if (total_shrink[filll]!=0) o=filll;
+else if (total_shrink[fill]!=0) o=fill;
+else if (total_shrink[fil]!=0) o=fil;
+else o=normal
+
+@ @<Report an overfull hbox and |goto common_ending|, if...@>=
+if ((-x-total_shrink[normal] > hfuzz)||(hbadness < 100))
+ {@+if ((overfull_rule > 0)&&(-x-total_shrink[normal] > hfuzz))
+ {@+while (link(q)!=null) q=link(q);
+ link(q)=new_rule();
+ width(link(q))=overfull_rule;
+ }
+ print_ln();print_nl("Overfull \\hbox (");
+@.Overfull \\hbox...@>
+ print_scaled(-x-total_shrink[normal]);print("pt too wide");
+ goto common_ending;
+ }
+
+@ @<Report a tight hbox and |goto common_ending|, if...@>=
+{@+last_badness=badness(-x, total_shrink[normal]);
+if (last_badness > hbadness)
+ {@+print_ln();print_nl("Tight \\hbox (badness ");print_int(last_badness);
+@.Tight \\hbox...@>
+ goto common_ending;
+ }
+}
+
+@ The |vpack| subroutine is actually a special case of a slightly more
+general routine called |vpackage|, which has four parameters. The fourth
+parameter, which is |max_dimen| in the case of |vpack|, specifies the
+maximum depth of the page box that is constructed. The depth is first
+computed by the normal rules; if it exceeds this limit, the reference
+point is simply moved down until the limiting depth is attained.
+
+
+@p
+#define vpack(...) @[vpackage(__VA_ARGS__, max_dimen)@] /*special case of unconstrained depth*/
+static pointer vpackage(pointer @!p, scaled @!h, small_number @!m, scaled @!l)
+{@+
+pointer r; /*the box node that will be returned*/
+scaled @!w, @!d, @!x; /*width, depth, and natural height*/
+scaled @!s; /*shift amount*/
+pointer @!g; /*points to a glue specification*/
+glue_ord @!o; /*order of infinity*/
+last_badness=0;r=get_node(box_node_size);type(r)=vlist_node;
+subtype(r)=min_quarterword;shift_amount(r)=0;
+list_ptr(r)=p;@/
+w=0;@<Clear dimensions to zero@>;
+while (p!=null) @<Examine node |p| in the vlist, taking account of its effect
+on the dimensions of the new box; then advance |p| to the next node@>;
+width(r)=w;
+if (d > l)
+ {@+x=x+d-l;depth(r)=l;
+ }
+else depth(r)=d;
+@<Determine the value of |height(r)| and the appropriate glue setting; then
+|return| or |goto common_ending|@>;
+common_ending: @<Finish issuing a diagnostic message for an overfull or underfull
+vbox@>;
+end: return r;
+}
+
+@ @<Examine node |p| in the vlist, taking account of its effect...@>=
+{@+if (is_char_node(p)) confusion("vpack");
+@:this can't happen vpack}{\quad vpack@>
+else switch (type(p)) {
+ case hlist_node: case vlist_node: case rule_node:
+ case unset_node:
+ @<Incorporate box dimensions into the dimensions of the vbox that will
+contain~it@>@;@+break;
+ case whatsit_node: @<Incorporate a whatsit node into a vbox@>;@+break;
+ case glue_node: @<Incorporate glue into the vertical totals@>@;@+break;
+ case kern_node: {@+x=x+d+width(p);d=0;
+ } @+break;
+ default:do_nothing;
+ }
+p=link(p);
+}
+
+@ @<Incorporate box dimensions into the dimensions of the vbox...@>=
+{@+x=x+d+height(p);d=depth(p);
+if (type(p) >= rule_node) s=0;@+else s=shift_amount(p);
+if (width(p)+s > w) w=width(p)+s;
+}
+
+@ @<Incorporate glue into the vertical totals@>=
+{@+x=x+d;d=0;@/
+g=glue_ptr(p);x=x+width(g);@/
+o=stretch_order(g);total_stretch[o]=total_stretch[o]+stretch(g);
+o=shrink_order(g);total_shrink[o]=total_shrink[o]+shrink(g);
+if (subtype(p) >= a_leaders)
+ {@+g=leader_ptr(p);
+ if (width(g) > w) w=width(g);
+ }
+}
+
+@ When we get to the present part of the program, |x| is the natural height
+of the box being packaged.
+
+@<Determine the value of |height(r)| and the appropriate glue setting...@>=
+if (m==additional) h=x+h;
+height(r)=h;x=h-x; /*now |x| is the excess to be made up*/
+if (x==0)
+ {@+glue_sign(r)=normal;glue_order(r)=normal;
+ set_glue_ratio_zero(glue_set(r));
+ goto end;
+ }
+else if (x > 0) @<Determine vertical glue stretch setting, then |return| or
+\hbox{|goto common_ending|}@>@;
+else@<Determine vertical glue shrink setting, then |return| or \hbox{|goto
+common_ending|}@>@;
+
+@ @<Determine vertical glue stretch setting...@>=
+{@+@<Determine the stretch order@>;
+glue_order(r)=o;glue_sign(r)=stretching;
+if (total_stretch[o]!=0) glue_set(r)=fix(x/(double)total_stretch[o]);
+@^real division@>
+else{@+glue_sign(r)=normal;
+ set_glue_ratio_zero(glue_set(r)); /*there's nothing to stretch*/
+ }
+if (o==normal) if (list_ptr(r)!=null)
+ @<Report an underfull vbox and |goto common_ending|, if this box is sufficiently
+bad@>;
+goto end;
+}
+
+@ @<Report an underfull vbox and |goto common_ending|, if...@>=
+{@+last_badness=badness(x, total_stretch[normal]);
+if (last_badness > vbadness)
+ {@+print_ln();
+ if (last_badness > 100) print_nl("Underfull");@+else print_nl("Loose");
+ print(" \\vbox (badness ");print_int(last_badness);
+@.Underfull \\vbox...@>
+@.Loose \\vbox...@>
+ goto common_ending;
+ }
+}
+
+@ @<Finish issuing a diagnostic message for an overfull or underfull vbox@>=
+if (output_active) print(") has occurred while \\output is active");
+else{@+if (pack_begin_line!=0) /*it's actually negative*/
+ {@+print(") in alignment at lines ");
+ print_int(abs(pack_begin_line));
+ print("--");
+ }
+ else print(") detected at line ");
+ print_int(line);
+ print_ln();@/
+ }
+begin_diagnostic();show_box(r);end_diagnostic(true)
+
+@ @<Determine vertical glue shrink setting...@>=
+{@+@<Determine the shrink order@>;
+glue_order(r)=o;glue_sign(r)=shrinking;
+if (total_shrink[o]!=0) glue_set(r)=fix((-x)/(double)total_shrink[o]);
+@^real division@>
+else{@+glue_sign(r)=normal;
+ set_glue_ratio_zero(glue_set(r)); /*there's nothing to shrink*/
+ }
+if ((total_shrink[o] < -x)&&(o==normal)&&(list_ptr(r)!=null))
+ {@+last_badness=1000000;
+ set_glue_ratio_one(glue_set(r)); /*use the maximum shrinkage*/
+ @<Report an overfull vbox and |goto common_ending|, if this box is sufficiently
+bad@>;
+ }
+else if (o==normal) if (list_ptr(r)!=null)
+ @<Report a tight vbox and |goto common_ending|, if this box is sufficiently
+bad@>;
+goto end;
+}
+
+@ @<Report an overfull vbox and |goto common_ending|, if...@>=
+if ((-x-total_shrink[normal] > vfuzz)||(vbadness < 100))
+ {@+print_ln();print_nl("Overfull \\vbox (");
+@.Overfull \\vbox...@>
+ print_scaled(-x-total_shrink[normal]);print("pt too high");
+ goto common_ending;
+ }
+
+@ @<Report a tight vbox and |goto common_ending|, if...@>=
+{@+last_badness=badness(-x, total_shrink[normal]);
+if (last_badness > vbadness)
+ {@+print_ln();print_nl("Tight \\vbox (badness ");print_int(last_badness);
+@.Tight \\vbox...@>
+ goto common_ending;
+ }
+}
+
+@ When a box is being appended to the current vertical list, the
+baselineskip calculation is handled by the |append_to_vlist| routine.
+
+@p static void append_to_vlist(pointer @!b)
+{@+scaled d; /*deficiency of space between baselines*/
+pointer @!p; /*a new glue node*/
+if (prev_depth > ignore_depth)
+ {@+d=width(baseline_skip)-prev_depth-height(b);
+ if (d < line_skip_limit) p=new_param_glue(line_skip_code);
+ else{@+p=new_skip_param(baseline_skip_code);
+ width(temp_ptr)=d; /*|temp_ptr==glue_ptr(p)|*/
+ }
+ link(tail)=p;tail=p;
+ }
+link(tail)=b;tail=b;prev_depth=depth(b);
+}
+
+@* Data structures for math mode.
+When \TeX\ reads a formula that is enclosed between \.\$'s, it constructs an
+{\sl mlist}, which is essentially a tree structure representing that
+formula. An mlist is a linear sequence of items, but we can regard it as
+a tree structure because mlists can appear within mlists. For example, many
+of the entries can be subscripted or superscripted, and such ``scripts''
+are mlists in their own right.
+
+An entire formula is parsed into such a tree before any of the actual
+typesetting is done, because the current style of type is usually not
+known until the formula has been fully scanned. For example, when the
+formula `\.{\$a+b \\over c+d\$}' is being read, there is no way to tell
+that `\.{a+b}' will be in script size until `\.{\\over}' has appeared.
+
+During the scanning process, each element of the mlist being built is
+classified as a relation, a binary operator, an open parenthesis, etc.,
+or as a construct like `\.{\\sqrt}' that must be built up. This classification
+appears in the mlist data structure.
+
+After a formula has been fully scanned, the mlist is converted to an hlist
+so that it can be incorporated into the surrounding text. This conversion is
+controlled by a recursive procedure that decides all of the appropriate
+styles by a ``top-down'' process starting at the outermost level and working
+in towards the subformulas. The formula is ultimately pasted together using
+combinations of horizontal and vertical boxes, with glue and penalty nodes
+inserted as necessary.
+
+An mlist is represented internally as a linked list consisting chiefly
+of ``noads'' (pronounced ``no-adds''), to distinguish them from the somewhat
+similar ``nodes'' in hlists and vlists. Certain kinds of ordinary nodes are
+allowed to appear in mlists together with the noads; \TeX\ tells the difference
+by means of the |type| field, since a noad's |type| is always greater than
+that of a node. An mlist does not contain character nodes, hlist nodes, vlist
+nodes, math nodes, ligature nodes,
+or unset nodes; in particular, each mlist item appears in the
+variable-size part of |mem|, so the |type| field is always present.
+
+@ Each noad is four or more words long. The first word contains the |type|
+and |subtype| and |link| fields that are already so familiar to us; the
+second, third, and fourth words are called the noad's |nucleus|, |subscr|,
+and |supscr| fields.
+
+Consider, for example, the simple formula `\.{\$x\^2\$}', which would be
+parsed into an mlist containing a single element called an |ord_noad|.
+The |nucleus| of this noad is a representation of `\.x', the |subscr| is
+empty, and the |supscr| is a representation of `\.2'.
+
+The |nucleus|, |subscr|, and |supscr| fields are further broken into
+subfields. If |p| points to a noad, and if |q| is one of its principal
+fields (e.g., |q==subscr(p)|), there are several possibilities for the
+subfields, depending on the |math_type| of |q|.
+
+\yskip\hang|math_type(q)==math_char| means that |fam(q)| refers to one of
+the sixteen font families, and |character(q)| is the number of a character
+within a font of that family, as in a character node.
+
+\yskip\hang|math_type(q)==math_text_char| is similar, but the character is
+unsubscripted and unsuperscripted and it is followed immediately by another
+character from the same font. (This |math_type| setting appears only
+briefly during the processing; it is used to suppress unwanted italic
+corrections.)
+
+\yskip\hang|math_type(q)==empty| indicates a field with no value (the
+corresponding attribute of noad |p| is not present).
+
+\yskip\hang|math_type(q)==sub_box| means that |info(q)| points to a box
+node (either an |hlist_node| or a |vlist_node|) that should be used as the
+value of the field. The |shift_amount| in the subsidiary box node is the
+amount by which that box will be shifted downward.
+
+\yskip\hang|math_type(q)==sub_mlist| means that |info(q)| points to
+an mlist; the mlist must be converted to an hlist in order to obtain
+the value of this field.
+
+\yskip\noindent In the latter case, we might have |info(q)==null|. This
+is not the same as |math_type(q)==empty|; for example, `\.{\$P\_\{\}\$}'
+and `\.{\$P\$}' produce different results (the former will not have the
+``italic correction'' added to the width of |P|, but the ``script skip''
+will be added).
+
+The definitions of subfields given here are evidently wasteful of space,
+since a halfword is being used for the |math_type| although only three
+bits would be needed. However, there are hardly ever many noads present at
+once, since they are soon converted to nodes that take up even more space,
+so we can afford to represent them in whatever way simplifies the
+programming.
+
+@d noad_size 4 /*number of words in a normal noad*/
+@d nucleus(A) A+1 /*the |nucleus| field of a noad*/
+@d supscr(A) A+2 /*the |supscr| field of a noad*/
+@d subscr(A) A+3 /*the |subscr| field of a noad*/
+@d math_type(A) link(A) /*a |halfword| in |mem|*/
+@d fam font /*a |quarterword| in |mem|*/
+@d math_char 1 /*|math_type| when the attribute is simple*/
+@d sub_box 2 /*|math_type| when the attribute is a box*/
+@d sub_mlist 3 /*|math_type| when the attribute is a formula*/
+@d math_text_char 4 /*|math_type| when italic correction is dubious*/
+
+@ Each portion of a formula is classified as Ord, Op, Bin, Rel, Open,
+Close, Punct, or Inner, for purposes of spacing and line breaking. An
+|ord_noad|, |op_noad|, |bin_noad|, |rel_noad|, |open_noad|, |close_noad|,
+|punct_noad|, or |inner_noad| is used to represent portions of the various
+types. For example, an `\.=' sign in a formula leads to the creation of a
+|rel_noad| whose |nucleus| field is a representation of an equals sign
+(usually |fam==0|, |character==075|). A formula preceded by \.{\\mathrel}
+also results in a |rel_noad|. When a |rel_noad| is followed by an
+|op_noad|, say, and possibly separated by one or more ordinary nodes (not
+noads), \TeX\ will insert a penalty node (with the current |rel_penalty|)
+just after the formula that corresponds to the |rel_noad|, unless there
+already was a penalty immediately following; and a ``thick space'' will be
+inserted just before the formula that corresponds to the |op_noad|.
+
+A noad of type |ord_noad|, |op_noad|, \dots, |inner_noad| usually
+has a |subtype==normal|. The only exception is that an |op_noad| might
+have |subtype==limits| or |no_limits|, if the normal positioning of
+limits has been overridden for this operator.
+
+@d ord_noad (unset_node+3) /*|type| of a noad classified Ord*/
+@d op_noad (ord_noad+1) /*|type| of a noad classified Op*/
+@d bin_noad (ord_noad+2) /*|type| of a noad classified Bin*/
+@d rel_noad (ord_noad+3) /*|type| of a noad classified Rel*/
+@d open_noad (ord_noad+4) /*|type| of a noad classified Open*/
+@d close_noad (ord_noad+5) /*|type| of a noad classified Close*/
+@d punct_noad (ord_noad+6) /*|type| of a noad classified Punct*/
+@d inner_noad (ord_noad+7) /*|type| of a noad classified Inner*/
+@d limits 1 /*|subtype| of |op_noad| whose scripts are to be above, below*/
+@d no_limits 2 /*|subtype| of |op_noad| whose scripts are to be normal*/
+
+@ A |radical_noad| is five words long; the fifth word is the |left_delimiter|
+field, which usually represents a square root sign.
+
+A |fraction_noad| is six words long; it has a |right_delimiter| field
+as well as a |left_delimiter|.
+
+Delimiter fields are of type |four_quarters|, and they have four subfields
+called |small_fam|, |small_char|, |large_fam|, |large_char|. These subfields
+represent variable-size delimiters by giving the ``small'' and ``large''
+starting characters, as explained in Chapter~17 of {\sl The \TeX book}.
+@:TeXbook}{\sl The \TeX book@>
+
+A |fraction_noad| is actually quite different from all other noads. Not
+only does it have six words, it has |thickness|, |denominator|, and
+|numerator| fields instead of |nucleus|, |subscr|, and |supscr|. The
+|thickness| is a scaled value that tells how thick to make a fraction
+rule; however, the special value |default_code| is used to stand for the
+|default_rule_thickness| of the current size. The |numerator| and
+|denominator| point to mlists that define a fraction; we always have
+$$\hbox{|math_type(numerator)==math_type(denominator)==sub_mlist|}.$$ The
+|left_delimiter| and |right_delimiter| fields specify delimiters that will
+be placed at the left and right of the fraction. In this way, a
+|fraction_noad| is able to represent all of \TeX's operators \.{\\over},
+\.{\\atop}, \.{\\above}, \.{\\overwithdelims}, \.{\\atopwithdelims}, and
+ \.{\\abovewithdelims}.
+
+@d left_delimiter(A) A+4 /*first delimiter field of a noad*/
+@d right_delimiter(A) A+5 /*second delimiter field of a fraction noad*/
+@d radical_noad (inner_noad+1) /*|type| of a noad for square roots*/
+@d radical_noad_size 5 /*number of |mem| words in a radical noad*/
+@d fraction_noad (radical_noad+1) /*|type| of a noad for generalized fractions*/
+@d fraction_noad_size 6 /*number of |mem| words in a fraction noad*/
+@d small_fam(A) mem[A].qqqq.b0 /*|fam| for ``small'' delimiter*/
+@d small_char(A) mem[A].qqqq.b1 /*|character| for ``small'' delimiter*/
+@d large_fam(A) mem[A].qqqq.b2 /*|fam| for ``large'' delimiter*/
+@d large_char(A) mem[A].qqqq.b3 /*|character| for ``large'' delimiter*/
+@d thickness(A) width(A) /*|thickness| field in a fraction noad*/
+@d default_code 010000000000 /*denotes |default_rule_thickness|*/
+@d numerator(A) supscr(A) /*|numerator| field in a fraction noad*/
+@d denominator(A) subscr(A) /*|denominator| field in a fraction noad*/
+
+@ The global variable |empty_field| is set up for initialization of empty
+fields in new noads. Similarly, |null_delimiter| is for the initialization
+of delimiter fields.
+
+@<Glob...@>=
+static two_halves @!empty_field;
+static four_quarters @!null_delimiter;
+
+@ @<Set init...@>=
+empty_field.rh=empty;empty_field.lh=null;@/
+null_delimiter.b0=0;null_delimiter.b1=min_quarterword;@/
+null_delimiter.b2=0;null_delimiter.b3=min_quarterword;
+
+@ The |new_noad| function creates an |ord_noad| that is completely null.
+
+@p static pointer new_noad(void)
+{@+pointer p;
+p=get_node(noad_size);
+type(p)=ord_noad;subtype(p)=normal;
+mem[nucleus(p)].hh=empty_field;
+mem[subscr(p)].hh=empty_field;
+mem[supscr(p)].hh=empty_field;
+return p;
+}
+
+@ A few more kinds of noads will complete the set: An |under_noad| has its
+nucleus underlined; an |over_noad| has it overlined. An |accent_noad| places
+an accent over its nucleus; the accent character appears as
+|fam(accent_chr(p))| and |character(accent_chr(p))|. A |vcenter_noad|
+centers its nucleus vertically with respect to the axis of the formula;
+in such noads we always have |math_type(nucleus(p))==sub_box|.
+
+And finally, we have |left_noad| and |right_noad| types, to implement
+\TeX's \.{\\left} and \.{\\right} as well as \eTeX's \.{\\middle}.
+The |nucleus| of such noads is
+replaced by a |delimiter| field; thus, for example, `\.{\\left(}' produces
+a |left_noad| such that |delimiter(p)| holds the family and character
+codes for all left parentheses. A |left_noad| never appears in an mlist
+except as the first element, and a |right_noad| never appears in an mlist
+except as the last element; furthermore, we either have both a |left_noad|
+and a |right_noad|, or neither one is present. The |subscr| and |supscr|
+fields are always |empty| in a |left_noad| and a |right_noad|.
+
+@d under_noad (fraction_noad+1) /*|type| of a noad for underlining*/
+@d over_noad (under_noad+1) /*|type| of a noad for overlining*/
+@d accent_noad (over_noad+1) /*|type| of a noad for accented subformulas*/
+@d accent_noad_size 5 /*number of |mem| words in an accent noad*/
+@d accent_chr(A) A+4 /*the |accent_chr| field of an accent noad*/
+@d vcenter_noad (accent_noad+1) /*|type| of a noad for \.{\\vcenter}*/
+@d left_noad (vcenter_noad+1) /*|type| of a noad for \.{\\left}*/
+@d right_noad (left_noad+1) /*|type| of a noad for \.{\\right}*/
+@d delimiter(A) nucleus(A) /*|delimiter| field in left and right noads*/
+@d middle_noad 1 /*|subtype| of right noad representing \.{\\middle}*/
+@d scripts_allowed(A) (type(A) >= ord_noad)&&(type(A) < left_noad)
+
+@ Math formulas can also contain instructions like \.{\\textstyle} that
+override \TeX's normal style rules. A |style_node| is inserted into the
+data structure to record such instructions; it is three words long, so it
+is considered a node instead of a noad. The |subtype| is either |display_style|
+or |text_style| or |script_style| or |script_script_style|. The
+second and third words of a |style_node| are not used, but they are
+present because a |choice_node| is converted to a |style_node|.
+
+\TeX\ uses even numbers 0, 2, 4, 6 to encode the basic styles
+|display_style|, \dots, |script_script_style|, and adds~1 to get the
+``cramped'' versions of these styles. This gives a numerical order that
+is backwards from the convention of Appendix~G in {\sl The \TeX book\/};
+i.e., a smaller style has a larger numerical value.
+@:TeXbook}{\sl The \TeX book@>
+
+@d style_node (unset_node+1) /*|type| of a style node*/
+@d style_node_size 3 /*number of words in a style node*/
+@d display_style 0 /*|subtype| for \.{\\displaystyle}*/
+@d text_style 2 /*|subtype| for \.{\\textstyle}*/
+@d script_style 4 /*|subtype| for \.{\\scriptstyle}*/
+@d script_script_style 6 /*|subtype| for \.{\\scriptscriptstyle}*/
+@d cramped 1 /*add this to an uncramped style if you want to cramp it*/
+
+@p static pointer new_style(small_number @!s) /*create a style node*/
+{@+pointer p; /*the new node*/
+p=get_node(style_node_size);type(p)=style_node;
+subtype(p)=s;width(p)=0;depth(p)=0; /*the |width| and |depth| are not used*/
+return p;
+}
+
+@ Finally, the \.{\\mathchoice} primitive creates a |choice_node|, which
+has special subfields |display_mlist|, |text_mlist|, |script_mlist|,
+and |script_script_mlist| pointing to the mlists for each style.
+
+@d choice_node (unset_node+2) /*|type| of a choice node*/
+@d display_mlist(A) info(A+1) /*mlist to be used in display style*/
+@d text_mlist(A) link(A+1) /*mlist to be used in text style*/
+@d script_mlist(A) info(A+2) /*mlist to be used in script style*/
+@d script_script_mlist(A) link(A+2) /*mlist to be used in scriptscript style*/
+
+@p static pointer new_choice(void) /*create a choice node*/
+{@+pointer p; /*the new node*/
+p=get_node(style_node_size);type(p)=choice_node;
+subtype(p)=0; /*the |subtype| is not used*/
+display_mlist(p)=null;text_mlist(p)=null;script_mlist(p)=null;
+script_script_mlist(p)=null;
+return p;
+}
+
+@ Let's consider now the previously unwritten part of |show_node_list|
+that displays the things that can only be present in mlists; this
+program illustrates how to access the data structures just defined.
+
+In the context of the following program, |p| points to a node or noad that
+should be displayed, and the current string contains the ``recursion history''
+that leads to this point. The recursion history consists of a dot for each
+outer level in which |p| is subsidiary to some node, or in which |p| is
+subsidiary to the |nucleus| field of some noad; the dot is replaced by
+`\.\_' or `\.\^' or `\./' or `\.\\' if |p| is descended from the |subscr|
+or |supscr| or |denominator| or |numerator| fields of noads. For example,
+the current string would be `\.{.\^.\_/}' if |p| points to the |ord_noad| for
+|x| in the (ridiculous) formula
+`\.{\$\\sqrt\{a\^\{\\mathinner\{b\_\{c\\over x+y\}\}\}\}\$}'.
+
+@<Cases of |show_node_list| that arise...@>=
+case style_node: print_style(subtype(p));@+break;
+case choice_node: @<Display choice node |p|@>@;@+break;
+case ord_noad: case op_noad: case bin_noad:
+ case rel_noad: case open_noad: case close_noad:
+ case punct_noad: case inner_noad:
+ case radical_noad: case over_noad: case under_noad:
+ case vcenter_noad: case accent_noad:
+ case left_noad: case right_noad: @<Display normal noad |p|@>@;@+break;
+case fraction_noad: @<Display fraction noad |p|@>@;@+break;
+
+@ Here are some simple routines used in the display of noads.
+
+@<Declare procedures needed for displaying the elements of mlists@>=
+static void print_fam_and_char(pointer @!p) /*prints family and character*/
+{@+print_esc("fam");print_int(fam(p));print_char(' ');
+print_ASCII(qo(character(p)));
+}
+@#
+static void print_delimiter(pointer @!p) /*prints a delimiter as 24-bit hex value*/
+{@+int a; /*accumulator*/
+a=small_fam(p)*256+qo(small_char(p));
+a=a*0x1000+large_fam(p)*256+qo(large_char(p));
+if (a < 0) print_int(a); /*this should never happen*/
+else print_hex(a);
+}
+
+@ The next subroutine will descend to another level of recursion when a
+subsidiary mlist needs to be displayed. The parameter |c| indicates what
+character is to become part of the recursion history. An empty mlist is
+distinguished from a field with |math_type(p)==empty|, because these are
+not equivalent (as explained above).
+@^recursion@>
+
+@<Declare procedures needed for displaying...@>=
+static void show_info(void); /*|show_node_list(info(temp_ptr))|*/
+static void print_subsidiary_data(pointer @!p, ASCII_code @!c)
+ /*display a noad field*/
+{@+if (cur_length >= depth_threshold)
+ {@+if (math_type(p)!=empty) print(" []");
+ }
+else{@+append_char(c); /*include |c| in the recursion history*/
+ temp_ptr=p; /*prepare for |show_info| if recursion is needed*/
+ switch (math_type(p)) {
+ case math_char: {@+print_ln();print_current_string();print_fam_and_char(p);
+ } @+break;
+ case sub_box: show_info();@+break; /*recursive call*/
+ case sub_mlist: if (info(p)==null)
+ {@+print_ln();print_current_string();print("{}");
+ }
+ else show_info();@+break; /*recursive call*/
+ default:do_nothing; /*|empty|*/
+ } @/
+ flush_char; /*remove |c| from the recursion history*/
+ }
+}
+
+@ The inelegant introduction of |show_info| in the code above seems better
+than the alternative of using \PASCAL's strange |forward| declaration for a
+procedure with parameters. The \PASCAL\ convention about dropping parameters
+from a post-|forward| procedure is, frankly, so intolerable to the author
+of \TeX\ that he would rather stoop to communication via a global temporary
+variable. (A similar stoopidity occurred with respect to |hlist_out| and
+|vlist_out| above, and it will occur with respect to |mlist_to_hlist| below.)
+@^Knuth, Donald Ervin@>
+@:PASCAL}{\PASCAL@>
+
+@p static void show_info(void) /*the reader will kindly forgive this*/
+{@+show_node_list(info(temp_ptr));
+}
+
+@ @<Declare procedures needed for displaying...@>=
+static void print_style(int @!c)
+{@+switch (c/2) {
+case 0: print_esc("displaystyle");@+break; /*|display_style==0|*/
+case 1: print_esc("textstyle");@+break; /*|text_style==2|*/
+case 2: print_esc("scriptstyle");@+break; /*|script_style==4|*/
+case 3: print_esc("scriptscriptstyle");@+break; /*|script_script_style==6|*/
+default:print("Unknown style!");
+}
+}
+
+@ @<Display choice node |p|@>=
+{@+print_esc("mathchoice");
+append_char('D');show_node_list(display_mlist(p));flush_char;
+append_char('T');show_node_list(text_mlist(p));flush_char;
+append_char('S');show_node_list(script_mlist(p));flush_char;
+append_char('s');show_node_list(script_script_mlist(p));flush_char;
+}
+
+@ @<Display normal noad |p|@>=
+{@+switch (type(p)) {
+case ord_noad: print_esc("mathord");@+break;
+case op_noad: print_esc("mathop");@+break;
+case bin_noad: print_esc("mathbin");@+break;
+case rel_noad: print_esc("mathrel");@+break;
+case open_noad: print_esc("mathopen");@+break;
+case close_noad: print_esc("mathclose");@+break;
+case punct_noad: print_esc("mathpunct");@+break;
+case inner_noad: print_esc("mathinner");@+break;
+case over_noad: print_esc("overline");@+break;
+case under_noad: print_esc("underline");@+break;
+case vcenter_noad: print_esc("vcenter");@+break;
+case radical_noad: {@+print_esc("radical");print_delimiter(left_delimiter(p));
+ } @+break;
+case accent_noad: {@+print_esc("accent");print_fam_and_char(accent_chr(p));
+ } @+break;
+case left_noad: {@+print_esc("left");print_delimiter(delimiter(p));
+ } @+break;
+case right_noad: {@+if (subtype(p)==normal) print_esc("right");
+ else print_esc("middle");
+ print_delimiter(delimiter(p));
+ }
+}
+if (type(p) < left_noad)
+ {@+if (subtype(p)!=normal)
+ if (subtype(p)==limits) print_esc("limits");
+ else print_esc("nolimits");
+ print_subsidiary_data(nucleus(p),'.');
+ }
+print_subsidiary_data(supscr(p),'^');
+print_subsidiary_data(subscr(p),'_');
+}
+
+@ @<Display fraction noad |p|@>=
+{@+print_esc("fraction, thickness ");
+if (thickness(p)==default_code) print("= default");
+else print_scaled(thickness(p));
+if ((small_fam(left_delimiter(p))!=0)||@+
+ (small_char(left_delimiter(p))!=min_quarterword)||@|
+ (large_fam(left_delimiter(p))!=0)||@|
+ (large_char(left_delimiter(p))!=min_quarterword))
+ {@+print(", left-delimiter ");print_delimiter(left_delimiter(p));
+ }
+if ((small_fam(right_delimiter(p))!=0)||@|
+ (small_char(right_delimiter(p))!=min_quarterword)||@|
+ (large_fam(right_delimiter(p))!=0)||@|
+ (large_char(right_delimiter(p))!=min_quarterword))
+ {@+print(", right-delimiter ");print_delimiter(right_delimiter(p));
+ }
+print_subsidiary_data(numerator(p),'\\');
+print_subsidiary_data(denominator(p),'/');
+}
+
+@ That which can be displayed can also be destroyed.
+
+@<Cases of |flush_node_list| that arise...@>=
+case style_node: {@+free_node(p, style_node_size);goto done;
+ }
+case choice_node: {@+flush_node_list(display_mlist(p));
+ flush_node_list(text_mlist(p));
+ flush_node_list(script_mlist(p));
+ flush_node_list(script_script_mlist(p));
+ free_node(p, style_node_size);goto done;
+ }
+case ord_noad: case op_noad: case bin_noad:
+ case rel_noad: case open_noad: case close_noad:
+ case punct_noad: case inner_noad:
+ case radical_noad: case over_noad: case under_noad:
+ case vcenter_noad: case accent_noad: @t@>@;@/
+ {@+if (math_type(nucleus(p)) >= sub_box)
+ flush_node_list(info(nucleus(p)));
+ if (math_type(supscr(p)) >= sub_box)
+ flush_node_list(info(supscr(p)));
+ if (math_type(subscr(p)) >= sub_box)
+ flush_node_list(info(subscr(p)));
+ if (type(p)==radical_noad) free_node(p, radical_noad_size);
+ else if (type(p)==accent_noad) free_node(p, accent_noad_size);
+ else free_node(p, noad_size);
+ goto done;
+ }
+case left_noad: case right_noad: {@+free_node(p, noad_size);goto done;
+ }
+case fraction_noad: {@+flush_node_list(info(numerator(p)));
+ flush_node_list(info(denominator(p)));
+ free_node(p, fraction_noad_size);goto done;
+ }
+
+@* Subroutines for math mode.
+In order to convert mlists to hlists, i.e., noads to nodes, we need several
+subroutines that are conveniently dealt with now.
+
+Let us first introduce the macros that make it easy to get at the parameters and
+other font information. A size code, which is a multiple of 16, is added to a
+family number to get an index into the table of internal font numbers
+for each combination of family and size. (Be alert: Size codes get
+larger as the type gets smaller.)
+
+@d text_size 0 /*size code for the largest size in a family*/
+@d script_size 16 /*size code for the medium size in a family*/
+@d script_script_size 32 /*size code for the smallest size in a family*/
+
+@<Basic printing procedures@>=
+static void print_size(int @!s)
+{@+if (s==text_size) print_esc("textfont");
+else if (s==script_size) print_esc("scriptfont");
+else print_esc("scriptscriptfont");
+}
+
+@ Before an mlist is converted to an hlist, \TeX\ makes sure that
+the fonts in family~2 have enough parameters to be math-symbol
+fonts, and that the fonts in family~3 have enough parameters to be
+math-extension fonts. The math-symbol parameters are referred to by using the
+following macros, which take a size code as their parameter; for example,
+|num1(cur_size)| gives the value of the |num1| parameter for the current size.
+@^parameters for symbols@>
+@^font parameters@>
+
+@d mathsy_end(A) fam_fnt(2+A)]].sc
+@d mathsy(A) font_info[A+param_base[mathsy_end
+@d math_x_height mathsy(5) /*height of `\.x'*/
+@d math_quad mathsy(6) /*\.{18mu}*/
+@d num1 mathsy(8) /*numerator shift-up in display styles*/
+@d num2 mathsy(9) /*numerator shift-up in non-display, non-\.{\\atop}*/
+@d num3 mathsy(10) /*numerator shift-up in non-display \.{\\atop}*/
+@d denom1 mathsy(11) /*denominator shift-down in display styles*/
+@d denom2 mathsy(12) /*denominator shift-down in non-display styles*/
+@d sup1 mathsy(13) /*superscript shift-up in uncramped display style*/
+@d sup2 mathsy(14) /*superscript shift-up in uncramped non-display*/
+@d sup3 mathsy(15) /*superscript shift-up in cramped styles*/
+@d sub1 mathsy(16) /*subscript shift-down if superscript is absent*/
+@d sub2 mathsy(17) /*subscript shift-down if superscript is present*/
+@d sup_drop mathsy(18) /*superscript baseline below top of large box*/
+@d sub_drop mathsy(19) /*subscript baseline below bottom of large box*/
+@d delim1 mathsy(20) /*size of \.{\\atopwithdelims} delimiters
+ in display styles*/
+@d delim2 mathsy(21) /*size of \.{\\atopwithdelims} delimiters in non-displays*/
+@d axis_height mathsy(22) /*height of fraction lines above the baseline*/
+@d total_mathsy_params 22
+
+@ The math-extension parameters have similar macros, but the size code is
+omitted (since it is always |cur_size| when we refer to such parameters).
+@^parameters for symbols@>
+@^font parameters@>
+
+@d mathex(A) font_info[A+param_base[fam_fnt(3+cur_size)]].sc
+@d default_rule_thickness mathex(8) /*thickness of \.{\\over} bars*/
+@d big_op_spacing1 mathex(9) /*minimum clearance above a displayed op*/
+@d big_op_spacing2 mathex(10) /*minimum clearance below a displayed op*/
+@d big_op_spacing3 mathex(11) /*minimum baselineskip above displayed op*/
+@d big_op_spacing4 mathex(12) /*minimum baselineskip below displayed op*/
+@d big_op_spacing5 mathex(13) /*padding above and below displayed limits*/
+@d total_mathex_params 13
+
+@ We also need to compute the change in style between mlists and their
+subsidiaries. The following macros define the subsidiary style for
+an overlined nucleus (|cramped_style|), for a subscript or a superscript
+(|sub_style| or |sup_style|), or for a numerator or denominator (|num_style|
+or |denom_style|).
+
+@d cramped_style(A) 2*(A/2)+cramped /*cramp the style*/
+@d sub_style(A) 2*(A/4)+script_style+cramped /*smaller and cramped*/
+@d sup_style(A) 2*(A/4)+script_style+(A%2) /*smaller*/
+@d num_style(A) A+2-2*(A/6) /*smaller unless already script-script*/
+@d denom_style(A) 2*(A/2)+cramped+2-2*(A/6) /*smaller, cramped*/
+
+@ When the style changes, the following piece of program computes associated
+information:
+
+@<Set up the values of |cur_size| and |cur_mu|, based on |cur_style|@>=
+{@+if (cur_style < script_style) cur_size=text_size;
+else cur_size=16*((cur_style-text_style)/2);
+cur_mu=x_over_n(math_quad(cur_size), 18);
+}
+
+@ Here is a function that returns a pointer to a rule node having a given
+thickness |t|. The rule will extend horizontally to the boundary of the vlist
+that eventually contains it.
+
+@p static pointer fraction_rule(scaled @!t)
+ /*construct the bar for a fraction*/
+{@+pointer p; /*the new node*/
+p=new_rule();height(p)=t;depth(p)=0;return p;
+}
+
+@ The |overbar| function returns a pointer to a vlist box that consists of
+a given box |b|, above which has been placed a kern of height |k| under a
+fraction rule of thickness |t| under additional space of height |t|.
+
+@p static pointer overbar(pointer @!b, scaled @!k, scaled @!t)
+{@+pointer p, @!q; /*nodes being constructed*/
+p=new_kern(k);link(p)=b;q=fraction_rule(t);link(q)=p;
+p=new_kern(t);link(p)=q;return vpack(p, natural);
+}
+
+@ The |var_delimiter| function, which finds or constructs a sufficiently
+large delimiter, is the most interesting of the auxiliary functions that
+currently concern us. Given a pointer |d| to a delimiter field in some noad,
+together with a size code |s| and a vertical distance |v|, this function
+returns a pointer to a box that contains the smallest variant of |d| whose
+height plus depth is |v| or more. (And if no variant is large enough, it
+returns the largest available variant.) In particular, this routine will
+construct arbitrarily large delimiters from extensible components, if
+|d| leads to such characters.
+
+The value returned is a box whose |shift_amount| has been set so that
+the box is vertically centered with respect to the axis in the given size.
+If a built-up symbol is returned, the height of the box before shifting
+will be the height of its topmost component.
+
+@p @t\4@>@<Declare subprocedures for |var_delimiter|@>@;
+static pointer var_delimiter(pointer @!d, small_number @!s, scaled @!v)
+{@+
+pointer b; /*the box that will be constructed*/
+internal_font_number @!f, @!g; /*best-so-far and tentative font codes*/
+quarterword @!c, @!x, @!y; /*best-so-far and tentative character codes*/
+int @!m, @!n; /*the number of extensible pieces*/
+scaled @!u; /*height-plus-depth of a tentative character*/
+scaled @!w; /*largest height-plus-depth so far*/
+four_quarters @!q; /*character info*/
+eight_bits @!hd; /*height-depth byte*/
+four_quarters @!r; /*extensible pieces*/
+small_number @!z; /*runs through font family members*/
+bool @!large_attempt; /*are we trying the ``large'' variant?*/
+f=null_font;w=0;large_attempt=false;
+z=small_fam(d);x=small_char(d);
+loop@+{@+@<Look at the variants of |(z,x)|; set |f| and |c| whenever a better
+character is found; |goto found| as soon as a large enough variant is encountered@>;
+ if (large_attempt) goto found; /*there were none large enough*/
+ large_attempt=true;z=large_fam(d);x=large_char(d);
+ }
+found: if (f!=null_font)
+ @<Make variable |b| point to a box for |(f,c)|@>;
+else{@+b=new_null_box();
+ width(b)=null_delimiter_space; /*use this width if no delimiter was found*/
+ }
+shift_amount(b)=half(height(b)-depth(b))-axis_height(s);
+return b;
+}
+
+@ The search process is complicated slightly by the facts that some of the
+characters might not be present in some of the fonts, and they might not
+be probed in increasing order of height.
+
+@<Look at the variants of |(z,x)|; set |f| and |c|...@>=
+if ((z!=0)||(x!=min_quarterword))
+ {@+z=z+s+16;
+ @/do@+{z=z-16;g=fam_fnt(z);
+ if (g!=null_font)
+ @<Look at the list of characters starting with |x| in font |g|; set |f|
+and |c| whenever a better character is found; |goto found| as soon as a large
+enough variant is encountered@>;
+ }@+ while (!(z < 16));
+ }
+
+@ @<Look at the list of characters starting with |x|...@>=
+{@+y=x;
+if ((qo(y) >= font_bc[g])&&(qo(y) <= font_ec[g]))
+ {@+resume: q=char_info(g, y);
+ if (char_exists(q))
+ {@+if (char_tag(q)==ext_tag)
+ {@+f=g;c=y;goto found;
+ }
+ hd=height_depth(q);
+ u=char_height(g, hd)+char_depth(g, hd);
+ if (u > w)
+ {@+f=g;c=y;w=u;
+ if (u >= v) goto found;
+ }
+ if (char_tag(q)==list_tag)
+ {@+y=rem_byte(q);goto resume;
+ }
+ }
+ }
+}
+
+@ Here is a subroutine that creates a new box, whose list contains a
+single character, and whose width includes the italic correction for
+that character. The height or depth of the box will be negative, if
+the height or depth of the character is negative; thus, this routine
+may deliver a slightly different result than |hpack| would produce.
+
+@<Declare subprocedures for |var_delimiter|@>=
+static pointer char_box(internal_font_number @!f, quarterword @!c)
+{@+four_quarters q;
+eight_bits @!hd; /*|height_depth| byte*/
+pointer @!b, @!p; /*the new box and its character node*/
+q=char_info(f, c);hd=height_depth(q);
+b=new_null_box();width(b)=char_width(f, q)+char_italic(f, q);
+height(b)=char_height(f, hd);depth(b)=char_depth(f, hd);
+p=get_avail();character(p)=c;font(p)=f;list_ptr(b)=p;return b;
+}
+
+@ When the following code is executed, |char_tag(q)| will be equal to
+|ext_tag| if and only if a built-up symbol is supposed to be returned.
+
+@<Make variable |b| point to a box for |(f,c)|@>=
+if (char_tag(q)==ext_tag)
+ @<Construct an extensible character in a new box |b|, using recipe |rem_byte(q)|
+and font |f|@>@;
+else b=char_box(f, c)
+
+@ When we build an extensible character, it's handy to have the
+following subroutine, which puts a given character on top
+of the characters already in box |b|:
+
+@<Declare subprocedures for |var_delimiter|@>=
+static void stack_into_box(pointer @!b, internal_font_number @!f,
+ quarterword @!c)
+{@+pointer p; /*new node placed into |b|*/
+p=char_box(f, c);link(p)=list_ptr(b);list_ptr(b)=p;
+height(b)=height(p);
+}
+
+@ Another handy subroutine computes the height plus depth of
+a given character:
+
+@<Declare subprocedures for |var_delimiter|@>=
+static scaled height_plus_depth(internal_font_number @!f, quarterword @!c)
+{@+four_quarters q;
+eight_bits @!hd; /*|height_depth| byte*/
+q=char_info(f, c);hd=height_depth(q);
+return char_height(f, hd)+char_depth(f, hd);
+}
+
+@ @<Construct an extensible...@>=
+{@+b=new_null_box();
+type(b)=vlist_node;
+r=font_info[exten_base[f]+rem_byte(q)].qqqq;@/
+@<Compute the minimum suitable height, |w|, and the corresponding number of
+extension steps, |n|; also set |width(b)|@>;
+c=ext_bot(r);
+if (c!=min_quarterword) stack_into_box(b, f, c);
+c=ext_rep(r);
+for (m=1; m<=n; m++) stack_into_box(b, f, c);
+c=ext_mid(r);
+if (c!=min_quarterword)
+ {@+stack_into_box(b, f, c);c=ext_rep(r);
+ for (m=1; m<=n; m++) stack_into_box(b, f, c);
+ }
+c=ext_top(r);
+if (c!=min_quarterword) stack_into_box(b, f, c);
+depth(b)=w-height(b);
+}
+
+@ The width of an extensible character is the width of the repeatable
+module. If this module does not have positive height plus depth,
+we don't use any copies of it, otherwise we use as few as possible
+(in groups of two if there is a middle part).
+
+@<Compute the minimum suitable height, |w|, and...@>=
+c=ext_rep(r);u=height_plus_depth(f, c);
+w=0;q=char_info(f, c);width(b)=char_width(f, q)+char_italic(f, q);@/
+c=ext_bot(r);@+if (c!=min_quarterword) w=w+height_plus_depth(f, c);
+c=ext_mid(r);@+if (c!=min_quarterword) w=w+height_plus_depth(f, c);
+c=ext_top(r);@+if (c!=min_quarterword) w=w+height_plus_depth(f, c);
+n=0;
+if (u > 0) while (w < v)
+ {@+w=w+u;incr(n);
+ if (ext_mid(r)!=min_quarterword) w=w+u;
+ }
+
+@ The next subroutine is much simpler; it is used for numerators and
+denominators of fractions as well as for displayed operators and
+their limits above and below. It takes a given box~|b| and
+changes it so that the new box is centered in a box of width~|w|.
+The centering is done by putting \.{\\hss} glue at the left and right
+of the list inside |b|, then packaging the new box; thus, the
+actual box might not really be centered, if it already contains
+infinite glue.
+
+The given box might contain a single character whose italic correction
+has been added to the width of the box; in this case a compensating
+kern is inserted.
+
+@p static pointer rebox(pointer @!b, scaled @!w)
+{@+pointer p; /*temporary register for list manipulation*/
+internal_font_number @!f; /*font in a one-character box*/
+scaled @!v; /*width of a character without italic correction*/
+if ((width(b)!=w)&&(list_ptr(b)!=null))
+ {@+if (type(b)==vlist_node) b=hpack(b, natural);
+ p=list_ptr(b);
+ if ((is_char_node(p))&&(link(p)==null))
+ {@+f=font(p);v=char_width(f, char_info(f, character(p)));
+ if (v!=width(b)) link(p)=new_kern(width(b)-v);
+ }
+ free_node(b, box_node_size);
+ b=new_glue(ss_glue);link(b)=p;
+ while (link(p)!=null) p=link(p);
+ link(p)=new_glue(ss_glue);
+ return hpack(b, w, exactly);
+ }
+else{@+width(b)=w;return b;
+ }
+}
+
+@ Here is a subroutine that creates a new glue specification from another
+one that is expressed in `\.{mu}', given the value of the math unit.
+
+@d mu_mult(A) nx_plus_y(n, A, xn_over_d(A, f, 0200000))
+
+@p static pointer math_glue(pointer @!g, scaled @!m)
+{@+pointer p; /*the new glue specification*/
+int @!n; /*integer part of |m|*/
+scaled @!f; /*fraction part of |m|*/
+n=x_over_n(m, 0200000);f=rem;@/
+if (f < 0)
+ {@+decr(n);f=f+0200000;
+ }
+p=get_node(glue_spec_size);
+width(p)=mu_mult(width(g)); /*convert \.{mu} to \.{pt}*/
+stretch_order(p)=stretch_order(g);
+if (stretch_order(p)==normal) stretch(p)=mu_mult(stretch(g));
+else stretch(p)=stretch(g);
+shrink_order(p)=shrink_order(g);
+if (shrink_order(p)==normal) shrink(p)=mu_mult(shrink(g));
+else shrink(p)=shrink(g);
+return p;
+}
+
+@ The |math_kern| subroutine removes |mu_glue| from a kern node, given
+the value of the math unit.
+
+@p static void math_kern(pointer @!p, scaled @!m)
+{@+int @!n; /*integer part of |m|*/
+scaled @!f; /*fraction part of |m|*/
+if (subtype(p)==mu_glue)
+ {@+n=x_over_n(m, 0200000);f=rem;@/
+ if (f < 0)
+ {@+decr(n);f=f+0200000;
+ }
+ width(p)=mu_mult(width(p));subtype(p)=explicit;
+ }
+}
+
+@ Sometimes it is necessary to destroy an mlist. The following
+subroutine empties the current list, assuming that |abs(mode)==mmode|.
+
+@p static void flush_math(void)
+{@+flush_node_list(link(head));flush_node_list(incompleat_noad);
+link(head)=null;tail=head;incompleat_noad=null;
+}
+
+@* Typesetting math formulas.
+\TeX's most important routine for dealing with formulas is called
+|mlist_to_hlist|. After a formula has been scanned and represented as an
+mlist, this routine converts it to an hlist that can be placed into a box
+or incorporated into the text of a paragraph. There are three implicit
+parameters, passed in global variables: |cur_mlist| points to the first
+node or noad in the given mlist (and it might be |null|); |cur_style| is a
+style code; and |mlist_penalties| is |true| if penalty nodes for potential
+line breaks are to be inserted into the resulting hlist. After
+|mlist_to_hlist| has acted, |link(temp_head)| points to the translated hlist.
+
+Since mlists can be inside mlists, the procedure is recursive. And since this
+is not part of \TeX's inner loop, the program has been written in a manner
+that stresses compactness over efficiency.
+@^recursion@>
+
+@<Glob...@>=
+static pointer @!cur_mlist; /*beginning of mlist to be translated*/
+static small_number @!cur_style; /*style code at current place in the list*/
+static small_number @!cur_size; /*size code corresponding to |cur_style|*/
+static scaled @!cur_mu; /*the math unit width corresponding to |cur_size|*/
+static bool @!mlist_penalties; /*should |mlist_to_hlist| insert penalties?*/
+
+@ The recursion in |mlist_to_hlist| is due primarily to a subroutine
+called |clean_box| that puts a given noad field into a box using a given
+math style; |mlist_to_hlist| can call |clean_box|, which can call
+|mlist_to_hlist|.
+@^recursion@>
+
+The box returned by |clean_box| is ``clean'' in the
+sense that its |shift_amount| is zero.
+
+@p static void mlist_to_hlist(void);@/
+static pointer clean_box(pointer @!p, small_number @!s)
+{@+
+pointer q; /*beginning of a list to be boxed*/
+small_number @!save_style; /*|cur_style| to be restored*/
+pointer @!x; /*box to be returned*/
+pointer @!r; /*temporary pointer*/
+switch (math_type(p)) {
+case math_char: {@+cur_mlist=new_noad();mem[nucleus(cur_mlist)]=mem[p];
+ } @+break;
+case sub_box: {@+q=info(p);goto found;
+ }
+case sub_mlist: cur_mlist=info(p);@+break;
+default:{@+q=new_null_box();goto found;
+ }
+} @/
+save_style=cur_style;cur_style=s;mlist_penalties=false;@/
+mlist_to_hlist();q=link(temp_head); /*recursive call*/
+cur_style=save_style; /*restore the style*/
+@<Set up the values of |cur_size| and |cur_mu|, based on |cur_style|@>;
+found: if (is_char_node(q)||(q==null)) x=hpack(q, natural);
+ else if ((link(q)==null)&&(type(q) <= vlist_node)&&(shift_amount(q)==0))
+ x=q; /*it's already clean*/
+ else x=hpack(q, natural);
+@<Simplify a trivial box@>;
+return x;
+}
+
+@ Here we save memory space in a common case.
+
+@<Simplify a trivial box@>=
+q=list_ptr(x);
+if (is_char_node(q))
+ {@+r=link(q);
+ if (r!=null) if (link(r)==null) if (!is_char_node(r))
+ if (type(r)==kern_node) /*unneeded italic correction*/
+ {@+free_node(r, small_node_size);link(q)=null;
+ }
+ }
+
+@ It is convenient to have a procedure that converts a |math_char|
+field to an ``unpacked'' form. The |fetch| routine sets |cur_f|, |cur_c|,
+and |cur_i| to the font code, character code, and character information bytes of
+a given noad field. It also takes care of issuing error messages for
+nonexistent characters; in such cases, |char_exists(cur_i)| will be |false|
+after |fetch| has acted, and the field will also have been reset to |empty|.
+
+@p static void fetch(pointer @!a) /*unpack the |math_char| field |a|*/
+{@+cur_c=character(a);cur_f=fam_fnt(fam(a)+cur_size);
+if (cur_f==null_font)
+ @<Complain about an undefined family and set |cur_i| null@>@;
+else{@+if ((qo(cur_c) >= font_bc[cur_f])&&(qo(cur_c) <= font_ec[cur_f]))
+ cur_i=char_info(cur_f, cur_c);
+ else cur_i=null_character;
+ if (!(char_exists(cur_i)))
+ {@+char_warning(cur_f, qo(cur_c));
+ math_type(a)=empty;cur_i=null_character;
+ }
+ }
+}
+
+@ @<Complain about an undefined family...@>=
+{@+print_err("");print_size(cur_size);print_char(' ');
+print_int(fam(a));print(" is undefined (character ");
+print_ASCII(qo(cur_c));print_char(')');
+help4("Somewhere in the math formula just ended, you used the",@/
+"stated character from an undefined font family. For example,",@/
+"plain TeX doesn't allow \\it or \\sl in subscripts. Proceed,",@/
+"and I'll try to forget that I needed that character.");
+error();cur_i=null_character;math_type(a)=empty;
+}
+
+@ The outputs of |fetch| are placed in global variables.
+
+@<Glob...@>=
+static internal_font_number @!cur_f; /*the |font| field of a |math_char|*/
+static quarterword @!cur_c; /*the |character| field of a |math_char|*/
+static four_quarters @!cur_i; /*the |char_info| of a |math_char|,
+ or a lig/kern instruction*/
+
+@ We need to do a lot of different things, so |mlist_to_hlist| makes two
+passes over the given mlist.
+
+The first pass does most of the processing: It removes ``mu'' spacing from
+glue, it recursively evaluates all subsidiary mlists so that only the
+top-level mlist remains to be handled, it puts fractions and square roots
+and such things into boxes, it attaches subscripts and superscripts, and
+it computes the overall height and depth of the top-level mlist so that
+the size of delimiters for a |left_noad| and a |right_noad| will be known.
+The hlist resulting from each noad is recorded in that noad's |new_hlist|
+field, an integer field that replaces the |nucleus| or |thickness|.
+@^recursion@>
+
+The second pass eliminates all noads and inserts the correct glue and
+penalties between nodes.
+
+@d new_hlist(A) mem[nucleus(A)].i /*the translation of an mlist*/
+
+@ Here is the overall plan of |mlist_to_hlist|, and the list of its
+local variables.
+
+@p @t\4@>@<Declare math construction procedures@>@;
+static void mlist_to_hlist(void)
+{@+
+
+pointer mlist; /*beginning of the given list*/
+bool @!penalties; /*should penalty nodes be inserted?*/
+small_number @!style; /*the given style*/
+small_number @!save_style; /*holds |cur_style| during recursion*/
+pointer @!q; /*runs through the mlist*/
+pointer @!r; /*the most recent noad preceding |q|*/
+small_number @!r_type; /*the |type| of noad |r|, or |op_noad| if |r==null|*/
+small_number @!t; /*the effective |type| of noad |q| during the second pass*/
+pointer @!p, @!x, @!y, @!z; /*temporary registers for list construction*/
+int @!pen; /*a penalty to be inserted*/
+small_number @!s; /*the size of a noad to be deleted*/
+scaled @!max_h, @!max_d; /*maximum height and depth of the list translated so far*/
+scaled @!delta; /*offset between subscript and superscript*/
+mlist=cur_mlist;penalties=mlist_penalties;
+style=cur_style; /*tuck global parameters away as local variables*/
+q=mlist;r=null;r_type=op_noad;max_h=0;max_d=0;
+@<Set up the values of |cur_size| and |cur_mu|, based on |cur_style|@>;
+while (q!=null) @<Process node-or-noad |q| as much as possible in preparation
+for the second pass of |mlist_to_hlist|, then move to the next item in the
+mlist@>;
+@<Convert \(a)a final |bin_noad| to an |ord_noad|@>;
+@<Make a second pass over the mlist, removing all noads and inserting the
+proper spacing and penalties@>;
+}
+
+@ We use the fact that no character nodes appear in an mlist, hence
+the field |type(q)| is always present.
+
+@<Process node-or-noad...@>=
+{@+@<Do first-pass processing based on |type(q)|; |goto done_with_noad| if
+a noad has been fully processed, |goto check_dimensions| if it has been translated
+into |new_hlist(q)|, or |goto done_with_node| if a node has been fully processed@>;
+check_dimensions: z=hpack(new_hlist(q), natural);
+if (height(z) > max_h) max_h=height(z);
+if (depth(z) > max_d) max_d=depth(z);
+free_node(z, box_node_size);
+done_with_noad: r=q;r_type=type(r);
+if (r_type==right_noad)
+ {@+r_type=left_noad;cur_style=style;@<Set up the values...@>;
+ }
+done_with_node: q=link(q);
+}
+
+@ One of the things we must do on the first pass is change a |bin_noad| to
+an |ord_noad| if the |bin_noad| is not in the context of a binary operator.
+The values of |r| and |r_type| make this fairly easy.
+
+@<Do first-pass processing...@>=
+reswitch: delta=0;
+switch (type(q)) {
+case bin_noad: switch (r_type) {
+ case bin_noad: case op_noad: case rel_noad:
+ case open_noad: case punct_noad: case left_noad:
+ {@+type(q)=ord_noad;goto reswitch;
+ }
+ default:do_nothing;
+ } @+break;
+case rel_noad: case close_noad: case punct_noad:
+ case right_noad: {@+@t@>@;@/
+ @<Convert \(a)a final |bin_noad| to an |ord_noad|@>;
+ if (type(q)==right_noad) goto done_with_noad;
+ } @+break;
+@t\4@>@<Cases for noads that can follow a |bin_noad|@>@;
+@t\4@>@<Cases for nodes that can appear in an mlist, after which we |goto
+done_with_node|@>@;
+default:confusion("mlist1");
+@:this can't happen mlist1}{\quad mlist1@>
+} @/
+@<Convert \(n)|nucleus(q)| to an hlist and attach the sub/superscripts@>@;
+
+@ @<Convert \(a)a final |bin_noad| to an |ord_noad|@>=
+if (r_type==bin_noad) type(r)=ord_noad
+
+@ @<Cases for nodes that can appear in an mlist...@>=
+case style_node: {@+cur_style=subtype(q);
+ @<Set up the values of |cur_size| and |cur_mu|, based on |cur_style|@>;
+ goto done_with_node;
+ }
+case choice_node: @<Change this node to a style node followed by the correct
+choice, then |goto done_with_node|@>@;
+case ins_node: case mark_node: case adjust_node:
+ case whatsit_node: case penalty_node:
+ case disc_node: goto done_with_node;
+case rule_node: {@+if (height(q) > max_h) max_h=height(q);
+ if (depth(q) > max_d) max_d=depth(q);goto done_with_node;
+ }
+case glue_node: {@+@<Convert \(m)math glue to ordinary glue@>;
+ goto done_with_node;
+ }
+case kern_node: {@+math_kern(q, cur_mu);goto done_with_node;
+ }
+
+@ @d choose_mlist(A) {@+p=A(q);A(q)=null;@+}
+
+@<Change this node to a style node...@>=
+{@+switch (cur_style/2) {
+case 0: choose_mlist(display_mlist)@;@+break; /*|display_style==0|*/
+case 1: choose_mlist(text_mlist)@;@+break; /*|text_style==2|*/
+case 2: choose_mlist(script_mlist)@;@+break; /*|script_style==4|*/
+case 3: choose_mlist(script_script_mlist); /*|script_script_style==6|*/
+} /*there are no other cases*/
+flush_node_list(display_mlist(q));
+flush_node_list(text_mlist(q));
+flush_node_list(script_mlist(q));
+flush_node_list(script_script_mlist(q));@/
+type(q)=style_node;subtype(q)=cur_style;width(q)=0;depth(q)=0;
+if (p!=null)
+ {@+z=link(q);link(q)=p;
+ while (link(p)!=null) p=link(p);
+ link(p)=z;
+ }
+goto done_with_node;
+}
+
+@ Conditional math glue (`\.{\\nonscript}') results in a |glue_node|
+pointing to |zero_glue|, with |subtype(q)==cond_math_glue|; in such a case
+the node following will be eliminated if it is a glue or kern node and if the
+current size is different from |text_size|. Unconditional math glue
+(`\.{\\muskip}') is converted to normal glue by multiplying the dimensions
+by |cur_mu|.
+@!@:non\_script\_}{\.{\\nonscript} primitive@>
+
+@<Convert \(m)math glue to ordinary glue@>=
+if (subtype(q)==mu_glue)
+ {@+x=glue_ptr(q);
+ y=math_glue(x, cur_mu);delete_glue_ref(x);glue_ptr(q)=y;
+ subtype(q)=normal;
+ }
+else if ((cur_size!=text_size)&&(subtype(q)==cond_math_glue))
+ {@+p=link(q);
+ if (p!=null) if ((type(p)==glue_node)||(type(p)==kern_node))
+ {@+link(q)=link(p);link(p)=null;flush_node_list(p);
+ }
+ }
+
+@ @<Cases for noads that can follow a |bin_noad|@>=
+case left_noad: goto done_with_noad;
+case fraction_noad: {@+make_fraction(q);goto check_dimensions;
+ }
+case op_noad: {@+delta=make_op(q);
+ if (subtype(q)==limits) goto check_dimensions;
+ } @+break;
+case ord_noad: make_ord(q);@+break;
+case open_noad: case inner_noad: do_nothing;@+break;
+case radical_noad: make_radical(q);@+break;
+case over_noad: make_over(q);@+break;
+case under_noad: make_under(q);@+break;
+case accent_noad: make_math_accent(q);@+break;
+case vcenter_noad: make_vcenter(q);@+break;
+
+@ Most of the actual construction work of |mlist_to_hlist| is done
+by procedures with names
+like |make_fraction|, |make_radical|, etc. To illustrate
+the general setup of such procedures, let's begin with a couple of
+simple ones.
+
+@<Declare math...@>=
+static void make_over(pointer @!q)
+{@+info(nucleus(q))=@|
+ overbar(clean_box(nucleus(q), cramped_style(cur_style)),@|
+ 3*default_rule_thickness, default_rule_thickness);
+math_type(nucleus(q))=sub_box;
+}
+
+@ @<Declare math...@>=
+static void make_under(pointer @!q)
+{@+pointer p, @!x, @!y; /*temporary registers for box construction*/
+scaled @!delta; /*overall height plus depth*/
+x=clean_box(nucleus(q), cur_style);
+p=new_kern(3*default_rule_thickness);link(x)=p;
+link(p)=fraction_rule(default_rule_thickness);
+y=vpack(x, natural);
+delta=height(y)+depth(y)+default_rule_thickness;
+height(y)=height(x);depth(y)=delta-height(y);
+info(nucleus(q))=y;math_type(nucleus(q))=sub_box;
+}
+
+@ @<Declare math...@>=
+static void make_vcenter(pointer @!q)
+{@+pointer v; /*the box that should be centered vertically*/
+scaled @!delta; /*its height plus depth*/
+v=info(nucleus(q));
+if (type(v)!=vlist_node) confusion("vcenter");
+@:this can't happen vcenter}{\quad vcenter@>
+delta=height(v)+depth(v);
+height(v)=axis_height(cur_size)+half(delta);
+depth(v)=delta-height(v);
+}
+
+@ According to the rules in the \.{DVI} file specifications, we ensure alignment
+@^square roots@>
+between a square root sign and the rule above its nucleus by assuming that the
+baseline of the square-root symbol is the same as the bottom of the rule. The
+height of the square-root symbol will be the thickness of the rule, and the
+depth of the square-root symbol should exceed or equal the height-plus-depth
+of the nucleus plus a certain minimum clearance~|clr|. The symbol will be
+placed so that the actual clearance is |clr| plus half the excess.
+
+@<Declare math...@>=
+static void make_radical(pointer @!q)
+{@+pointer x, @!y; /*temporary registers for box construction*/
+scaled @!delta, @!clr; /*dimensions involved in the calculation*/
+x=clean_box(nucleus(q), cramped_style(cur_style));
+if (cur_style < text_style) /*display style*/
+ clr=default_rule_thickness+(abs(math_x_height(cur_size))/4);
+else{@+clr=default_rule_thickness;clr=clr+(abs(clr)/4);
+ }
+y=var_delimiter(left_delimiter(q), cur_size, height(x)+depth(x)+clr+
+ default_rule_thickness);
+delta=depth(y)-(height(x)+depth(x)+clr);
+if (delta > 0) clr=clr+half(delta); /*increase the actual clearance*/
+shift_amount(y)=-(height(x)+clr);
+link(y)=overbar(x, clr, height(y));
+info(nucleus(q))=hpack(y, natural);math_type(nucleus(q))=sub_box;
+}
+
+@ Slants are not considered when placing accents in math mode. The accenter is
+centered over the accentee, and the accent width is treated as zero with
+respect to the size of the final box.
+
+@<Declare math...@>=
+static void make_math_accent(pointer @!q)
+{@+
+pointer p, @!x, @!y; /*temporary registers for box construction*/
+int @!a; /*address of lig/kern instruction*/
+quarterword @!c; /*accent character*/
+internal_font_number @!f; /*its font*/
+four_quarters @!i; /*its |char_info|*/
+scaled @!s; /*amount to skew the accent to the right*/
+scaled @!h; /*height of character being accented*/
+scaled @!delta; /*space to remove between accent and accentee*/
+scaled @!w; /*width of the accentee, not including sub/superscripts*/
+fetch(accent_chr(q));
+if (char_exists(cur_i))
+ {@+i=cur_i;c=cur_c;f=cur_f;@/
+ @<Compute the amount of skew@>;
+ x=clean_box(nucleus(q), cramped_style(cur_style));w=width(x);h=height(x);
+ @<Switch to a larger accent if available and appropriate@>;
+ if (h < x_height(f)) delta=h;@+else delta=x_height(f);
+ if ((math_type(supscr(q))!=empty)||(math_type(subscr(q))!=empty))
+ if (math_type(nucleus(q))==math_char)
+ @<Swap the subscript and superscript into box |x|@>;
+ y=char_box(f, c);
+ shift_amount(y)=s+half(w-width(y));
+ width(y)=0;p=new_kern(-delta);link(p)=x;link(y)=p;
+ y=vpack(y, natural);width(y)=width(x);
+ if (height(y) < h) @<Make the height of box |y| equal to |h|@>;
+ info(nucleus(q))=y;
+ math_type(nucleus(q))=sub_box;
+ }
+}
+
+@ @<Make the height of box |y|...@>=
+{@+p=new_kern(h-height(y));link(p)=list_ptr(y);list_ptr(y)=p;
+height(y)=h;
+}
+
+@ @<Switch to a larger accent if available and appropriate@>=
+loop@+{@+if (char_tag(i)!=list_tag) goto done;
+ y=rem_byte(i);
+ i=char_info(f, y);
+ if (!char_exists(i)) goto done;
+ if (char_width(f, i) > w) goto done;
+ c=y;
+ }
+done:
+
+@ @<Compute the amount of skew@>=
+s=0;
+if (math_type(nucleus(q))==math_char)
+ {@+fetch(nucleus(q));
+ if (char_tag(cur_i)==lig_tag)
+ {@+a=lig_kern_start(cur_f, cur_i);
+ cur_i=font_info[a].qqqq;
+ if (skip_byte(cur_i) > stop_flag)
+ {@+a=lig_kern_restart(cur_f, cur_i);
+ cur_i=font_info[a].qqqq;
+ }
+ loop@+{@+if (qo(next_char(cur_i))==skew_char[cur_f])
+ {@+if (op_byte(cur_i) >= kern_flag)
+ if (skip_byte(cur_i) <= stop_flag) s=char_kern(cur_f, cur_i);
+ goto done1;
+ }
+ if (skip_byte(cur_i) >= stop_flag) goto done1;
+ a=a+qo(skip_byte(cur_i))+1;
+ cur_i=font_info[a].qqqq;
+ }
+ }
+ }
+done1:
+
+@ @<Swap the subscript and superscript into box |x|@>=
+{@+flush_node_list(x);x=new_noad();
+mem[nucleus(x)]=mem[nucleus(q)];
+mem[supscr(x)]=mem[supscr(q)];
+mem[subscr(x)]=mem[subscr(q)];@/
+mem[supscr(q)].hh=empty_field;
+mem[subscr(q)].hh=empty_field;@/
+math_type(nucleus(q))=sub_mlist;info(nucleus(q))=x;
+x=clean_box(nucleus(q), cur_style);delta=delta+height(x)-h;h=height(x);
+}
+
+@ The |make_fraction| procedure is a bit different because it sets
+|new_hlist(q)| directly rather than making a sub-box.
+
+@<Declare math...@>=
+static void make_fraction(pointer @!q)
+{@+pointer p, @!v, @!x, @!y, @!z; /*temporary registers for box construction*/
+scaled @!delta, @!delta1, @!delta2, @!shift_up, @!shift_down, @!clr;
+ /*dimensions for box calculations*/
+if (thickness(q)==default_code) thickness(q)=default_rule_thickness;
+@<Create equal-width boxes |x| and |z| for the numerator and denominator,
+and compute the default amounts |shift_up| and |shift_down| by which they
+are displaced from the baseline@>;
+if (thickness(q)==0) @<Adjust \(s)|shift_up| and |shift_down| for the case
+of no fraction line@>@;
+else@<Adjust \(s)|shift_up| and |shift_down| for the case of a fraction line@>;
+@<Construct a vlist box for the fraction, according to |shift_up| and |shift_down|@>;
+@<Put the \(f)fraction into a box with its delimiters, and make |new_hlist(q)|
+point to it@>;
+}
+
+@ @<Create equal-width boxes |x| and |z| for the numerator and denom...@>=
+x=clean_box(numerator(q), num_style(cur_style));
+z=clean_box(denominator(q), denom_style(cur_style));
+if (width(x) < width(z)) x=rebox(x, width(z));
+else z=rebox(z, width(x));
+if (cur_style < text_style) /*display style*/
+ {@+shift_up=num1(cur_size);shift_down=denom1(cur_size);
+ }
+else{@+shift_down=denom2(cur_size);
+ if (thickness(q)!=0) shift_up=num2(cur_size);
+ else shift_up=num3(cur_size);
+ }
+
+@ The numerator and denominator must be separated by a certain minimum
+clearance, called |clr| in the following program. The difference between
+|clr| and the actual clearance is twice |delta|.
+
+@<Adjust \(s)|shift_up| and |shift_down| for the case of no fraction line@>=
+{@+if (cur_style < text_style) clr=7*default_rule_thickness;
+else clr=3*default_rule_thickness;
+delta=half(clr-((shift_up-depth(x))-(height(z)-shift_down)));
+if (delta > 0)
+ {@+shift_up=shift_up+delta;
+ shift_down=shift_down+delta;
+ }
+}
+
+@ In the case of a fraction line, the minimum clearance depends on the actual
+thickness of the line.
+
+@<Adjust \(s)|shift_up| and |shift_down| for the case of a fraction line@>=
+{@+if (cur_style < text_style) clr=3*thickness(q);
+else clr=thickness(q);
+delta=half(thickness(q));
+delta1=clr-((shift_up-depth(x))-(axis_height(cur_size)+delta));
+delta2=clr-((axis_height(cur_size)-delta)-(height(z)-shift_down));
+if (delta1 > 0) shift_up=shift_up+delta1;
+if (delta2 > 0) shift_down=shift_down+delta2;
+}
+
+@ @<Construct a vlist box for the fraction...@>=
+v=new_null_box();type(v)=vlist_node;
+height(v)=shift_up+height(x);depth(v)=depth(z)+shift_down;
+width(v)=width(x); /*this also equals |width(z)|*/
+if (thickness(q)==0)
+ {@+p=new_kern((shift_up-depth(x))-(height(z)-shift_down));
+ link(p)=z;
+ }
+else{@+y=fraction_rule(thickness(q));@/
+ p=new_kern((axis_height(cur_size)-delta)-@|(height(z)-shift_down));@/
+ link(y)=p;link(p)=z;@/
+ p=new_kern((shift_up-depth(x))-(axis_height(cur_size)+delta));
+ link(p)=y;
+ }
+link(x)=p;list_ptr(v)=x
+
+@ @<Put the \(f)fraction into a box with its delimiters...@>=
+if (cur_style < text_style) delta=delim1(cur_size);
+else delta=delim2(cur_size);
+x=var_delimiter(left_delimiter(q), cur_size, delta);link(x)=v;@/
+z=var_delimiter(right_delimiter(q), cur_size, delta);link(v)=z;@/
+new_hlist(q)=hpack(x, natural)
+
+@ If the nucleus of an |op_noad| is a single character, it is to be
+centered vertically with respect to the axis, after first being enlarged
+(via a character list in the font) if we are in display style. The normal
+convention for placing displayed limits is to put them above and below the
+operator in display style.
+
+The italic correction is removed from the character if there is a subscript
+and the limits are not being displayed. The |make_op|
+routine returns the value that should be used as an offset between
+subscript and superscript.
+
+After |make_op| has acted, |subtype(q)| will be |limits| if and only if
+the limits have been set above and below the operator. In that case,
+|new_hlist(q)| will already contain the desired final box.
+
+@<Declare math...@>=
+static scaled make_op(pointer @!q)
+{@+scaled delta; /*offset between subscript and superscript*/
+pointer @!p, @!v, @!x, @!y, @!z; /*temporary registers for box construction*/
+quarterword @!c;@+four_quarters @!i; /*registers for character examination*/
+scaled @!shift_up, @!shift_down; /*dimensions for box calculation*/
+if ((subtype(q)==normal)&&(cur_style < text_style))
+ subtype(q)=limits;
+if (math_type(nucleus(q))==math_char)
+ {@+fetch(nucleus(q));
+ if ((cur_style < text_style)&&(char_tag(cur_i)==list_tag)) /*make it larger*/
+ {@+c=rem_byte(cur_i);i=char_info(cur_f, c);
+ if (char_exists(i))
+ {@+cur_c=c;cur_i=i;character(nucleus(q))=c;
+ }
+ }
+ delta=char_italic(cur_f, cur_i);x=clean_box(nucleus(q), cur_style);
+ if ((math_type(subscr(q))!=empty)&&(subtype(q)!=limits))
+ width(x)=width(x)-delta; /*remove italic correction*/
+ shift_amount(x)=half(height(x)-depth(x))-axis_height(cur_size);
+ /*center vertically*/
+ math_type(nucleus(q))=sub_box;info(nucleus(q))=x;
+ }
+else delta=0;
+if (subtype(q)==limits)
+ @<Construct a box with limits above and below it, skewed by |delta|@>;
+return delta;
+}
+
+@ The following program builds a vlist box |v| for displayed limits. The
+width of the box is not affected by the fact that the limits may be skewed.
+
+@<Construct a box with limits above and below it...@>=
+{@+x=clean_box(supscr(q), sup_style(cur_style));
+y=clean_box(nucleus(q), cur_style);
+z=clean_box(subscr(q), sub_style(cur_style));
+v=new_null_box();type(v)=vlist_node;width(v)=width(y);
+if (width(x) > width(v)) width(v)=width(x);
+if (width(z) > width(v)) width(v)=width(z);
+x=rebox(x, width(v));y=rebox(y, width(v));z=rebox(z, width(v));@/
+shift_amount(x)=half(delta);shift_amount(z)=-shift_amount(x);
+height(v)=height(y);depth(v)=depth(y);
+@<Attach the limits to |y| and adjust |height(v)|, |depth(v)| to account for
+their presence@>;
+new_hlist(q)=v;
+}
+
+@ We use |shift_up| and |shift_down| in the following program for the
+amount of glue between the displayed operator |y| and its limits |x| and
+|z|. The vlist inside box |v| will consist of |x| followed by |y| followed
+by |z|, with kern nodes for the spaces between and around them.
+
+@<Attach the limits to |y| and adjust |height(v)|, |depth(v)|...@>=
+if (math_type(supscr(q))==empty)
+ {@+free_node(x, box_node_size);list_ptr(v)=y;
+ }
+else{@+shift_up=big_op_spacing3-depth(x);
+ if (shift_up < big_op_spacing1) shift_up=big_op_spacing1;
+ p=new_kern(shift_up);link(p)=y;link(x)=p;@/
+ p=new_kern(big_op_spacing5);link(p)=x;list_ptr(v)=p;
+ height(v)=height(v)+big_op_spacing5+height(x)+depth(x)+shift_up;
+ }
+if (math_type(subscr(q))==empty) free_node(z, box_node_size);
+else{@+shift_down=big_op_spacing4-height(z);
+ if (shift_down < big_op_spacing2) shift_down=big_op_spacing2;
+ p=new_kern(shift_down);link(y)=p;link(p)=z;@/
+ p=new_kern(big_op_spacing5);link(z)=p;
+ depth(v)=depth(v)+big_op_spacing5+height(z)+depth(z)+shift_down;
+ }
+
+@ A ligature found in a math formula does not create a |ligature_node|, because
+there is no question of hyphenation afterwards; the ligature will simply be
+stored in an ordinary |char_node|, after residing in an |ord_noad|.
+
+The |math_type| is converted to |math_text_char| here if we would not want to
+apply an italic correction to the current character unless it belongs
+to a math font (i.e., a font with |space==0|).
+
+No boundary characters enter into these ligatures.
+
+@<Declare math...@>=
+static void make_ord(pointer @!q)
+{@+
+int a; /*address of lig/kern instruction*/
+pointer @!p, @!r; /*temporary registers for list manipulation*/
+restart: @t@>@;@/
+if (math_type(subscr(q))==empty) if (math_type(supscr(q))==empty)
+ if (math_type(nucleus(q))==math_char)
+ {@+p=link(q);
+ if (p!=null) if ((type(p) >= ord_noad)&&(type(p) <= punct_noad))
+ if (math_type(nucleus(p))==math_char)
+ if (fam(nucleus(p))==fam(nucleus(q)))
+ {@+math_type(nucleus(q))=math_text_char;
+ fetch(nucleus(q));
+ if (char_tag(cur_i)==lig_tag)
+ {@+a=lig_kern_start(cur_f, cur_i);
+ cur_c=character(nucleus(p));
+ cur_i=font_info[a].qqqq;
+ if (skip_byte(cur_i) > stop_flag)
+ {@+a=lig_kern_restart(cur_f, cur_i);
+ cur_i=font_info[a].qqqq;
+ }
+ loop@+{@+@<If instruction |cur_i| is a kern with |cur_c|, attach the
+kern after~|q|; or if it is a ligature with |cur_c|, combine noads |q| and~|p|
+appropriately; then |return| if the cursor has moved past a noad, or |goto
+restart|@>;
+ if (skip_byte(cur_i) >= stop_flag) return;
+ a=a+qo(skip_byte(cur_i))+1;
+ cur_i=font_info[a].qqqq;
+ }
+ }
+ }
+ }
+}
+
+@ Note that a ligature between an |ord_noad| and another kind of noad
+is replaced by an |ord_noad|, when the two noads collapse into one.
+But we could make a parenthesis (say) change shape when it follows
+certain letters. Presumably a font designer will define such
+ligatures only when this convention makes sense.
+
+\chardef\@@='174 % vertical line to indicate character retention
+
+@<If instruction |cur_i| is a kern with |cur_c|,...@>=
+if (next_char(cur_i)==cur_c) if (skip_byte(cur_i) <= stop_flag)
+ if (op_byte(cur_i) >= kern_flag)
+ {@+p=new_kern(char_kern(cur_f, cur_i));
+ link(p)=link(q);link(q)=p;return;
+ }
+ else{@+check_interrupt; /*allow a way out of infinite ligature loop*/
+ switch (op_byte(cur_i)) {
+ case qi(1): case qi(5): character(nucleus(q))=rem_byte(cur_i);@+break; /*\.{=:\@@}, \.{=:\@@>}*/
+ case qi(2): case qi(6): character(nucleus(p))=rem_byte(cur_i);@+break; /*\.{\@@=:}, \.{\@@=:>}*/
+ case qi(3): case qi(7): case qi(11): {@+r=new_noad(); /*\.{\@@=:\@@}, \.{\@@=:\@@>}, \.{\@@=:\@@>>}*/
+ character(nucleus(r))=rem_byte(cur_i);
+ fam(nucleus(r))=fam(nucleus(q));@/
+ link(q)=r;link(r)=p;
+ if (op_byte(cur_i) < qi(11)) math_type(nucleus(r))=math_char;
+ else math_type(nucleus(r))=math_text_char; /*prevent combination*/
+ } @+break;
+ default:{@+link(q)=link(p);
+ character(nucleus(q))=rem_byte(cur_i); /*\.{=:}*/
+ mem[subscr(q)]=mem[subscr(p)];mem[supscr(q)]=mem[supscr(p)];@/
+ free_node(p, noad_size);
+ }
+ }
+ if (op_byte(cur_i) > qi(3)) return;
+ math_type(nucleus(q))=math_char;goto restart;
+ }
+
+@ When we get to the following part of the program, we have ``fallen through''
+from cases that did not lead to |check_dimensions| or |done_with_noad| or
+|done_with_node|. Thus, |q|~points to a noad whose nucleus may need to be
+converted to an hlist, and whose subscripts and superscripts need to be
+appended if they are present.
+
+If |nucleus(q)| is not a |math_char|, the variable |delta| is the amount
+by which a superscript should be moved right with respect to a subscript
+when both are present.
+@^subscripts@>
+@^superscripts@>
+
+@<Convert \(n)|nucleus(q)| to an hlist and attach the sub/superscripts@>=
+switch (math_type(nucleus(q))) {
+case math_char: case math_text_char:
+ @<Create a character node |p| for |nucleus(q)|, possibly followed by a kern
+node for the italic correction, and set |delta| to the italic correction if
+a subscript is present@>@;@+break;
+case empty: p=null;@+break;
+case sub_box: p=info(nucleus(q));@+break;
+case sub_mlist: {@+cur_mlist=info(nucleus(q));save_style=cur_style;
+ mlist_penalties=false;mlist_to_hlist(); /*recursive call*/
+@^recursion@>
+ cur_style=save_style;@<Set up the values...@>;
+ p=hpack(link(temp_head), natural);
+ } @+break;
+default:confusion("mlist2");
+@:this can't happen mlist2}{\quad mlist2@>
+} @/
+new_hlist(q)=p;
+if ((math_type(subscr(q))==empty)&&(math_type(supscr(q))==empty))
+ goto check_dimensions;
+make_scripts(q, delta)
+
+@ @<Create a character node |p| for |nucleus(q)|...@>=
+{@+fetch(nucleus(q));
+if (char_exists(cur_i))
+ {@+delta=char_italic(cur_f, cur_i);p=new_character(cur_f, qo(cur_c));
+ if ((math_type(nucleus(q))==math_text_char)&&(space(cur_f)!=0))
+ delta=0; /*no italic correction in mid-word of text font*/
+ if ((math_type(subscr(q))==empty)&&(delta!=0))
+ {@+link(p)=new_kern(delta);delta=0;
+ }
+ }
+else p=null;
+}
+
+@ The purpose of |make_scripts(q, delta)| is to attach the subscript and/or
+superscript of noad |q| to the list that starts at |new_hlist(q)|,
+given that the subscript and superscript aren't both empty. The superscript
+will appear to the right of the subscript by a given distance |delta|.
+
+We set |shift_down| and |shift_up| to the minimum amounts to shift the
+baseline of subscripts and superscripts based on the given nucleus.
+
+@<Declare math...@>=
+static void make_scripts(pointer @!q, scaled @!delta)
+{@+pointer p, @!x, @!y, @!z; /*temporary registers for box construction*/
+scaled @!shift_up, @!shift_down, @!clr; /*dimensions in the calculation*/
+small_number @!t; /*subsidiary size code*/
+p=new_hlist(q);
+if (is_char_node(p))
+ {@+shift_up=0;shift_down=0;
+ }
+else{@+z=hpack(p, natural);
+ if (cur_style < script_style) t=script_size;@+else t=script_script_size;
+ shift_up=height(z)-sup_drop(t);
+ shift_down=depth(z)+sub_drop(t);
+ free_node(z, box_node_size);
+ }
+if (math_type(supscr(q))==empty)
+ @<Construct a subscript box |x| when there is no superscript@>@;
+else{@+@<Construct a superscript box |x|@>;
+ if (math_type(subscr(q))==empty) shift_amount(x)=-shift_up;
+ else@<Construct a sub/superscript combination box |x|, with the superscript
+offset by |delta|@>;
+ }
+if (new_hlist(q)==null) new_hlist(q)=x;
+else{@+p=new_hlist(q);
+ while (link(p)!=null) p=link(p);
+ link(p)=x;
+ }
+}
+
+@ When there is a subscript without a superscript, the top of the subscript
+should not exceed the baseline plus four-fifths of the x-height.
+
+@<Construct a subscript box |x| when there is no superscript@>=
+{@+x=clean_box(subscr(q), sub_style(cur_style));
+width(x)=width(x)+script_space;
+if (shift_down < sub1(cur_size)) shift_down=sub1(cur_size);
+clr=height(x)-(abs(math_x_height(cur_size)*4)/5);
+if (shift_down < clr) shift_down=clr;
+shift_amount(x)=shift_down;
+}
+
+@ The bottom of a superscript should never descend below the baseline plus
+one-fourth of the x-height.
+
+@<Construct a superscript box |x|@>=
+{@+x=clean_box(supscr(q), sup_style(cur_style));
+width(x)=width(x)+script_space;
+if (odd(cur_style)) clr=sup3(cur_size);
+else if (cur_style < text_style) clr=sup1(cur_size);
+else clr=sup2(cur_size);
+if (shift_up < clr) shift_up=clr;
+clr=depth(x)+(abs(math_x_height(cur_size))/4);
+if (shift_up < clr) shift_up=clr;
+}
+
+@ When both subscript and superscript are present, the subscript must be
+separated from the superscript by at least four times |default_rule_thickness|.
+If this condition would be violated, the subscript moves down, after which
+both subscript and superscript move up so that the bottom of the superscript
+is at least as high as the baseline plus four-fifths of the x-height.
+
+@<Construct a sub/superscript combination box |x|...@>=
+{@+y=clean_box(subscr(q), sub_style(cur_style));
+width(y)=width(y)+script_space;
+if (shift_down < sub2(cur_size)) shift_down=sub2(cur_size);
+clr=4*default_rule_thickness-
+ ((shift_up-depth(x))-(height(y)-shift_down));
+if (clr > 0)
+ {@+shift_down=shift_down+clr;
+ clr=(abs(math_x_height(cur_size)*4)/5)-(shift_up-depth(x));
+ if (clr > 0)
+ {@+shift_up=shift_up+clr;
+ shift_down=shift_down-clr;
+ }
+ }
+shift_amount(x)=delta; /*superscript is |delta| to the right of the subscript*/
+p=new_kern((shift_up-depth(x))-(height(y)-shift_down));link(x)=p;link(p)=y;
+x=vpack(x, natural);shift_amount(x)=shift_down;
+}
+
+@ We have now tied up all the loose ends of the first pass of |mlist_to_hlist|.
+The second pass simply goes through and hooks everything together with the
+proper glue and penalties. It also handles the |left_noad| and |right_noad| that
+might be present, since |max_h| and |max_d| are now known. Variable |p| points
+to a node at the current end of the final hlist.
+
+@<Make a second pass over the mlist,...@>=
+p=temp_head;link(p)=null;q=mlist;r_type=0;cur_style=style;
+@<Set up the values of |cur_size| and |cur_mu|, based on |cur_style|@>;
+while (q!=null)
+ {@+@<If node |q| is a style node, change the style and |goto delete_q|;
+otherwise if it is not a noad, put it into the hlist, advance |q|, and |goto
+done|; otherwise set |s| to the size of noad |q|, set |t| to the associated
+type (|ord_noad.. inner_noad|), and set |pen| to the associated penalty@>;
+ @<Append inter-element spacing based on |r_type| and |t|@>;
+ @<Append any |new_hlist| entries for |q|, and any appropriate penalties@>;
+ if (type(q)==right_noad) t=open_noad;
+ r_type=t;
+ delete_q: r=q;q=link(q);free_node(r, s);
+ done: ;}
+
+@ Just before doing the big |case| switch in the second pass, the program
+sets up default values so that most of the branches are short.
+
+@<If node |q| is a style node, change the style...@>=
+t=ord_noad;s=noad_size;pen=inf_penalty;
+switch (type(q)) {
+case op_noad: case open_noad: case close_noad:
+ case punct_noad: case inner_noad: t=type(q);@+break;
+case bin_noad: {@+t=bin_noad;pen=bin_op_penalty;
+ } @+break;
+case rel_noad: {@+t=rel_noad;pen=rel_penalty;
+ } @+break;
+case ord_noad: case vcenter_noad: case over_noad:
+ case under_noad: do_nothing;@+break;
+case radical_noad: s=radical_noad_size;@+break;
+case accent_noad: s=accent_noad_size;@+break;
+case fraction_noad: s=fraction_noad_size;@+break;
+case left_noad: case right_noad: t=make_left_right(q, style, max_d, max_h);@+break;
+case style_node: @<Change the current style and |goto delete_q|@>@;
+case whatsit_node: case penalty_node:
+ case rule_node: case disc_node: case adjust_node:
+ case ins_node: case mark_node:
+ case glue_node: case kern_node: @t@>@;@/
+ {@+link(p)=q;p=q;q=link(q);link(p)=null;goto done;
+ }
+default:confusion("mlist3");
+@:this can't happen mlist3}{\quad mlist3@>
+}
+
+@ The |make_left_right| function constructs a left or right delimiter of
+the required size and returns the value |open_noad| or |close_noad|. The
+|right_noad| and |left_noad| will both be based on the original |style|,
+so they will have consistent sizes.
+
+We use the fact that |right_noad-left_noad==close_noad-open_noad|.
+
+@<Declare math...@>=
+static small_number make_left_right(pointer @!q, small_number @!style,
+ scaled @!max_d, scaled @!max_h)
+{@+scaled delta, @!delta1, @!delta2; /*dimensions used in the calculation*/
+cur_style=style;@<Set up the values...@>;
+delta2=max_d+axis_height(cur_size);
+delta1=max_h+max_d-delta2;
+if (delta2 > delta1) delta1=delta2; /*|delta1| is max distance from axis*/
+delta=(delta1/500)*delimiter_factor;
+delta2=delta1+delta1-delimiter_shortfall;
+if (delta < delta2) delta=delta2;
+new_hlist(q)=var_delimiter(delimiter(q), cur_size, delta);
+return type(q)-(left_noad-open_noad); /*|open_noad| or |close_noad|*/
+}
+
+@ @<Change the current style and |goto delete_q|@>=
+{@+cur_style=subtype(q);s=style_node_size;
+@<Set up the values of |cur_size| and |cur_mu|, based on |cur_style|@>;
+goto delete_q;
+}
+
+@ The inter-element spacing in math formulas depends on an $8\times8$ table that
+\TeX\ preloads as a 64-digit string. The elements of this string have the
+following significance:
+$$\vbox{\halign{#\hfil\cr
+\.0 means no space;\cr
+\.1 means a conditional thin space (\.{\\nonscript\\mskip\\thinmuskip});\cr
+\.2 means a thin space (\.{\\mskip\\thinmuskip});\cr
+\.3 means a conditional medium space
+ (\.{\\nonscript\\mskip\\medmuskip});\cr
+\.4 means a conditional thick space
+ (\.{\\nonscript\\mskip\\thickmuskip});\cr
+\.* means an impossible case.\cr}}$$
+This is all pretty cryptic, but {\sl The \TeX book\/} explains what is
+supposed to happen, and the string makes it happen.
+@:TeXbook}{\sl The \TeX book@>
+
+A global variable |magic_offset| is computed so that if |a| and |b| are
+in the range |ord_noad dotdot inner_noad|, then |str_pool[a*8+b+magic_offset]|
+is the digit for spacing between noad types |a| and |b|.
+
+If \PASCAL\ had provided a good way to preload constant arrays, this part of
+the program would not have been so strange.
+@:PASCAL}{\PASCAL@>
+
+@d math_spacing @;@/
+@t\hskip-35pt@>
+"0234000122*4000133**3**344*0400400*000000234000111*1111112341011"
+@t$ \hskip-35pt$@>
+
+@ @<Glob...@>=
+static const int @!magic_offset=-9*ord_noad; /*used to find inter-element spacing*/
+
+@ @<Append inter-element spacing based on |r_type| and |t|@>=
+if (r_type > 0) /*not the first noad*/
+ {@+switch (so(math_spacing[r_type*8+t+magic_offset])) {
+ case '0': x=0;@+break;
+ case '1': if (cur_style < script_style) x=thin_mu_skip_code;@+else x=0;@+break;
+ case '2': x=thin_mu_skip_code;@+break;
+ case '3': if (cur_style < script_style) x=med_mu_skip_code;@+else x=0;@+break;
+ case '4': if (cur_style < script_style) x=thick_mu_skip_code;@+else x=0;@+break;
+ default:confusion("mlist4");
+@:this can't happen mlist4}{\quad mlist4@>
+ }
+ if (x!=0)
+ {@+y=math_glue(glue_par(x), cur_mu);
+ z=new_glue(y);glue_ref_count(y)=null;link(p)=z;p=z;@/
+ subtype(z)=x+1; /*store a symbolic subtype*/
+ }
+ }
+
+@ We insert a penalty node after the hlist entries of noad |q| if |pen|
+is not an ``infinite'' penalty, and if the node immediately following |q|
+is not a penalty node or a |rel_noad| or absent entirely.
+
+@<Append any |new_hlist| entries for |q|, and any appropriate penalties@>=
+if (new_hlist(q)!=null)
+ {@+link(p)=new_hlist(q);
+ @/do@+{p=link(p);
+ }@+ while (!(link(p)==null));
+ }
+if (penalties) if (link(q)!=null) if (pen < inf_penalty)
+ {@+r_type=type(link(q));
+ if (r_type!=penalty_node) if (r_type!=rel_noad)
+ {@+z=new_penalty(pen);link(p)=z;p=z;
+ }
+ }
+
+@* Alignment.
+It's sort of a miracle whenever \.{\\halign} and \.{\\valign} work, because
+they cut across so many of the control structures of \TeX.
+
+Therefore the
+present page is probably not the best place for a beginner to start reading
+this program; it is better to master everything else first.
+
+Let us focus our thoughts on an example of what the input might be, in order
+to get some idea about how the alignment miracle happens. The example doesn't
+do anything useful, but it is sufficiently general to indicate all of the
+special cases that must be dealt with; please do not be disturbed by its
+apparent complexity and meaninglessness.
+$$\vbox{\halign{\.{#}\hfil\cr
+{}\\tabskip 2pt plus 3pt\cr
+{}\\halign to 300pt\{u1\#v1\&\cr
+\hskip 50pt\\tabskip 1pt plus 1fil u2\#v2\&\cr
+\hskip 50pt u3\#v3\\cr\cr
+\hskip 25pt a1\&\\omit a2\&\\vrule\\cr\cr
+\hskip 25pt \\noalign\{\\vskip 3pt\}\cr
+\hskip 25pt b1\\span b2\\cr\cr
+\hskip 25pt \\omit\&c2\\span\\omit\\cr\}\cr}}$$
+Here's what happens:
+
+\yskip
+(0) When `\.{\\halign to 300pt\{}' is scanned, the |scan_spec| routine
+places the 300pt dimension onto the |save_stack|, and an |align_group|
+code is placed above it. This will make it possible to complete the alignment
+when the matching `\.\}' is found.
+
+(1) The preamble is scanned next. Macros in the preamble are not expanded,
+@^preamble@>
+except as part of a tabskip specification. For example, if \.{u2} had been
+a macro in the preamble above, it would have been expanded, since \TeX\
+must look for `\.{minus...}' as part of the tabskip glue. A ``preamble list''
+is constructed based on the user's preamble; in our case it contains the
+following seven items:
+$$\vbox{\halign{\.{#}\hfil\qquad&(#)\hfil\cr
+{}\\glue 2pt plus 3pt&the tabskip preceding column 1\cr
+{}\\alignrecord, width $-\infty$&preamble info for column 1\cr
+{}\\glue 2pt plus 3pt&the tabskip between columns 1 and 2\cr
+{}\\alignrecord, width $-\infty$&preamble info for column 2\cr
+{}\\glue 1pt plus 1fil&the tabskip between columns 2 and 3\cr
+{}\\alignrecord, width $-\infty$&preamble info for column 3\cr
+{}\\glue 1pt plus 1fil&the tabskip following column 3\cr}}$$
+These ``alignrecord'' entries have the same size as an |unset_node|,
+since they will later be converted into such nodes. However, at the
+moment they have no |type| or |subtype| fields; they have |info| fields
+instead, and these |info| fields are initially set to the value |end_span|,
+for reasons explained below. Furthermore, the alignrecord nodes have no
+|height| or |depth| fields; these are renamed |u_part| and |v_part|,
+and they point to token lists for the templates of the alignment.
+For example, the |u_part| field in the first alignrecord points to the
+token list `\.{u1}', i.e., the template preceding the `\.\#' for column~1.
+
+(2) \TeX\ now looks at what follows the \.{\\cr} that ended the preamble.
+It is not `\.{\\noalign}' or `\.{\\omit}', so this input is put back to
+be read again, and the template `\.{u1}' is fed to the scanner. Just
+before reading `\.{u1}', \TeX\ goes into restricted horizontal mode.
+Just after reading `\.{u1}', \TeX\ will see `\.{a1}', and then (when the
+{\.\&} is sensed) \TeX\ will see `\.{v1}'. Then \TeX\ scans an |endv|
+token, indicating the end of a column. At this point an |unset_node| is
+created, containing the contents of the current hlist (i.e., `\.{u1a1v1}').
+The natural width of this unset node replaces the |width| field of the
+alignrecord for column~1; in general, the alignrecords will record the
+maximum natural width that has occurred so far in a given column.
+
+(3) Since `\.{\\omit}' follows the `\.\&', the templates for column~2
+are now bypassed. Again \TeX\ goes into restricted horizontal mode and
+makes an |unset_node| from the resulting hlist; but this time the
+hlist contains simply `\.{a2}'. The natural width of the new unset box
+is remembered in the |width| field of the alignrecord for column~2.
+
+(4) A third |unset_node| is created for column 3, using essentially the
+mechanism that worked for column~1; this unset box contains `\.{u3\\vrule
+v3}'. The vertical rule in this case has running dimensions that will later
+extend to the height and depth of the whole first row, since each |unset_node|
+in a row will eventually inherit the height and depth of its enclosing box.
+
+(5) The first row has now ended; it is made into a single unset box
+comprising the following seven items:
+$$\vbox{\halign{\hbox to 325pt{\qquad\.{#}\hfil}\cr
+{}\\glue 2pt plus 3pt\cr
+{}\\unsetbox for 1 column: u1a1v1\cr
+{}\\glue 2pt plus 3pt\cr
+{}\\unsetbox for 1 column: a2\cr
+{}\\glue 1pt plus 1fil\cr
+{}\\unsetbox for 1 column: u3\\vrule v3\cr
+{}\\glue 1pt plus 1fil\cr}}$$
+The width of this unset row is unimportant, but it has the correct height
+and depth, so the correct baselineskip glue will be computed as the row
+is inserted into a vertical list.
+
+(6) Since `\.{\\noalign}' follows the current \.{\\cr}, \TeX\ appends
+additional material (in this case \.{\\vskip 3pt}) to the vertical list.
+While processing this material, \TeX\ will be in internal vertical
+mode, and |no_align_group| will be on |save_stack|.
+
+(7) The next row produces an unset box that looks like this:
+$$\vbox{\halign{\hbox to 325pt{\qquad\.{#}\hfil}\cr
+{}\\glue 2pt plus 3pt\cr
+{}\\unsetbox for 2 columns: u1b1v1u2b2v2\cr
+{}\\glue 1pt plus 1fil\cr
+{}\\unsetbox for 1 column: {\rm(empty)}\cr
+{}\\glue 1pt plus 1fil\cr}}$$
+The natural width of the unset box that spans columns 1~and~2 is stored
+in a ``span node,'' which we will explain later; the |info| field of the
+alignrecord for column~1 now points to the new span node, and the |info|
+of the span node points to |end_span|.
+
+(8) The final row produces the unset box
+$$\vbox{\halign{\hbox to 325pt{\qquad\.{#}\hfil}\cr
+{}\\glue 2pt plus 3pt\cr
+{}\\unsetbox for 1 column: {\rm(empty)}\cr
+{}\\glue 2pt plus 3pt\cr
+{}\\unsetbox for 2 columns: u2c2v2\cr
+{}\\glue 1pt plus 1fil\cr}}$$
+A new span node is attached to the alignrecord for column 2.
+
+(9) The last step is to compute the true column widths and to change all the
+unset boxes to hboxes, appending the whole works to the vertical list that
+encloses the \.{\\halign}. The rules for deciding on the final widths of
+each unset column box will be explained below.
+
+\yskip\noindent
+Note that as \.{\\halign} is being processed, we fearlessly give up control
+to the rest of \TeX. At critical junctures, an alignment routine is
+called upon to step in and do some little action, but most of the time
+these routines just lurk in the background. It's something like
+post-hypnotic suggestion.
+
+@ We have mentioned that alignrecords contain no |height| or |depth| fields.
+Their |glue_sign| and |glue_order| are pre-empted as well, since it
+is necessary to store information about what to do when a template ends.
+This information is called the |extra_info| field.
+
+@d u_part(A) mem[A+height_offset].i /*pointer to \<u_j> token list*/
+@d v_part(A) mem[A+depth_offset].i /*pointer to \<v_j> token list*/
+@d extra_info(A) info(A+list_offset) /*info to remember during template*/
+
+@ Alignments can occur within alignments, so a small stack is used to access
+the alignrecord information. At each level we have a |preamble| pointer,
+indicating the beginning of the preamble list; a |cur_align| pointer,
+indicating the current position in the preamble list; a |cur_span| pointer,
+indicating the value of |cur_align| at the beginning of a sequence of
+spanned columns; a |cur_loop| pointer, indicating the tabskip glue before
+an alignrecord that should be copied next if the current list is extended;
+and the |align_state| variable, which indicates the nesting of braces so
+that \.{\\cr} and \.{\\span} and tab marks are properly intercepted.
+There also are pointers |cur_head| and |cur_tail| to the head and tail
+of a list of adjustments being moved out from horizontal mode to
+vertical~mode.
+
+The current values of these seven quantities appear in global variables;
+when they have to be pushed down, they are stored in 5-word nodes, and
+|align_ptr| points to the topmost such node.
+
+@d preamble link(align_head) /*the current preamble list*/
+@d align_stack_node_size 5 /*number of |mem| words to save alignment states*/
+
+@<Glob...@>=
+static pointer @!cur_align; /*current position in preamble list*/
+static pointer @!cur_span; /*start of currently spanned columns in preamble list*/
+static pointer @!cur_loop; /*place to copy when extending a periodic preamble*/
+static pointer @!align_ptr; /*most recently pushed-down alignment stack node*/
+static pointer @!cur_head, @!cur_tail; /*adjustment list pointers*/
+
+@ The |align_state| and |preamble| variables are initialized elsewhere.
+
+@<Set init...@>=
+align_ptr=null;cur_align=null;cur_span=null;cur_loop=null;
+cur_head=null;cur_tail=null;
+
+@ Alignment stack maintenance is handled by a pair of trivial routines
+called |push_alignment| and |pop_alignment|.
+
+@p static void push_alignment(void)
+{@+pointer p; /*the new alignment stack node*/
+p=get_node(align_stack_node_size);
+link(p)=align_ptr;info(p)=cur_align;
+llink(p)=preamble;rlink(p)=cur_span;
+mem[p+2].i=cur_loop;mem[p+3].i=align_state;
+info(p+4)=cur_head;link(p+4)=cur_tail;
+align_ptr=p;
+cur_head=get_avail();
+}
+@#
+static void pop_alignment(void)
+{@+pointer p; /*the top alignment stack node*/
+free_avail(cur_head);
+p=align_ptr;
+cur_tail=link(p+4);cur_head=info(p+4);
+align_state=mem[p+3].i;cur_loop=mem[p+2].i;
+cur_span=rlink(p);preamble=llink(p);
+cur_align=info(p);align_ptr=link(p);
+free_node(p, align_stack_node_size);
+}
+
+@ \TeX\ has eight procedures that govern alignments: |init_align| and
+|fin_align| are used at the very beginning and the very end; |init_row| and
+|fin_row| are used at the beginning and end of individual rows; |init_span|
+is used at the beginning of a sequence of spanned columns (possibly involving
+only one column); |init_col| and |fin_col| are used at the beginning and
+end of individual columns; and |align_peek| is used after \.{\\cr} to see
+whether the next item is \.{\\noalign}.
+
+We shall consider these routines in the order they are first used during
+the course of a complete \.{\\halign}, namely |init_align|, |align_peek|,
+|init_row|, |init_span|, |init_col|, |fin_col|, |fin_row|, |fin_align|.
+
+@ When \.{\\halign} or \.{\\valign} has been scanned in an appropriate
+mode, \TeX\ calls |init_align|, whose task is to get everything off to a
+good start. This mostly involves scanning the preamble and putting its
+information into the preamble list.
+@^preamble@>
+
+@p @t\4@>@<Declare the procedure called |get_preamble_token|@>@t@>@/
+static void align_peek(void);@/
+static void normal_paragraph(void);@/
+static void init_align(void)
+{@+
+pointer save_cs_ptr; /*|warning_index| value for error messages*/
+pointer @!p; /*for short-term temporary use*/
+save_cs_ptr=cur_cs; /*\.{\\halign} or \.{\\valign}, usually*/
+push_alignment();align_state=-1000000; /*enter a new alignment level*/
+@<Check for improper alignment in displayed math@>;
+push_nest(); /*enter a new semantic level*/
+@<Change current mode to |-vmode| for \.{\\halign}, |-hmode| for \.{\\valign}@>;
+scan_spec(align_group, false);@/
+@<Scan the preamble and record it in the |preamble| list@>;
+new_save_level(align_group);
+if (every_cr!=null) begin_token_list(every_cr, every_cr_text);
+align_peek(); /*look for \.{\\noalign} or \.{\\omit}*/
+}
+
+@ In vertical modes, |prev_depth| already has the correct value. But
+if we are in |mmode| (displayed formula mode), we reach out to the
+enclosing vertical mode for the |prev_depth| value that produces the
+correct baseline calculations.
+
+@<Change current mode...@>=
+if (mode==mmode)
+ {@+mode=-vmode;prev_depth=nest[nest_ptr-2].aux_field.sc;
+ }
+else if (mode > 0) negate(mode)
+
+@ When \.{\\halign} is used as a displayed formula, there should be
+no other pieces of mlists present.
+
+@<Check for improper alignment in displayed math@>=
+if ((mode==mmode)&&((tail!=head)||(incompleat_noad!=null)))
+ {@+print_err("Improper ");print_esc("halign");print(" inside $$'s");
+@.Improper \\halign...@>
+ help3("Displays can use special alignments (like \\eqalignno)",@/
+ "only if nothing but the alignment itself is between $$'s.",@/
+ "So I've deleted the formulas that preceded this alignment.");
+ error();flush_math();
+ }
+
+@ @<Scan the preamble and record it in the |preamble| list@>=
+preamble=null;cur_align=align_head;cur_loop=null;scanner_status=aligning;
+warning_index=save_cs_ptr;align_state=-1000000;
+ /*at this point, |cur_cmd==left_brace|*/
+loop@+{@+@<Append the current tabskip glue to the preamble list@>;
+ if (cur_cmd==car_ret) goto done; /*\.{\\cr} ends the preamble*/
+ @<Scan preamble text until |cur_cmd| is |tab_mark| or |car_ret|, looking
+for changes in the tabskip glue; append an alignrecord to the preamble list@>;
+ }
+done: scanner_status=normal
+
+@ @<Append the current tabskip glue to the preamble list@>=
+link(cur_align)=new_param_glue(tab_skip_code);
+cur_align=link(cur_align)
+
+@ @<Scan preamble text until |cur_cmd| is |tab_mark| or |car_ret|...@>=
+@<Scan the template \<u_j>, putting the resulting token list in |hold_head|@>;
+link(cur_align)=new_null_box();cur_align=link(cur_align); /*a new alignrecord*/
+info(cur_align)=end_span;width(cur_align)=null_flag;
+u_part(cur_align)=link(hold_head);
+@<Scan the template \<v_j>, putting the resulting token list in |hold_head|@>;
+v_part(cur_align)=link(hold_head)
+
+@ We enter `\.{\\span}' into |eqtb| with |tab_mark| as its command code,
+and with |span_code| as the command modifier. This makes \TeX\ interpret it
+essentially the same as an alignment delimiter like `\.\&', yet it is
+recognizably different when we need to distinguish it from a normal delimiter.
+It also turns out to be useful to give a special |cr_code| to `\.{\\cr}',
+and an even larger |cr_cr_code| to `\.{\\crcr}'.
+
+The end of a template is represented by two ``frozen'' control sequences
+called \.{\\endtemplate}. The first has the command code |end_template|, which
+is | > outer_call|, so it will not easily disappear in the presence of errors.
+The |get_x_token| routine converts the first into the second, which has |endv|
+as its command code.
+
+@d span_code 256 /*distinct from any character*/
+@d cr_code 257 /*distinct from |span_code| and from any character*/
+@d cr_cr_code (cr_code+1) /*this distinguishes \.{\\crcr} from \.{\\cr}*/
+@d end_template_token cs_token_flag+frozen_end_template
+
+@<Put each of \TeX's primitives into the hash table@>=
+primitive("span", tab_mark, span_code);@/
+@!@:span\_}{\.{\\span} primitive@>
+primitive("cr", car_ret, cr_code);
+@!@:cr\_}{\.{\\cr} primitive@>
+text(frozen_cr)=text(cur_val);eqtb[frozen_cr]=eqtb[cur_val];@/
+primitive("crcr", car_ret, cr_cr_code);
+@!@:cr\_cr\_}{\.{\\crcr} primitive@>
+text(frozen_end_template)=text(frozen_endv)=s_no("endtemplate");
+@.endtemplate@>
+eq_type(frozen_endv)=endv;equiv(frozen_endv)=null_list;
+eq_level(frozen_endv)=level_one;@/
+eqtb[frozen_end_template]=eqtb[frozen_endv];
+eq_type(frozen_end_template)=end_template;
+
+@ @<Cases of |print_cmd_chr|...@>=
+case tab_mark: if (chr_code==span_code) print_esc("span");
+ else chr_cmd("alignment tab character ")@;@+break;
+case car_ret: if (chr_code==cr_code) print_esc("cr");
+ else print_esc("crcr");@+break;
+
+@ The preamble is copied directly, except that \.{\\tabskip} causes a change
+to the tabskip glue, thereby possibly expanding macros that immediately
+follow it. An appearance of \.{\\span} also causes such an expansion.
+
+Note that if the preamble contains `\.{\\global\\tabskip}', the `\.{\\global}'
+token survives in the preamble and the `\.{\\tabskip}' defines new
+tabskip glue (locally).
+
+@<Declare the procedure called |get_preamble_token|@>=
+static void get_preamble_token(void)
+{@+
+restart: get_token();
+while ((cur_chr==span_code)&&(cur_cmd==tab_mark))
+ {@+get_token(); /*this token will be expanded once*/
+ if (cur_cmd > max_command)
+ {@+expand();get_token();
+ }
+ }
+if (cur_cmd==endv)
+ fatal_error("(interwoven alignment preambles are not allowed)");
+@.interwoven alignment preambles...@>
+if ((cur_cmd==assign_glue)&&(cur_chr==glue_base+tab_skip_code))
+ {@+scan_optional_equals();scan_glue(glue_val);
+ if (global_defs > 0) geq_define(glue_base+tab_skip_code, glue_ref, cur_val);
+ else eq_define(glue_base+tab_skip_code, glue_ref, cur_val);
+ goto restart;
+ }
+}
+
+@ Spaces are eliminated from the beginning of a template.
+
+@<Scan the template \<u_j>...@>=
+p=hold_head;link(p)=null;
+loop@+{@+get_preamble_token();
+ if (cur_cmd==mac_param) goto done1;
+ if ((cur_cmd <= car_ret)&&(cur_cmd >= tab_mark)&&(align_state==-1000000))
+ if ((p==hold_head)&&(cur_loop==null)&&(cur_cmd==tab_mark)
+ ) cur_loop=cur_align;
+ else{@+print_err("Missing # inserted in alignment preamble");
+@.Missing \# inserted...@>
+ help3("There should be exactly one # between &'s, when an",@/
+ "\\halign or \\valign is being set up. In this case you had",@/
+ "none, so I've put one in; maybe that will work.");
+ back_error();goto done1;
+ }
+ else if ((cur_cmd!=spacer)||(p!=hold_head))
+ {@+link(p)=get_avail();p=link(p);info(p)=cur_tok;
+ }
+ }
+done1:
+
+@ @<Scan the template \<v_j>...@>=
+p=hold_head;link(p)=null;
+loop@+{@+resume: get_preamble_token();
+ if ((cur_cmd <= car_ret)&&(cur_cmd >= tab_mark)&&(align_state==-1000000))
+ goto done2;
+ if (cur_cmd==mac_param)
+ {@+print_err("Only one # is allowed per tab");
+@.Only one \# is allowed...@>
+ help3("There should be exactly one # between &'s, when an",@/
+ "\\halign or \\valign is being set up. In this case you had",@/
+ "more than one, so I'm ignoring all but the first.");
+ error();goto resume;
+ }
+ link(p)=get_avail();p=link(p);info(p)=cur_tok;
+ }
+done2: link(p)=get_avail();p=link(p);
+info(p)=end_template_token /*put \.{\\endtemplate} at the end*/
+
+@ The tricky part about alignments is getting the templates into the
+scanner at the right time, and recovering control when a row or column
+is finished.
+
+We usually begin a row after each \.{\\cr} has been sensed, unless that
+\.{\\cr} is followed by \.{\\noalign} or by the right brace that terminates
+the alignment. The |align_peek| routine is used to look ahead and do
+the right thing; it either gets a new row started, or gets a \.{\\noalign}
+started, or finishes off the alignment.
+
+@<Declare the procedure called |align_peek|@>=
+static void align_peek(void)
+{@+
+restart: align_state=1000000;
+@/do@+{get_x_or_protected();
+}@+ while (!(cur_cmd!=spacer));
+if (cur_cmd==no_align)
+ {@+scan_left_brace();new_save_level(no_align_group);
+ if (mode==-vmode) normal_paragraph();
+ }
+else if (cur_cmd==right_brace) fin_align();
+else if ((cur_cmd==car_ret)&&(cur_chr==cr_cr_code))
+ goto restart; /*ignore \.{\\crcr}*/
+else{@+init_row(); /*start a new row*/
+ init_col(); /*start a new column and replace what we peeked at*/
+ }
+}
+
+@ To start a row (i.e., a `row' that rhymes with `dough' but not with `bough'),
+we enter a new semantic level, copy the first tabskip glue, and change
+from internal vertical mode to restricted horizontal mode or vice versa.
+The |space_factor| and |prev_depth| are not used on this semantic level,
+but we clear them to zero just to be tidy.
+
+@p @t\4@>@<Declare the procedure called |init_span|@>@t@>@/
+static void init_row(void)
+{@+push_nest();mode=(-hmode-vmode)-mode;
+if (mode==-hmode) space_factor=0;@+else prev_depth=0;
+tail_append(new_glue(glue_ptr(preamble)));
+subtype(tail)=tab_skip_code+1;@/
+cur_align=link(preamble);cur_tail=cur_head;init_span(cur_align);
+}
+
+@ The parameter to |init_span| is a pointer to the alignrecord where the
+next column or group of columns will begin. A new semantic level is
+entered, so that the columns will generate a list for subsequent packaging.
+
+@<Declare the procedure called |init_span|@>=
+static void init_span(pointer @!p)
+{@+push_nest();
+if (mode==-hmode) space_factor=1000;
+else{@+prev_depth=ignore_depth;normal_paragraph();
+ }
+cur_span=p;
+}
+
+@ When a column begins, we assume that |cur_cmd| is either |omit| or else
+the current token should be put back into the input until the \<u_j>
+template has been scanned. (Note that |cur_cmd| might be |tab_mark| or
+|car_ret|.) We also assume that |align_state| is approximately 1000000 at
+this time. We remain in the same mode, and start the template if it is
+called for.
+
+@p static void init_col(void)
+{@+extra_info(cur_align)=cur_cmd;
+if (cur_cmd==omit) align_state=0;
+else{@+back_input();begin_token_list(u_part(cur_align), u_template);
+ } /*now |align_state==1000000|*/
+}
+
+@ The scanner sets |align_state| to zero when the \<u_j> template ends. When
+a subsequent \.{\\cr} or \.{\\span} or tab mark occurs with |align_state==0|,
+the scanner activates the following code, which fires up the \<v_j> template.
+We need to remember the |cur_chr|, which is either |cr_cr_code|, |cr_code|,
+|span_code|, or a character code, depending on how the column text has ended.
+
+This part of the program had better not be activated when the preamble
+to another alignment is being scanned, or when no alignment preamble is active.
+
+@<Insert the \(v)\<v_j>...@>=
+{@+if ((scanner_status==aligning)||(cur_align==null))
+ fatal_error("(interwoven alignment preambles are not allowed)");
+@.interwoven alignment preambles...@>
+cur_cmd=extra_info(cur_align);extra_info(cur_align)=cur_chr;
+if (cur_cmd==omit) begin_token_list(omit_template, v_template);
+else begin_token_list(v_part(cur_align), v_template);
+align_state=1000000;goto restart;
+}
+
+@ The token list |omit_template| just referred to is a constant token
+list that contains the special control sequence \.{\\endtemplate} only.
+
+@<Initialize the special...@>=
+info(omit_template)=end_template_token; /*|link(omit_template)==null|*/
+
+@ When the |endv| command at the end of a \<v_j> template comes through the
+scanner, things really start to happen; and it is the |fin_col| routine
+that makes them happen. This routine returns |true| if a row as well as a
+column has been finished.
+
+@p static bool fin_col(void)
+{@+
+pointer p; /*the alignrecord after the current one*/
+pointer @!q, @!r; /*temporary pointers for list manipulation*/
+pointer @!s; /*a new span node*/
+pointer @!u; /*a new unset box*/
+scaled @!w; /*natural width*/
+glue_ord @!o; /*order of infinity*/
+halfword @!n; /*span counter*/
+if (cur_align==null) confusion("endv");
+q=link(cur_align);@+if (q==null) confusion("endv");
+@:this can't happen endv}{\quad endv@>
+if (align_state < 500000)
+ fatal_error("(interwoven alignment preambles are not allowed)");
+@.interwoven alignment preambles...@>
+p=link(q);
+@<If the preamble list has been traversed, check that the row has ended@>;
+if (extra_info(cur_align)!=span_code)
+ {@+unsave();new_save_level(align_group);@/
+ @<Package an unset box for the current column and record its width@>;
+ @<Copy the tabskip glue between columns@>;
+ if (extra_info(cur_align) >= cr_code)
+ {@+return true;
+ }
+ init_span(p);
+ }
+align_state=1000000;
+@/do@+{get_x_or_protected();
+}@+ while (!(cur_cmd!=spacer));
+cur_align=p;
+init_col();return false;
+}
+
+@ @<If the preamble list has been traversed, check that the row has ended@>=
+if ((p==null)&&(extra_info(cur_align) < cr_code))
+ if (cur_loop!=null) @<Lengthen the preamble periodically@>@;
+ else{@+print_err("Extra alignment tab has been changed to ");
+@.Extra alignment tab...@>
+ print_esc("cr");
+ help3("You have given more \\span or & marks than there were",@/
+ "in the preamble to the \\halign or \\valign now in progress.",@/
+ "So I'll assume that you meant to type \\cr instead.");
+ extra_info(cur_align)=cr_code;error();
+ }
+
+@ @<Lengthen the preamble...@>=
+{@+link(q)=new_null_box();p=link(q); /*a new alignrecord*/
+info(p)=end_span;width(p)=null_flag;cur_loop=link(cur_loop);
+@<Copy the templates from node |cur_loop| into node |p|@>;
+cur_loop=link(cur_loop);
+link(p)=new_glue(glue_ptr(cur_loop));
+subtype(link(p))=tab_skip_code+1;
+}
+
+@ @<Copy the templates from node |cur_loop| into node |p|@>=
+q=hold_head;r=u_part(cur_loop);
+while (r!=null)
+ {@+link(q)=get_avail();q=link(q);info(q)=info(r);r=link(r);
+ }
+link(q)=null;u_part(p)=link(hold_head);
+q=hold_head;r=v_part(cur_loop);
+while (r!=null)
+ {@+link(q)=get_avail();q=link(q);info(q)=info(r);r=link(r);
+ }
+link(q)=null;v_part(p)=link(hold_head)
+
+@ @<Copy the tabskip glue...@>=
+tail_append(new_glue(glue_ptr(link(cur_align))));
+subtype(tail)=tab_skip_code+1
+
+@ @<Package an unset...@>=
+{@+if (mode==-hmode)
+ {@+adjust_tail=cur_tail;u=hpack(link(head), natural);w=width(u);
+ cur_tail=adjust_tail;adjust_tail=null;
+ }
+else{@+u=vpackage(link(head), natural, 0);w=height(u);
+ }
+n=min_quarterword; /*this represents a span count of 1*/
+if (cur_span!=cur_align) @<Update width entry for spanned columns@>@;
+else if (w > width(cur_align)) width(cur_align)=w;
+type(u)=unset_node;span_count(u)=n;@/
+@<Determine the stretch order@>;
+glue_order(u)=o;glue_stretch(u)=total_stretch[o];@/
+@<Determine the shrink order@>;
+glue_sign(u)=o;glue_shrink(u)=total_shrink[o];@/
+pop_nest();link(tail)=u;tail=u;
+}
+
+@ A span node is a 2-word record containing |width|, |info|, and |link|
+fields. The |link| field is not really a link, it indicates the number of
+spanned columns; the |info| field points to a span node for the same
+starting column, having a greater extent of spanning, or to |end_span|,
+which has the largest possible |link| field; the |width| field holds the
+largest natural width corresponding to a particular set of spanned columns.
+
+A list of the maximum widths so far, for spanned columns starting at a
+given column, begins with the |info| field of the alignrecord for that
+column.
+
+@d span_node_size 2 /*number of |mem| words for a span node*/
+
+@<Initialize the special list heads...@>=
+link(end_span)=max_quarterword+1;info(end_span)=null;
+
+@ @<Update width entry for spanned columns@>=
+{@+q=cur_span;
+@/do@+{incr(n);q=link(link(q));
+}@+ while (!(q==cur_align));
+if (n > max_quarterword) confusion("256 spans"); /*this can happen, but won't*/
+@^system dependencies@>
+@:this can't happen 256 spans}{\quad 256 spans@>
+q=cur_span;while (link(info(q)) < n) q=info(q);
+if (link(info(q)) > n)
+ {@+s=get_node(span_node_size);info(s)=info(q);link(s)=n;
+ info(q)=s;width(s)=w;
+ }
+else if (width(info(q)) < w) width(info(q))=w;
+}
+
+@ At the end of a row, we append an unset box to the current vlist (for
+\.{\\halign}) or the current hlist (for \.{\\valign}). This unset box
+contains the unset boxes for the columns, separated by the tabskip glue.
+Everything will be set later.
+
+@p static void fin_row(void)
+{@+pointer p; /*the new unset box*/
+if (mode==-hmode)
+ {@+p=hpack(link(head), natural);
+ pop_nest();append_to_vlist(p);
+ if (cur_head!=cur_tail)
+ {@+link(tail)=link(cur_head);tail=cur_tail;
+ }
+ }
+else{@+p=vpack(link(head), natural);pop_nest();
+ link(tail)=p;tail=p;space_factor=1000;
+ }
+type(p)=unset_node;glue_stretch(p)=0;
+if (every_cr!=null) begin_token_list(every_cr, every_cr_text);
+align_peek();
+} /*note that |glue_shrink(p)==0| since |glue_shrink====shift_amount|*/
+
+@ Finally, we will reach the end of the alignment, and we can breathe a
+sigh of relief that memory hasn't overflowed. All the unset boxes will now be
+set so that the columns line up, taking due account of spanned columns.
+
+@p static void do_assignments(void);@/
+static void resume_after_display(void);@/
+static void build_page(void);@/
+static void fin_align(void)
+{@+pointer @!p, @!q, @!r, @!s, @!u, @!v; /*registers for the list operations*/
+scaled @!t, @!w; /*width of column*/
+scaled @!o; /*shift offset for unset boxes*/
+halfword @!n; /*matching span amount*/
+scaled @!rule_save; /*temporary storage for |overfull_rule|*/
+memory_word @!aux_save; /*temporary storage for |aux|*/
+if (cur_group!=align_group) confusion("align1");
+@:this can't happen align}{\quad align@>
+unsave(); /*that |align_group| was for individual entries*/
+if (cur_group!=align_group) confusion("align0");
+unsave(); /*that |align_group| was for the whole alignment*/
+if (nest[nest_ptr-1].mode_field==mmode) o=display_indent;
+ else o=0;
+@<Go through the preamble list, determining the column widths and changing
+the alignrecords to dummy unset boxes@>;
+@<Package the preamble list, to determine the actual tabskip glue amounts,
+and let |p| point to this prototype box@>;
+@<Set the glue in all the unset boxes of the current list@>;
+flush_node_list(p);pop_alignment();
+@<Insert the \(c)current list into its environment@>;
+} @/
+@t\4@>@<Declare the procedure called |align_peek|@>@;
+
+@ It's time now to dismantle the preamble list and to compute the column
+widths. Let $w_{ij}$ be the maximum of the natural widths of all entries
+that span columns $i$ through $j$, inclusive. The alignrecord for column~$i$
+contains $w_{ii}$ in its |width| field, and there is also a linked list of
+the nonzero $w_{ij}$ for increasing $j$, accessible via the |info| field;
+these span nodes contain the value $j-i+|min_quarterword|$ in their
+|link| fields. The values of $w_{ii}$ were initialized to |null_flag|, which
+we regard as $-\infty$.
+
+The final column widths are defined by the formula
+$$w_j=\max_{1\le i\le j}\biggl( w_{ij}-\sum_{i\le k<j}(t_k+w_k)\biggr),$$
+where $t_k$ is the natural width of the tabskip glue between columns
+$k$ and~$k+1$. However, if $w_{ij}=-\infty$ for all |i| in the range
+|1 <= i <= j| (i.e., if every entry that involved column~|j| also involved
+column~|j+1|), we let $w_j=0$, and we zero out the tabskip glue after
+column~|j|.
+
+\TeX\ computes these values by using the following scheme: First $w_1=w_{11}$.
+Then replace $w_{2j}$ by $\max(w_{2j},w_{1j}-t_1-w_1)$, for all $j>1$.
+Then $w_2=w_{22}$. Then replace $w_{3j}$ by $\max(w_{3j},w_{2j}-t_2-w_2)$
+for all $j>2$; and so on. If any $w_j$ turns out to be $-\infty$, its
+value is changed to zero and so is the next tabskip.
+
+@<Go through the preamble list,...@>=
+q=link(preamble);
+@/do@+{flush_list(u_part(q));flush_list(v_part(q));
+p=link(link(q));
+if (width(q)==null_flag)
+ @<Nullify |width(q)| and the tabskip glue following this column@>;
+if (info(q)!=end_span)
+ @<Merge the widths in the span nodes of |q| with those of |p|, destroying
+the span nodes of |q|@>;
+type(q)=unset_node;span_count(q)=min_quarterword;height(q)=0;
+depth(q)=0;glue_order(q)=normal;glue_sign(q)=normal;
+glue_stretch(q)=0;glue_shrink(q)=0;q=p;
+}@+ while (!(q==null))
+
+@ @<Nullify |width(q)| and the tabskip glue following this column@>=
+{@+width(q)=0;r=link(q);s=glue_ptr(r);
+if (s!=zero_glue)
+ {@+add_glue_ref(zero_glue);delete_glue_ref(s);
+ glue_ptr(r)=zero_glue;
+ }
+}
+
+@ Merging of two span-node lists is a typical exercise in the manipulation of
+linearly linked data structures. The essential invariant in the following
+|@/do@+{| loop is that we want to dispense with node |r|, in |q|'s list,
+and |u| is its successor; all nodes of |p|'s list up to and including |s|
+have been processed, and the successor of |s| matches |r| or precedes |r|
+or follows |r|, according as |link(r)==n| or |link(r) > n| or |link(r) < n|.
+
+@<Merge the widths...@>=
+{@+t=width(q)+width(glue_ptr(link(q)));
+r=info(q);s=end_span;info(s)=p;n=min_quarterword+1;
+@/do@+{width(r)=width(r)-t;u=info(r);
+while (link(r) > n)
+ {@+s=info(s);n=link(info(s))+1;
+ }
+if (link(r) < n)
+ {@+info(r)=info(s);info(s)=r;decr(link(r));s=r;
+ }
+else{@+if (width(r) > width(info(s))) width(info(s))=width(r);
+ free_node(r, span_node_size);
+ }
+r=u;
+}@+ while (!(r==end_span));
+}
+
+@ Now the preamble list has been converted to a list of alternating unset
+boxes and tabskip glue, where the box widths are equal to the final
+column sizes. In case of \.{\\valign}, we change the widths to heights,
+so that a correct error message will be produced if the alignment is
+overfull or underfull.
+
+@<Package the preamble list...@>=
+save_ptr=save_ptr-2;pack_begin_line=-mode_line;
+if (mode==-vmode)
+ {@+rule_save=overfull_rule;
+ overfull_rule=0; /*prevent rule from being packaged*/
+ p=hpack(preamble, saved(1), saved(0));overfull_rule=rule_save;
+ }
+else{@+q=link(preamble);
+ @/do@+{height(q)=width(q);width(q)=0;q=link(link(q));
+ }@+ while (!(q==null));
+ p=vpack(preamble, saved(1), saved(0));
+ q=link(preamble);
+ @/do@+{width(q)=height(q);height(q)=0;q=link(link(q));
+ }@+ while (!(q==null));
+ }
+pack_begin_line=0
+
+@ @<Set the glue in all the unset...@>=
+q=link(head);s=head;
+while (q!=null)
+ {@+if (!is_char_node(q))
+ if (type(q)==unset_node)
+ @<Set the unset box |q| and the unset boxes in it@>@;
+ else if (type(q)==rule_node)
+ @<Make the running dimensions in rule |q| extend to the boundaries of
+the alignment@>;
+ s=q;q=link(q);
+ }
+
+@ @<Make the running dimensions in rule |q| extend...@>=
+{@+if (is_running(width(q))) width(q)=width(p);
+if (is_running(height(q))) height(q)=height(p);
+if (is_running(depth(q))) depth(q)=depth(p);
+if (o!=0)
+ {@+r=link(q);link(q)=null;q=hpack(q, natural);
+ shift_amount(q)=o;link(q)=r;link(s)=q;
+ }
+}
+
+@ The unset box |q| represents a row that contains one or more unset boxes,
+depending on how soon \.{\\cr} occurred in that row.
+
+@<Set the unset box |q| and the unset boxes in it@>=
+{@+if (mode==-vmode)
+ {@+type(q)=hlist_node;width(q)=width(p);
+ }
+else{@+type(q)=vlist_node;height(q)=height(p);
+ }
+glue_order(q)=glue_order(p);glue_sign(q)=glue_sign(p);
+glue_set(q)=glue_set(p);shift_amount(q)=o;
+r=link(list_ptr(q));s=link(list_ptr(p));
+@/do@+{@<Set the glue in node |r| and change it from an unset node@>;
+r=link(link(r));s=link(link(s));
+}@+ while (!(r==null));
+}
+
+@ A box made from spanned columns will be followed by tabskip glue nodes and
+by empty boxes as if there were no spanning. This permits perfect alignment
+of subsequent entries, and it prevents values that depend on floating point
+arithmetic from entering into the dimensions of any boxes.
+
+@<Set the glue in node |r|...@>=
+n=span_count(r);t=width(s);w=t;u=hold_head;
+while (n > min_quarterword)
+ {@+decr(n);
+ @<Append tabskip glue and an empty box to list |u|, and update |s| and |t|
+as the prototype nodes are passed@>;
+ }
+if (mode==-vmode)
+ @<Make the unset node |r| into an |hlist_node| of width |w|, setting the
+glue as if the width were |t|@>@;
+else@<Make the unset node |r| into a |vlist_node| of height |w|, setting the
+glue as if the height were |t|@>;
+shift_amount(r)=0;
+if (u!=hold_head) /*append blank boxes to account for spanned nodes*/
+ {@+link(u)=link(r);link(r)=link(hold_head);r=u;
+ }
+
+@ @<Append tabskip glue and an empty box to list |u|...@>=
+s=link(s);v=glue_ptr(s);link(u)=new_glue(v);u=link(u);
+subtype(u)=tab_skip_code+1;t=t+width(v);
+if (glue_sign(p)==stretching)
+ {@+if (stretch_order(v)==glue_order(p))
+ t=t+round(unfix(glue_set(p))*stretch(v));
+@^real multiplication@>
+ }
+else if (glue_sign(p)==shrinking)
+ {@+if (shrink_order(v)==glue_order(p))
+ t=t-round(unfix(glue_set(p))*shrink(v));
+ }
+s=link(s);link(u)=new_null_box();u=link(u);t=t+width(s);
+if (mode==-vmode) width(u)=width(s);@+else
+ {@+type(u)=vlist_node;height(u)=width(s);
+ }
+
+@ @<Make the unset node |r| into an |hlist_node| of width |w|...@>=
+{@+height(r)=height(q);depth(r)=depth(q);
+if (t==width(r))
+ {@+glue_sign(r)=normal;glue_order(r)=normal;
+ set_glue_ratio_zero(glue_set(r));
+ }
+else if (t > width(r))
+ {@+glue_sign(r)=stretching;
+ if (glue_stretch(r)==0) set_glue_ratio_zero(glue_set(r));
+ else glue_set(r)=fix((t-width(r))/(double)glue_stretch(r));
+@^real division@>
+ }
+else{@+glue_order(r)=glue_sign(r);glue_sign(r)=shrinking;
+ if (glue_shrink(r)==0) set_glue_ratio_zero(glue_set(r));
+ else if ((glue_order(r)==normal)&&(width(r)-t > glue_shrink(r)))
+ set_glue_ratio_one(glue_set(r));
+ else glue_set(r)=fix((width(r)-t)/(double)glue_shrink(r));
+ }
+width(r)=w;type(r)=hlist_node;
+}
+
+@ @<Make the unset node |r| into a |vlist_node| of height |w|...@>=
+{@+width(r)=width(q);
+if (t==height(r))
+ {@+glue_sign(r)=normal;glue_order(r)=normal;
+ set_glue_ratio_zero(glue_set(r));
+ }
+else if (t > height(r))
+ {@+glue_sign(r)=stretching;
+ if (glue_stretch(r)==0) set_glue_ratio_zero(glue_set(r));
+ else glue_set(r)=fix((t-height(r))/(double)glue_stretch(r));
+@^real division@>
+ }
+else{@+glue_order(r)=glue_sign(r);glue_sign(r)=shrinking;
+ if (glue_shrink(r)==0) set_glue_ratio_zero(glue_set(r));
+ else if ((glue_order(r)==normal)&&(height(r)-t > glue_shrink(r)))
+ set_glue_ratio_one(glue_set(r));
+ else glue_set(r)=fix((height(r)-t)/(double)glue_shrink(r));
+ }
+height(r)=w;type(r)=vlist_node;
+}
+
+@ We now have a completed alignment, in the list that starts at |head|
+and ends at |tail|. This list will be merged with the one that encloses
+it. (In case the enclosing mode is |mmode|, for displayed formulas,
+we will need to insert glue before and after the display; that part of the
+program will be deferred until we're more familiar with such operations.)
+
+In restricted horizontal mode, the |clang| part of |aux| is undefined;
+an over-cautious \PASCAL\ runtime system may complain about this.
+@^dirty \PASCAL@>
+
+@<Insert the \(c)current list into its environment@>=
+aux_save=aux;p=link(head);q=tail;pop_nest();
+if (mode==mmode) @<Finish an alignment in a display@>@;
+else{@+aux=aux_save;link(tail)=p;
+ if (p!=null) tail=q;
+ if (mode==vmode) build_page();
+ }
+
+@* Breaking paragraphs into lines.
+We come now to what is probably the most interesting algorithm of \TeX:
+the mechanism for choosing the ``best possible'' breakpoints that yield
+the individual lines of a paragraph. \TeX's line-breaking algorithm takes
+a given horizontal list and converts it to a sequence of boxes that are
+appended to the current vertical list. In the course of doing this, it
+creates a special data structure containing three kinds of records that are
+not used elsewhere in \TeX. Such nodes are created while a paragraph is
+being processed, and they are destroyed afterwards; thus, the other parts
+of \TeX\ do not need to know anything about how line-breaking is done.
+
+The method used here is based on an approach devised by Michael F. Plass and
+@^Plass, Michael Frederick@>
+@^Knuth, Donald Ervin@>
+the author in 1977, subsequently generalized and improved by the same two
+people in 1980. A detailed discussion appears in {\sl Software---Practice
+and Experience \bf11} (1981), 1119--1184, where it is shown that the
+line-breaking problem can be regarded as a special case of the problem of
+computing the shortest path in an acyclic network. The cited paper includes
+numerous examples and describes the history of line breaking as it has been
+practiced by printers through the ages. The present implementation adds two
+new ideas to the algorithm of 1980: Memory space requirements are considerably
+reduced by using smaller records for inactive nodes than for active ones,
+and arithmetic overflow is avoided by using ``delta distances'' instead of
+keeping track of the total distance from the beginning of the paragraph to the
+current point.
+
+@ The |line_break| procedure should be invoked only in horizontal mode; it
+leaves that mode and places its output into the current vlist of the
+enclosing vertical mode (or internal vertical mode).
+There is one explicit parameter: |final_widow_penalty| is the amount of
+additional penalty to be inserted before the final line of the paragraph.
+
+There are also a number of implicit parameters: The hlist to be broken
+starts at |link(head)|, and it is nonempty. The value of |prev_graf| in the
+enclosing semantic level tells where the paragraph should begin in the
+sequence of line numbers, in case hanging indentation or \.{\\parshape}
+is in use; |prev_graf| is zero unless this paragraph is being continued
+after a displayed formula. Other implicit parameters, such as the
+|par_shape_ptr| and various penalties to use for hyphenation, etc., appear
+in |eqtb|.
+
+After |line_break| has acted, it will have updated the current vlist and the
+value of |prev_graf|. Furthermore, the global variable |just_box| will
+point to the final box created by |line_break|, so that the width of this
+line can be ascertained when it is necessary to decide whether to use
+|above_display_skip| or |above_display_short_skip| before a displayed formula.
+
+@<Glob...@>=
+static pointer @!just_box; /*the |hlist_node| for the last line of the new paragraph*/
+
+@ Since |line_break| is a rather lengthy procedure---sort of a small world unto
+itself---we must build it up little by little, somewhat more cautiously
+than we have done with the simpler procedures of \TeX. Here is the
+general outline.
+
+@p @t\4@>@<Declare subprocedures for |line_break|@>@;
+static void line_break(int @!final_widow_penalty)
+{@+
+@<Local variables for line breaking@>@;
+@<Local variables to save the profiling context@>@;
+@<Charge the time used here on |line_break|@>@;
+pack_begin_line=mode_line; /*this is for over/underfull box messages*/
+@<Get ready to start line breaking@>;
+@<Find optimal breakpoints@>;
+@<Break the paragraph at the chosen breakpoints, justify the resulting lines
+to the correct widths, and append them to the current vertical list@>;
+@<Clean up the memory by removing the break nodes@>;
+pack_begin_line=0;
+@<restore the previous current file, line, and command@>@;
+}
+@#
+@t\4@>@<Declare \eTeX\ procedures for use by |main_control|@>@;
+
+@ The first task is to move the list from |head| to |temp_head| and go
+into the enclosing semantic level. We also append the \.{\\parfillskip}
+glue to the end of the paragraph, removing a space (or other glue node) if
+it was there, since spaces usually precede blank lines and instances of
+`\.{\$\$}'. The |par_fill_skip| is preceded by an infinite penalty, so
+it will never be considered as a potential breakpoint.
+
+This code assumes that a |glue_node| and a |penalty_node| occupy the
+same number of |mem|~words.
+@^data structure assumptions@>
+
+@<Get ready to start...@>=
+link(temp_head)=link(head);
+if (is_char_node(tail)) tail_append(new_penalty(inf_penalty))@;
+else if (type(tail)!=glue_node) tail_append(new_penalty(inf_penalty))@;
+else{@+type(tail)=penalty_node;delete_glue_ref(glue_ptr(tail));
+ flush_node_list(leader_ptr(tail));penalty(tail)=inf_penalty;
+ }
+link(tail)=new_param_glue(par_fill_skip_code);
+init_cur_lang=prev_graf%0200000;
+init_l_hyf=prev_graf/020000000;
+init_r_hyf=(prev_graf/0200000)%0100;
+pop_nest();
+
+@ When looking for optimal line breaks, \TeX\ creates a ``break node'' for
+each break that is {\sl feasible}, in the sense that there is a way to end
+a line at the given place without requiring any line to stretch more than
+a given tolerance. A break node is characterized by three things: the position
+of the break (which is a pointer to a |glue_node|, |math_node|, |penalty_node|,
+or |disc_node|); the ordinal number of the line that will follow this
+breakpoint; and the fitness classification of the line that has just
+ended, i.e., |tight_fit|, |decent_fit|, |loose_fit|, or |very_loose_fit|.
+
+@d tight_fit 3 /*fitness classification for lines shrinking 0.5 to 1.0 of their
+ shrinkability*/
+@d loose_fit 1 /*fitness classification for lines stretching 0.5 to 1.0 of their
+ stretchability*/
+@d very_loose_fit 0 /*fitness classification for lines stretching more than
+ their stretchability*/
+@d decent_fit 2 /*fitness classification for all other lines*/
+
+@ The algorithm essentially determines the best possible way to achieve
+each feasible combination of position, line, and fitness. Thus, it answers
+questions like, ``What is the best way to break the opening part of the
+paragraph so that the fourth line is a tight line ending at such-and-such
+a place?'' However, the fact that all lines are to be the same length
+after a certain point makes it possible to regard all sufficiently large
+line numbers as equivalent, when the looseness parameter is zero, and this
+makes it possible for the algorithm to save space and time.
+
+An ``active node'' and a ``passive node'' are created in |mem| for each
+feasible breakpoint that needs to be considered. Active nodes are three
+words long and passive nodes are two words long. We need active nodes only
+for breakpoints near the place in the paragraph that is currently being
+examined, so they are recycled within a comparatively short time after
+they are created.
+
+@ An active node for a given breakpoint contains six fields:
+
+\yskip\hang|link| points to the next node in the list of active nodes; the
+last active node has |link==last_active|.
+
+\yskip\hang|break_node| points to the passive node associated with this
+breakpoint.
+
+\yskip\hang|line_number| is the number of the line that follows this
+breakpoint.
+
+\yskip\hang|fitness| is the fitness classification of the line ending at this
+breakpoint.
+
+\yskip\hang|type| is either |hyphenated| or |unhyphenated|, depending on
+whether this breakpoint is a |disc_node|.
+
+\yskip\hang|total_demerits| is the minimum possible sum of demerits over all
+lines leading from the beginning of the paragraph to this breakpoint.
+
+\yskip\noindent
+The value of |link(active)| points to the first active node on a linked list
+of all currently active nodes. This list is in order by |line_number|,
+except that nodes with |line_number > easy_line| may be in any order relative
+to each other.
+
+@d active_node_size 3 /*number of words in active nodes*/
+@d fitness(A) subtype(A) /*|very_loose_fit dotdot tight_fit| on final line for this break*/
+@d break_node(A) rlink(A) /*pointer to the corresponding passive node*/
+@d line_number(A) llink(A) /*line that begins at this breakpoint*/
+@d total_demerits(A) mem[A+2].i /*the quantity that \TeX\ minimizes*/
+@d unhyphenated 0 /*the |type| of a normal active break node*/
+@d hyphenated 1 /*the |type| of an active node that breaks at a |disc_node|*/
+@d last_active active /*the active list ends where it begins*/
+
+@ @<Initialize the special list heads...@>=
+type(last_active)=hyphenated;line_number(last_active)=max_halfword;
+subtype(last_active)=0; /*the |subtype| is never examined by the algorithm*/
+
+@ The passive node for a given breakpoint contains only four fields:
+
+\yskip\hang|link| points to the passive node created just before this one,
+if any, otherwise it is |null|.
+
+\yskip\hang|cur_break| points to the position of this breakpoint in the
+horizontal list for the paragraph being broken.
+
+\yskip\hang|prev_break| points to the passive node that should precede this
+one in an optimal path to this breakpoint.
+
+\yskip\hang|serial| is equal to |n| if this passive node is the |n|th
+one created during the current pass. (This field is used only when
+printing out detailed statistics about the line-breaking calculations.)
+
+\yskip\noindent
+There is a global variable called |passive| that points to the most
+recently created passive node. Another global variable, |printed_node|,
+is used to help print out the paragraph when detailed information about
+the line-breaking computation is being displayed.
+
+@d passive_node_size 2 /*number of words in passive nodes*/
+@d cur_break(A) rlink(A) /*in passive node, points to position of this breakpoint*/
+@d prev_break(A) llink(A) /*points to passive node that should precede this one*/
+@d serial(A) info(A) /*serial number for symbolic identification*/
+
+@<Glob...@>=
+static pointer @!passive; /*most recent node on passive list*/
+static pointer @!printed_node; /*most recent node that has been printed*/
+static halfword @!pass_number; /*the number of passive nodes allocated on this pass*/
+
+@ The active list also contains ``delta'' nodes that help the algorithm
+compute the badness of individual lines. Such nodes appear only between two
+active nodes, and they have |type==delta_node|. If |p| and |r| are active nodes
+and if |q| is a delta node between them, so that |link(p)==q| and |link(q)==r|,
+then |q| tells the space difference between lines in the horizontal list that
+start after breakpoint |p| and lines that start after breakpoint |r|. In
+other words, if we know the length of the line that starts after |p| and
+ends at our current position, then the corresponding length of the line that
+starts after |r| is obtained by adding the amounts in node~|q|. A delta node
+contains six scaled numbers, since it must record the net change in glue
+stretchability with respect to all orders of infinity. The natural width
+difference appears in |mem[q+1].sc|; the stretch differences in units of
+pt, fil, fill, and filll appear in |mem[q+2 dotdot q+5].sc|; and the shrink difference
+appears in |mem[q+6].sc|. The |subtype| field of a delta node is not used.
+
+@d delta_node_size 7 /*number of words in a delta node*/
+@d delta_node 2 /*|type| field in a delta node*/
+
+@ As the algorithm runs, it maintains a set of six delta-like registers
+for the length of the line following the first active breakpoint to the
+current position in the given hlist. When it makes a pass through the
+active list, it also maintains a similar set of six registers for the
+length following the active breakpoint of current interest. A third set
+holds the length of an empty line (namely, the sum of \.{\\leftskip} and
+\.{\\rightskip}); and a fourth set is used to create new delta nodes.
+
+When we pass a delta node we want to do operations like
+$$\hbox{\ignorespaces|for
+k=1 to 6 do cur_active_width[k]=cur_active_width[k]+mem[q+k].sc|};$$ and we
+want to do this without the overhead of |for| loops. The |do_all_six|
+macro makes such six-tuples convenient.
+
+@d do_all_six(A) A(1);A(2);A(3);A(4);A(5);A(6)
+
+@<Glob...@>=
+static scaled @!active_width0[6], *const @!active_width = @!active_width0-1;
+ /*distance from first active node to~|cur_p|*/
+static scaled @!cur_active_width0[6],
+ *const @!cur_active_width = @!cur_active_width0-1; /*distance from current active node*/
+static scaled @!background0[6], *const @!background = @!background0-1; /*length of an ``empty'' line*/
+static scaled @!break_width0[6], *const @!break_width = @!break_width0-1; /*length being computed after current break*/
+
+@ Let's state the principles of the delta nodes more precisely and concisely,
+so that the following programs will be less obscure. For each legal
+breakpoint~|p| in the paragraph, we define two quantities $\alpha(p)$ and
+$\beta(p)$ such that the length of material in a line from breakpoint~|p|
+to breakpoint~|q| is $\gamma+\beta(q)-\alpha(p)$, for some fixed $\gamma$.
+Intuitively, $\alpha(p)$ and $\beta(q)$ are the total length of material from
+the beginning of the paragraph to a point ``after'' a break at |p| and to a
+point ``before'' a break at |q|; and $\gamma$ is the width of an empty line,
+namely the length contributed by \.{\\leftskip} and \.{\\rightskip}.
+
+Suppose, for example, that the paragraph consists entirely of alternating
+boxes and glue skips; let the boxes have widths $x_1\ldots x_n$ and
+let the skips have widths $y_1\ldots y_n$, so that the paragraph can be
+represented by $x_1y_1\ldots x_ny_n$. Let $p_i$ be the legal breakpoint
+at $y_i$; then $\alpha(p_i)=x_1+y_1+\cdots+x_i+y_i$, and $\beta(p_i)=
+x_1+y_1+\cdots+x_i$. To check this, note that the length of material from
+$p_2$ to $p_5$, say, is $\gamma+x_3+y_3+x_4+y_4+x_5=\gamma+\beta(p_5)
+-\alpha(p_2)$.
+
+The quantities $\alpha$, $\beta$, $\gamma$ involve glue stretchability and
+shrinkability as well as a natural width. If we were to compute $\alpha(p)$
+and $\beta(p)$ for each |p|, we would need multiple precision arithmetic, and
+the multiprecise numbers would have to be kept in the active nodes.
+\TeX\ avoids this problem by working entirely with relative differences
+or ``deltas.'' Suppose, for example, that the active list contains
+$a_1\,\delta_1\,a_2\,\delta_2\,a_3$, where the |a|'s are active breakpoints
+and the $\delta$'s are delta nodes. Then $\delta_1=\alpha(a_1)-\alpha(a_2)$
+and $\delta_2=\alpha(a_2)-\alpha(a_3)$. If the line breaking algorithm is
+currently positioned at some other breakpoint |p|, the |active_width| array
+contains the value $\gamma+\beta(p)-\alpha(a_1)$. If we are scanning through
+the list of active nodes and considering a tentative line that runs from
+$a_2$ to~|p|, say, the |cur_active_width| array will contain the value
+$\gamma+\beta(p)-\alpha(a_2)$. Thus, when we move from $a_2$ to $a_3$,
+we want to add $\alpha(a_2)-\alpha(a_3)$ to |cur_active_width|; and this
+is just $\delta_2$, which appears in the active list between $a_2$ and
+$a_3$. The |background| array contains $\gamma$. The |break_width| array
+will be used to calculate values of new delta nodes when the active
+list is being updated.
+
+@ Glue nodes in a horizontal list that is being paragraphed are not supposed to
+include ``infinite'' shrinkability; that is why the algorithm maintains
+four registers for stretching but only one for shrinking. If the user tries to
+introduce infinite shrinkability, the shrinkability will be reset to finite
+and an error message will be issued. A boolean variable |no_shrink_error_yet|
+prevents this error message from appearing more than once per paragraph.
+
+@d check_shrinkage(A) if ((shrink_order(A)!=normal)&&(shrink(A)!=0))
+ {@+A=finite_shrink(A);
+ }
+
+@<Glob...@>=
+static bool @!no_shrink_error_yet; /*have we complained about infinite shrinkage?*/
+
+@ @<Declare subprocedures for |line_break|@>=
+static pointer finite_shrink(pointer @!p) /*recovers from infinite shrinkage*/
+{@+pointer q; /*new glue specification*/
+if (no_shrink_error_yet)
+ {@+no_shrink_error_yet=false;
+#ifdef @!STAT
+ if (tracing_paragraphs > 0) end_diagnostic(true);
+#endif
+@;
+ print_err("Infinite glue shrinkage found in a paragraph");
+@.Infinite glue shrinkage...@>
+ help5("The paragraph just ended includes some glue that has",@/
+ "infinite shrinkability, e.g., `\\hskip 0pt minus 1fil'.",@/
+ "Such glue doesn't belong there---it allows a paragraph",@/
+ "of any length to fit on one line. But it's safe to proceed,",@/
+ "since the offensive shrinkability has been made finite.");
+ error();
+#ifdef @!STAT
+ if (tracing_paragraphs > 0) begin_diagnostic();
+#endif
+@;
+ }
+q=new_spec(p);shrink_order(q)=normal;
+delete_glue_ref(p);return q;
+}
+
+@ @<Get ready to start...@>=
+no_shrink_error_yet=true;@/
+check_shrinkage(left_skip);check_shrinkage(right_skip);@/
+q=left_skip;r=right_skip;background[1]=width(q)+width(r);@/
+background[2]=0;background[3]=0;background[4]=0;background[5]=0;@/
+background[2+stretch_order(q)]=stretch(q);@/
+background[2+stretch_order(r)]=@|background[2+stretch_order(r)]+stretch(r);@/
+background[6]=shrink(q)+shrink(r);
+
+@ A pointer variable |cur_p| runs through the given horizontal list as we look
+for breakpoints. This variable is global, since it is used both by |line_break|
+and by its subprocedure |try_break|.
+
+Another global variable called |threshold| is used to determine the feasibility
+of individual lines: Breakpoints are feasible if there is a way to reach
+them without creating lines whose badness exceeds |threshold|. (The
+badness is compared to |threshold| before penalties are added, so that
+penalty values do not affect the feasibility of breakpoints, except that
+no break is allowed when the penalty is 10000 or more.) If |threshold|
+is 10000 or more, all legal breaks are considered feasible, since the
+|badness| function specified above never returns a value greater than~10000.
+
+Up to three passes might be made through the paragraph in an attempt to find at
+least one set of feasible breakpoints. On the first pass, we have
+|threshold==pretolerance| and |second_pass==final_pass==false|.
+If this pass fails to find a
+feasible solution, |threshold| is set to |tolerance|, |second_pass| is set
+|true|, and an attempt is made to hyphenate as many words as possible.
+If that fails too, we add |emergency_stretch| to the background
+stretchability and set |final_pass==true|.
+
+@<Glob...@>=
+static pointer @!cur_p; /*the current breakpoint under consideration*/
+static bool @!second_pass; /*is this our second attempt to break this paragraph?*/
+static bool @!final_pass; /*is this our final attempt to break this paragraph?*/
+static int @!threshold; /*maximum badness on feasible lines*/
+
+@ The heart of the line-breaking procedure is `|try_break|', a subroutine
+that tests if the current breakpoint |cur_p| is feasible, by running
+through the active list to see what lines of text can be made from active
+nodes to~|cur_p|. If feasible breaks are possible, new break nodes are
+created. If |cur_p| is too far from an active node, that node is
+deactivated.
+
+The parameter |pi| to |try_break| is the penalty associated
+with a break at |cur_p|; we have |pi==eject_penalty| if the break is forced,
+and |pi==inf_penalty| if the break is illegal.
+
+The other parameter, |break_type|, is set to |hyphenated| or |unhyphenated|,
+depending on whether or not the current break is at a |disc_node|. The
+end of a paragraph is also regarded as `|hyphenated|'; this case is
+distinguishable by the condition |cur_p==null|.
+
+@d copy_to_cur_active(A) cur_active_width[A]=active_width[A]
+@<Declare subprocedures for |line_break|@>=
+static void try_break(int @!pi, small_number @!break_type)
+{@+
+pointer r; /*runs through the active list*/
+pointer @!prev_r; /*stays a step behind |r|*/
+halfword @!old_l; /*maximum line number in current equivalence class of lines*/
+bool @!no_break_yet; /*have we found a feasible break at |cur_p|?*/
+@<Other local variables for |try_break|@>@;
+@<Make sure that |pi| is in the proper range@>;
+no_break_yet=true;prev_r=active;old_l=0;
+do_all_six(copy_to_cur_active);
+loop@+{@+resume: r=link(prev_r);
+ @<If node |r| is of type |delta_node|, update |cur_active_width|, set |prev_r|
+and |prev_prev_r|, then |goto resume|@>;
+ @<If a line number class has ended, create new active nodes for the best
+feasible breaks in that class; then |return| if |r=last_active|, otherwise
+compute the new |line_width|@>;
+ @<Consider the demerits for a line from |r| to |cur_p|; deactivate node
+|r| if it should no longer be active; then |goto resume| if a line from
+|r| to |cur_p| is infeasible, otherwise record a new feasible break@>;
+ }
+end: ;
+#ifdef @!STAT
+@<Update the value of |printed_node| for symbolic displays@>;
+#endif
+@;
+}
+
+@ @<Other local variables for |try_break|@>=
+pointer @!prev_prev_r; /*a step behind |prev_r|, if |type(prev_r)==delta_node|*/
+pointer @!s; /*runs through nodes ahead of |cur_p|*/
+pointer @!q; /*points to a new node being created*/
+pointer @!v; /*points to a glue specification or a node ahead of |cur_p|*/
+int @!t; /*node count, if |cur_p| is a discretionary node*/
+internal_font_number @!f; /*used in character width calculation*/
+halfword @!l; /*line number of current active node*/
+bool @!node_r_stays_active; /*should node |r| remain in the active list?*/
+scaled @!line_width; /*the current line will be justified to this width*/
+int @!fit_class; /*possible fitness class of test line*/
+halfword @!b; /*badness of test line*/
+int @!d; /*demerits of test line*/
+bool @!artificial_demerits; /*has |d| been forced to zero?*/
+#ifdef @!STAT
+pointer @!save_link; /*temporarily holds value of |link(cur_p)|*/
+#endif
+scaled @!shortfall; /*used in badness calculations*/
+
+@ @<Make sure that |pi| is in the proper range@>=
+if (abs(pi) >= inf_penalty)
+ if (pi > 0) goto end; /*this breakpoint is inhibited by infinite penalty*/
+ else pi=eject_penalty /*this breakpoint will be forced*/
+
+@ The following code uses the fact that |type(last_active)!=delta_node|.
+
+@d update_width(A) @|
+ cur_active_width[A]=cur_active_width[A]+mem[r+A].sc
+
+@<If node |r|...@>=
+@^inner loop@>
+if (type(r)==delta_node)
+ {@+do_all_six(update_width);
+ prev_prev_r=prev_r;prev_r=r;goto resume;
+ }
+
+@ As we consider various ways to end a line at |cur_p|, in a given line number
+class, we keep track of the best total demerits known, in an array with
+one entry for each of the fitness classifications. For example,
+|minimal_demerits[tight_fit]| contains the fewest total demerits of feasible
+line breaks ending at |cur_p| with a |tight_fit| line; |best_place[tight_fit]|
+points to the passive node for the break before~|cur_p| that achieves such
+an optimum; and |best_pl_line[tight_fit]| is the |line_number| field in the
+active node corresponding to |best_place[tight_fit]|. When no feasible break
+sequence is known, the |minimal_demerits| entries will be equal to
+|awful_bad|, which is $2^{30}-1$. Another variable, |minimum_demerits|,
+keeps track of the smallest value in the |minimal_demerits| array.
+
+@d awful_bad 07777777777 /*more than a billion demerits*/
+
+@<Global...@>=
+static int @!minimal_demerits0[tight_fit-very_loose_fit+1],
+ *const @!minimal_demerits = @!minimal_demerits0-very_loose_fit; /*best total
+ demerits known for current line class and position, given the fitness*/
+static int @!minimum_demerits; /*best total demerits known for current line class
+ and position*/
+static pointer @!best_place0[tight_fit-very_loose_fit+1],
+ *const @!best_place = @!best_place0-very_loose_fit; /*how to achieve
+ |minimal_demerits|*/
+static halfword @!best_pl_line0[tight_fit-very_loose_fit+1],
+ *const @!best_pl_line = @!best_pl_line0-very_loose_fit; /*corresponding
+ line number*/
+
+@ @<Get ready to start...@>=
+minimum_demerits=awful_bad;
+minimal_demerits[tight_fit]=awful_bad;
+minimal_demerits[decent_fit]=awful_bad;
+minimal_demerits[loose_fit]=awful_bad;
+minimal_demerits[very_loose_fit]=awful_bad;
+
+@ The first part of the following code is part of \TeX's inner loop, so
+we don't want to waste any time. The current active node, namely node |r|,
+contains the line number that will be considered next. At the end of the
+list we have arranged the data structure so that |r==last_active| and
+|line_number(last_active) > old_l|.
+@^inner loop@>
+
+@<If a line number class...@>=
+{@+l=line_number(r);
+if (l > old_l)
+ {@+ /*now we are no longer in the inner loop*/
+ if ((minimum_demerits < awful_bad)&&@|
+ ((old_l!=easy_line)||(r==last_active)))
+ @<Create new active nodes for the best feasible breaks just found@>;
+ if (r==last_active) goto end;
+ @<Compute the new line width@>;
+ }
+}
+
+@ It is not necessary to create new active nodes having |minimal_demerits|
+greater than
+|minimum_demerits+abs(adj_demerits)|, since such active nodes will never
+be chosen in the final paragraph breaks. This observation allows us to
+omit a substantial number of feasible breakpoints from further consideration.
+
+@<Create new active nodes...@>=
+{@+if (no_break_yet) @<Compute the values of |break_width|@>;
+@<Insert a delta node to prepare for breaks at |cur_p|@>;
+if (abs(adj_demerits) >= awful_bad-minimum_demerits)
+ minimum_demerits=awful_bad-1;
+else minimum_demerits=minimum_demerits+abs(adj_demerits);
+for (fit_class=very_loose_fit; fit_class<=tight_fit; fit_class++)
+ {@+if (minimal_demerits[fit_class] <= minimum_demerits)
+ @<Insert a new active node from |best_place[fit_class]| to |cur_p|@>;
+ minimal_demerits[fit_class]=awful_bad;
+ }
+minimum_demerits=awful_bad;
+@<Insert a delta node to prepare for the next active node@>;
+}
+
+@ When we insert a new active node for a break at |cur_p|, suppose this
+new node is to be placed just before active node |a|; then we essentially
+want to insert `$\delta\,|cur_p|\,\delta^\prime$' before |a|, where
+$\delta=\alpha(a)-\alpha(|cur_p|)$ and $\delta^\prime=\alpha(|cur_p|)-\alpha(a)$
+in the notation explained above. The |cur_active_width| array now holds
+$\gamma+\beta(|cur_p|)-\alpha(a)$; so $\delta$ can be obtained by
+subtracting |cur_active_width| from the quantity $\gamma+\beta(|cur_p|)-
+\alpha(|cur_p|)$. The latter quantity can be regarded as the length of a
+line ``from |cur_p| to |cur_p|''; we call it the |break_width| at |cur_p|.
+
+The |break_width| is usually negative, since it consists of the background
+(which is normally zero) minus the width of nodes following~|cur_p| that are
+eliminated after a break. If, for example, node |cur_p| is a glue node, the
+width of this glue is subtracted from the background; and we also look
+ahead to eliminate all subsequent glue and penalty and kern and math
+nodes, subtracting their widths as well.
+
+Kern nodes do not disappear at a line break unless they are |explicit|.
+
+@d set_break_width_to_background(A) break_width[A]=background[A]
+
+@<Compute the values of |break...@>=
+{@+no_break_yet=false;do_all_six(set_break_width_to_background);
+s=cur_p;
+if (break_type > unhyphenated) if (cur_p!=null)
+ @<Compute the discretionary |break_width| values@>;
+while (s!=null)
+ {@+if (is_char_node(s)) goto done;
+ switch (type(s)) {
+ case glue_node: @<Subtract glue from |break_width|@>@;@+break;
+ case penalty_node: do_nothing;@+break;
+ case math_node: break_width[1]=break_width[1]-width(s);@+break;
+ case kern_node: if (subtype(s)!=explicit) goto done;
+ else break_width[1]=break_width[1]-width(s);@+break;
+ default:goto done;
+ } @/
+ s=link(s);
+ }
+done: ;}
+
+@ @<Subtract glue from |break...@>=
+{@+v=glue_ptr(s);break_width[1]=break_width[1]-width(v);
+break_width[2+stretch_order(v)]=break_width[2+stretch_order(v)]-stretch(v);
+break_width[6]=break_width[6]-shrink(v);
+}
+
+@ When |cur_p| is a discretionary break, the length of a line ``from |cur_p| to
+|cur_p|'' has to be defined properly so that the other calculations work out.
+Suppose that the pre-break text at |cur_p| has length $l_0$, the post-break
+text has length $l_1$, and the replacement text has length |l|. Suppose
+also that |q| is the node following the replacement text. Then length of a
+line from |cur_p| to |q| will be computed as $\gamma+\beta(q)-\alpha(|cur_p|)$,
+where $\beta(q)=\beta(|cur_p|)-l_0+l$. The actual length will be the background
+plus $l_1$, so the length from |cur_p| to |cur_p| should be $\gamma+l_0+l_1-l$.
+If the post-break text of the discretionary is empty, a break may also
+discard~|q|; in that unusual case we subtract the length of~|q| and any
+other nodes that will be discarded after the discretionary break.
+
+The value of $l_0$ need not be computed, since |line_break| will put
+it into the global variable |disc_width| before calling |try_break|.
+
+@<Glob...@>=
+static scaled @!disc_width; /*the length of discretionary material preceding a break*/
+
+@ @<Compute the discretionary |break...@>=
+{@+t=replace_count(cur_p);v=cur_p;s=post_break(cur_p);
+while (t > 0)
+ {@+decr(t);v=link(v);
+ @<Subtract the width of node |v| from |break_width|@>;
+ }
+while (s!=null)
+ {@+@<Add the width of node |s| to |break_width|@>;
+ s=link(s);
+ }
+break_width[1]=break_width[1]+disc_width;
+if (post_break(cur_p)==null) s=link(v);
+ /*nodes may be discardable after the break*/
+}
+
+@ Replacement texts and discretionary texts are supposed to contain
+only character nodes, kern nodes, ligature nodes, and box or rule nodes.
+
+@<Subtract the width of node |v|...@>=
+if (is_char_node(v))
+ {@+f=font(v);
+ break_width[1]=break_width[1]-char_width(f, char_info(f, character(v)));
+ }
+else switch (type(v)) {
+ case ligature_node: {@+f=font(lig_char(v));@/
+ break_width[1]=@|break_width[1]-
+ char_width(f, char_info(f, character(lig_char(v))));
+ } @+break;
+ case hlist_node: case vlist_node: case rule_node:
+ case kern_node:
+ break_width[1]=break_width[1]-width(v);@+break;
+ default:confusion("disc1");
+@:this can't happen disc1}{\quad disc1@>
+ }
+
+@ @<Add the width of node |s| to |b...@>=
+if (is_char_node(s))
+ {@+f=font(s);
+ break_width[1]=@|break_width[1]+char_width(f, char_info(f, character(s)));
+ }
+else switch (type(s)) {
+ case ligature_node: {@+f=font(lig_char(s));
+ break_width[1]=break_width[1]+
+ char_width(f, char_info(f, character(lig_char(s))));
+ } @+break;
+ case hlist_node: case vlist_node: case rule_node:
+ case kern_node:
+ break_width[1]=break_width[1]+width(s);@+break;
+ default:confusion("disc2");
+@:this can't happen disc2}{\quad disc2@>
+ }
+
+@ We use the fact that |type(active)!=delta_node|.
+
+@d convert_to_break_width(A) @|
+ mem[prev_r+A].sc=@|@t\hskip10pt@>mem[prev_r+A].sc
+ -cur_active_width[A]+break_width[A]
+@d store_break_width(A) active_width[A]=break_width[A]
+@d new_delta_to_break_width(A) @|
+ mem[q+A].sc=break_width[A]-cur_active_width[A]
+
+@<Insert a delta node to prepare for breaks at |cur_p|@>=
+if (type(prev_r)==delta_node) /*modify an existing delta node*/
+ {@+do_all_six(convert_to_break_width);
+ }
+else if (prev_r==active) /*no delta node needed at the beginning*/
+ {@+do_all_six(store_break_width);
+ }
+else{@+q=get_node(delta_node_size);link(q)=r;type(q)=delta_node;@/
+ subtype(q)=0; /*the |subtype| is not used*/
+ do_all_six(new_delta_to_break_width);
+ link(prev_r)=q;prev_prev_r=prev_r;prev_r=q;
+ }
+
+@ When the following code is performed, we will have just inserted at
+least one active node before |r|, so |type(prev_r)!=delta_node|.
+
+@d new_delta_from_break_width(A) @|mem[q+A].sc=
+ cur_active_width[A]-break_width[A]
+
+@<Insert a delta node to prepare for the next active node@>=
+if (r!=last_active)
+ {@+q=get_node(delta_node_size);link(q)=r;type(q)=delta_node;@/
+ subtype(q)=0; /*the |subtype| is not used*/
+ do_all_six(new_delta_from_break_width);
+ link(prev_r)=q;prev_prev_r=prev_r;prev_r=q;
+ }
+
+@ When we create an active node, we also create the corresponding
+passive node.
+
+@<Insert a new active node from |best_place[fit_class]| to |cur_p|@>=
+{@+q=get_node(passive_node_size);
+link(q)=passive;passive=q;cur_break(q)=cur_p;
+#ifdef @!STAT
+incr(pass_number);serial(q)=pass_number;
+#endif
+@;@/
+prev_break(q)=best_place[fit_class];@/
+q=get_node(active_node_size);break_node(q)=passive;
+line_number(q)=best_pl_line[fit_class]+1;
+fitness(q)=fit_class;type(q)=break_type;
+total_demerits(q)=minimal_demerits[fit_class];
+link(q)=r;link(prev_r)=q;prev_r=q;
+#ifdef @!STAT
+if (tracing_paragraphs > 0)
+ @<Print a symbolic description of the new break node@>;
+#endif
+@;@/
+}
+
+@ @<Print a symbolic description of the new break node@>=
+{@+print_nl("@@@@");print_int(serial(passive));
+@.\AT\AT@>
+print(": line ");print_int(line_number(q)-1);
+print_char('.');print_int(fit_class);
+if (break_type==hyphenated) print_char('-');
+print(" t=");print_int(total_demerits(q));
+print(" -> @@@@");
+if (prev_break(passive)==null) print_char('0');
+else print_int(serial(prev_break(passive)));
+}
+
+@ The length of lines depends on whether the user has specified
+\.{\\parshape} or \.{\\hangindent}. If |par_shape_ptr| is not null, it
+points to a $(2n+1)$-word record in |mem|, where the |info| in the first
+word contains the value of |n|, and the other $2n$ words contain the left
+margins and line lengths for the first |n| lines of the paragraph; the
+specifications for line |n| apply to all subsequent lines. If
+|par_shape_ptr==null|, the shape of the paragraph depends on the value of
+|n==hang_after|; if |n >= 0|, hanging indentation takes place on lines |n+1|,
+|n+2|, \dots, otherwise it takes place on lines 1, \dots, $\vert
+n\vert$. When hanging indentation is active, the left margin is
+|hang_indent|, if |hang_indent >= 0|, else it is 0; the line length is
+$|hsize|-\vert|hang_indent|\vert$. The normal setting is
+|par_shape_ptr==null|, |hang_after==1|, and |hang_indent==0|.
+Note that if |hang_indent==0|, the value of |hang_after| is irrelevant.
+@^length of lines@> @^hanging indentation@>
+
+@<Glob...@>=
+static halfword @!easy_line; /*line numbers | > easy_line| are equivalent in break nodes*/
+static halfword @!last_special_line; /*line numbers | > last_special_line| all have
+ the same width*/
+static scaled @!first_width; /*the width of all lines | <= last_special_line|, if
+ no \.{\\parshape} has been specified*/
+static scaled @!second_width; /*the width of all lines | > last_special_line|*/
+static scaled @!first_indent; /*left margin to go with |first_width|*/
+static scaled @!second_indent; /*left margin to go with |second_width|*/
+
+@ We compute the values of |easy_line| and the other local variables relating
+to line length when the |line_break| procedure is initializing itself.
+
+@<Get ready to start...@>=
+if (par_shape_ptr==null)
+ if (hang_indent==0)
+ {@+last_special_line=0;second_width=hsize;
+ second_indent=0;
+ }
+ else@<Set line length parameters in preparation for hanging indentation@>@;
+else{@+last_special_line=info(par_shape_ptr)-1;
+ second_width=mem[par_shape_ptr+2*(last_special_line+1)].sc;
+ second_indent=mem[par_shape_ptr+2*last_special_line+1].sc;
+ }
+if (looseness==0) easy_line=last_special_line;
+else easy_line=max_halfword
+
+@ @<Set line length parameters in preparation for hanging indentation@>=
+{@+last_special_line=abs(hang_after);
+if (hang_after < 0)
+ {@+first_width=hsize-abs(hang_indent);
+ if (hang_indent >= 0) first_indent=hang_indent;
+ else first_indent=0;
+ second_width=hsize;second_indent=0;
+ }
+else{@+first_width=hsize;first_indent=0;
+ second_width=hsize-abs(hang_indent);
+ if (hang_indent >= 0) second_indent=hang_indent;
+ else second_indent=0;
+ }
+}
+
+@ When we come to the following code, we have just encountered the first
+active node~|r| whose |line_number| field contains |l|. Thus we want to
+compute the length of the $l\mskip1mu$th line of the current paragraph. Furthermore,
+we want to set |old_l| to the last number in the class of line numbers
+equivalent to~|l|.
+
+@<Compute the new line width@>=
+if (l > easy_line)
+ {@+line_width=second_width;old_l=max_halfword-1;
+ }
+else{@+old_l=l;
+ if (l > last_special_line) line_width=second_width;
+ else if (par_shape_ptr==null) line_width=first_width;
+ else line_width=mem[par_shape_ptr+2*l@,].sc;
+ }
+
+@ The remaining part of |try_break| deals with the calculation of
+demerits for a break from |r| to |cur_p|.
+
+The first thing to do is calculate the badness, |b|. This value will always
+be between zero and |inf_bad+1|; the latter value occurs only in the
+case of lines from |r| to |cur_p| that cannot shrink enough to fit the necessary
+width. In such cases, node |r| will be deactivated.
+We also deactivate node~|r| when a break at~|cur_p| is forced, since future
+breaks must go through a forced break.
+
+@<Consider the demerits for a line from |r| to |cur_p|...@>=
+{@+artificial_demerits=false;@/
+@^inner loop@>
+shortfall=line_width-cur_active_width[1]; /*we're this much too short*/
+if (shortfall > 0)
+ @<Set the value of |b| to the badness for stretching the line, and compute
+the corresponding |fit_class|@>@;
+else@<Set the value of |b| to the badness for shrinking the line, and compute
+the corresponding |fit_class|@>;
+if ((b > inf_bad)||(pi==eject_penalty))
+ @<Prepare to deactivate node~|r|, and |goto deactivate| unless there is
+a reason to consider lines of text from |r| to |cur_p|@>@;
+else{@+prev_r=r;
+ if (b > threshold) goto resume;
+ node_r_stays_active=true;
+ }
+@<Record a new feasible break@>;
+if (node_r_stays_active) goto resume; /*|prev_r| has been set to |r|*/
+deactivate: @<Deactivate node |r|@>;
+}
+
+@ When a line must stretch, the available stretchability can be found in the
+subarray |cur_active_width[2 dotdot 5]|, in units of points, fil, fill, and filll.
+
+The present section is part of \TeX's inner loop, and it is most often performed
+when the badness is infinite; therefore it is worth while to make a quick
+test for large width excess and small stretchability, before calling the
+|badness| subroutine.
+@^inner loop@>
+
+@<Set the value of |b| to the badness for stretching...@>=
+if ((cur_active_width[3]!=0)||(cur_active_width[4]!=0)||@|
+ (cur_active_width[5]!=0))
+ {@+b=0;fit_class=decent_fit; /*infinite stretch*/
+ }
+else{@+if (shortfall > 7230584) if (cur_active_width[2] < 1663497)
+ {@+b=inf_bad;fit_class=very_loose_fit;goto done1;
+ }
+ b=badness(shortfall, cur_active_width[2]);
+ if (b > 12)
+ if (b > 99) fit_class=very_loose_fit;
+ else fit_class=loose_fit;
+ else fit_class=decent_fit;
+ done1: ;
+ }
+
+@ Shrinkability is never infinite in a paragraph;
+we can shrink the line from |r| to |cur_p| by at most |cur_active_width[6]|.
+
+@<Set the value of |b| to the badness for shrinking...@>=
+{@+if (-shortfall > cur_active_width[6]) b=inf_bad+1;
+else b=badness(-shortfall, cur_active_width[6]);
+if (b > 12) fit_class=tight_fit;@+else fit_class=decent_fit;
+}
+
+@ During the final pass, we dare not lose all active nodes, lest we lose
+touch with the line breaks already found. The code shown here makes sure
+that such a catastrophe does not happen, by permitting overfull boxes as
+a last resort. This particular part of \TeX\ was a source of several subtle
+bugs before the correct program logic was finally discovered; readers
+who seek to ``improve'' \TeX\ should therefore think thrice before daring
+to make any changes here.
+@^overfull boxes@>
+
+@<Prepare to deactivate node~|r|, and |goto deactivate| unless...@>=
+{@+if (final_pass&&(minimum_demerits==awful_bad)&&@|
+ (link(r)==last_active)&&
+ (prev_r==active))
+ artificial_demerits=true; /*set demerits zero, this break is forced*/
+else if (b > threshold) goto deactivate;
+node_r_stays_active=false;
+}
+
+@ When we get to this part of the code, the line from |r| to |cur_p| is
+feasible, its badness is~|b|, and its fitness classification is |fit_class|.
+We don't want to make an active node for this break yet, but we will
+compute the total demerits and record them in the |minimal_demerits| array,
+if such a break is the current champion among all ways to get to |cur_p|
+in a given line-number class and fitness class.
+
+@<Record a new feasible break@>=
+if (artificial_demerits) d=0;
+else@<Compute the demerits, |d|, from |r| to |cur_p|@>;
+#ifdef @!STAT
+if (tracing_paragraphs > 0)
+ @<Print a symbolic description of this feasible break@>;
+#endif
+@;@/
+d=d+total_demerits(r); /*this is the minimum total demerits
+ from the beginning to |cur_p| via |r|*/
+if (d <= minimal_demerits[fit_class])
+ {@+minimal_demerits[fit_class]=d;
+ best_place[fit_class]=break_node(r);best_pl_line[fit_class]=l;
+ if (d < minimum_demerits) minimum_demerits=d;
+ }
+
+@ @<Print a symbolic description of this feasible break@>=
+{@+if (printed_node!=cur_p)
+ @<Print the list between |printed_node| and |cur_p|, then set |printed_node:=cur_p|@>;
+print_nl("@@");
+@.\AT@>
+if (cur_p==null) print_esc("par");
+else if (type(cur_p)!=glue_node)
+ {@+if (type(cur_p)==penalty_node) print_esc("penalty");
+ else if (type(cur_p)==disc_node) print_esc("discretionary");
+ else if (type(cur_p)==kern_node) print_esc("kern");
+ else print_esc("math");
+ }
+print(" via @@@@");
+if (break_node(r)==null) print_char('0');
+else print_int(serial(break_node(r)));
+print(" b=");
+if (b > inf_bad) print_char('*');@+else print_int(b);
+@.*\relax@>
+print(" p=");print_int(pi);print(" d=");
+if (artificial_demerits) print_char('*');@+else print_int(d);
+}
+
+@ @<Print the list between |printed_node| and |cur_p|...@>=
+{@+print_nl("");
+if (cur_p==null) short_display(link(printed_node));
+else{@+save_link=link(cur_p);
+ link(cur_p)=null;print_nl("");short_display(link(printed_node));
+ link(cur_p)=save_link;
+ }
+printed_node=cur_p;
+}
+
+@ When the data for a discretionary break is being displayed, we will have
+printed the |pre_break| and |post_break| lists; we want to skip over the
+third list, so that the discretionary data will not appear twice. The
+following code is performed at the very end of |try_break|.
+
+@<Update the value of |printed_node|...@>=
+if (cur_p==printed_node) if (cur_p!=null) if (type(cur_p)==disc_node)
+ {@+t=replace_count(cur_p);
+ while (t > 0)
+ {@+decr(t);printed_node=link(printed_node);
+ }
+ }
+
+@ @<Compute the demerits, |d|, from |r| to |cur_p|@>=
+{@+d=line_penalty+b;
+if (abs(d) >= 10000) d=100000000;@+else d=d*d;
+if (pi!=0)
+ if (pi > 0) d=d+pi*pi;
+ else if (pi > eject_penalty) d=d-pi*pi;
+if ((break_type==hyphenated)&&(type(r)==hyphenated))
+ if (cur_p!=null) d=d+double_hyphen_demerits;
+ else d=d+final_hyphen_demerits;
+if (abs(fit_class-fitness(r)) > 1) d=d+adj_demerits;
+}
+
+@ When an active node disappears, we must delete an adjacent delta node if the
+active node was at the beginning or the end of the active list, or if it
+was surrounded by delta nodes. We also must preserve the property that
+|cur_active_width| represents the length of material from |link(prev_r)|
+to~|cur_p|.
+
+@d combine_two_deltas(A) @|mem[prev_r+A].sc=mem[prev_r+A].sc+mem[r+A].sc
+@d downdate_width(A) @|cur_active_width[A]=cur_active_width[A]-
+ mem[prev_r+A].sc
+
+@<Deactivate node |r|@>=
+link(prev_r)=link(r);free_node(r, active_node_size);
+if (prev_r==active) @<Update the active widths, since the first active node
+has been deleted@>@;
+else if (type(prev_r)==delta_node)
+ {@+r=link(prev_r);
+ if (r==last_active)
+ {@+do_all_six(downdate_width);
+ link(prev_prev_r)=last_active;
+ free_node(prev_r, delta_node_size);prev_r=prev_prev_r;
+ }
+ else if (type(r)==delta_node)
+ {@+do_all_six(update_width);
+ do_all_six(combine_two_deltas);
+ link(prev_r)=link(r);free_node(r, delta_node_size);
+ }
+ }
+
+@ The following code uses the fact that |type(last_active)!=delta_node|. If the
+active list has just become empty, we do not need to update the
+|active_width| array, since it will be initialized when an active
+node is next inserted.
+
+@d update_active(A) active_width[A]=active_width[A]+mem[r+A].sc
+
+@<Update the active widths,...@>=
+{@+r=link(active);
+if (type(r)==delta_node)
+ {@+do_all_six(update_active);
+ do_all_six(copy_to_cur_active);
+ link(active)=link(r);free_node(r, delta_node_size);
+ }
+}
+
+@* Breaking paragraphs into lines, continued.
+So far we have gotten a little way into the |line_break| routine, having
+covered its important |try_break| subroutine. Now let's consider the
+rest of the process.
+
+The main loop of |line_break| traverses the given hlist,
+starting at |link(temp_head)|, and calls |try_break| at each legal
+breakpoint. A variable called |auto_breaking| is set to true except
+within math formulas, since glue nodes are not legal breakpoints when
+they appear in formulas.
+
+The current node of interest in the hlist is pointed to by |cur_p|. Another
+variable, |prev_p|, is usually one step behind |cur_p|, but the real
+meaning of |prev_p| is this: If |type(cur_p)==glue_node| then |cur_p| is a legal
+breakpoint if and only if |auto_breaking| is true and |prev_p| does not
+point to a glue node, penalty node, explicit kern node, or math node.
+
+The following declarations provide for a few other local variables that are
+used in special calculations.
+
+@<Local variables for line breaking@>=
+bool @!auto_breaking; /*is node |cur_p| outside a formula?*/
+pointer @!prev_p; /*helps to determine when glue nodes are breakpoints*/
+pointer @!q, @!r, @!s, @!prev_s; /*miscellaneous nodes of temporary interest*/
+internal_font_number @!f; /*used when calculating character widths*/
+
+@ The `\ignorespaces|loop|\unskip' in the following code is performed at most
+thrice per call of |line_break|, since it is actually a pass over the
+entire paragraph.
+
+@<Find optimal breakpoints@>=
+threshold=pretolerance;
+if (threshold >= 0)
+ {
+#ifdef @!STAT
+if (tracing_paragraphs > 0)
+ {@+begin_diagnostic();print_nl("@@firstpass");@+} @;
+#endif
+@;@/
+ second_pass=false;final_pass=false;
+ }
+else{@+threshold=tolerance;second_pass=true;
+ final_pass=(emergency_stretch <= 0);
+#ifdef @!STAT
+ if (tracing_paragraphs > 0) begin_diagnostic();
+#endif
+@;
+ }
+loop@+{@+if (threshold > inf_bad) threshold=inf_bad;
+ if (second_pass) @<Initialize for hyphenating a paragraph@>;
+ @<Create an active breakpoint representing the beginning of the paragraph@>;
+ cur_p=link(temp_head);auto_breaking=true;@/
+ prev_p=cur_p; /*glue at beginning is not a legal breakpoint*/
+ while ((cur_p!=null)&&(link(active)!=last_active))
+ @<Call |try_break| if |cur_p| is a legal breakpoint; on the second pass,
+also try to hyphenate the next word, if |cur_p| is a glue node; then advance
+|cur_p| to the next node of the paragraph that could possibly be a legal breakpoint@>;
+ if (cur_p==null)
+ @<Try the final line break at the end of the paragraph, and |goto done|
+if the desired breakpoints have been found@>;
+ @<Clean up the memory by removing the break nodes@>;
+ if (!second_pass)
+ {
+#ifdef @!STAT
+if (tracing_paragraphs > 0) print_nl("@@secondpass");@;
+#endif
+ threshold=tolerance;second_pass=true;final_pass=(emergency_stretch <= 0);
+ } /*if at first you don't succeed, \dots*/
+ else{
+#ifdef @!STAT
+if (tracing_paragraphs > 0)
+ print_nl("@@emergencypass");@;
+#endif
+ background[2]=background[2]+emergency_stretch;final_pass=true;
+ }
+ }
+done:
+#ifdef @!STAT
+if (tracing_paragraphs > 0)
+ {@+end_diagnostic(true);normalize_selector();
+ }
+#endif
+
+@ The active node that represents the starting point does not need a
+corresponding passive node.
+
+@d store_background(A) active_width[A]=background[A]
+
+@<Create an active breakpoint representing the beginning of the paragraph@>=
+q=get_node(active_node_size);
+type(q)=unhyphenated;fitness(q)=decent_fit;
+link(q)=last_active;break_node(q)=null;
+line_number(q)=prev_graf+1;total_demerits(q)=0;link(active)=q;
+do_all_six(store_background);@/
+passive=null;printed_node=temp_head;pass_number=0;
+font_in_short_display=null_font
+
+@ @<Clean...@>=
+q=link(active);
+while (q!=last_active)
+ {@+cur_p=link(q);
+ if (type(q)==delta_node) free_node(q, delta_node_size);
+ else free_node(q, active_node_size);
+ q=cur_p;
+ }
+q=passive;
+while (q!=null)
+ {@+cur_p=link(q);
+ free_node(q, passive_node_size);
+ q=cur_p;
+ }
+
+@ Here is the main switch in the |line_break| routine, where legal breaks
+are determined. As we move through the hlist, we need to keep the |active_width|
+array up to date, so that the badness of individual lines is readily calculated
+by |try_break|. It is convenient to use the short name |act_width| for
+the component of active width that represents real width as opposed to glue.
+
+@d act_width active_width[1] /*length from first active node to current node*/
+@d kern_break {@+if (!is_char_node(link(cur_p))&&auto_breaking)
+ if (type(link(cur_p))==glue_node) try_break(0, unhyphenated);
+ act_width=act_width+width(cur_p);
+ }
+
+@<Call |try_break| if |cur_p| is a legal breakpoint...@>=
+{@+if (is_char_node(cur_p))
+ @<Advance \(c)|cur_p| to the node following the present string of characters@>;
+switch (type(cur_p)) {
+case hlist_node: case vlist_node: case rule_node: act_width=act_width+width(cur_p);@+break;
+case whatsit_node: @<Advance \(p)past a whatsit node in the \(l)|line_break|
+loop@>@;@+break;
+case glue_node: {@+@<If node |cur_p| is a legal breakpoint, call |try_break|;
+then update the active widths by including the glue in |glue_ptr(cur_p)|@>;
+ if (second_pass&&auto_breaking)
+ @<Try to hyphenate the following word@>;
+ } @+break;
+case kern_node: if (subtype(cur_p)==explicit) kern_break@;
+ else act_width=act_width+width(cur_p);@+break;
+case ligature_node: {@+f=font(lig_char(cur_p));
+ act_width=act_width+char_width(f, char_info(f, character(lig_char(cur_p))));
+ } @+break;
+case disc_node: @<Try to break after a discretionary fragment, then |goto
+done5|@>@;
+case math_node: {@+auto_breaking=(subtype(cur_p)==after);kern_break;
+ } @+break;
+case penalty_node: try_break(penalty(cur_p), unhyphenated);@+break;
+case mark_node: case ins_node: case adjust_node: do_nothing;@+break;
+default:confusion("paragraph");
+@:this can't happen paragraph}{\quad paragraph@>
+} @/
+prev_p=cur_p;cur_p=link(cur_p);
+done5: ;}
+
+@ The code that passes over the characters of words in a paragraph is
+part of \TeX's inner loop, so it has been streamlined for speed. We use
+the fact that `\.{\\parfillskip}' glue appears at the end of each paragraph;
+it is therefore unnecessary to check if |link(cur_p)==null| when |cur_p| is a
+character node.
+@^inner loop@>
+
+@<Advance \(c)|cur_p| to the node following the present string...@>=
+{@+prev_p=cur_p;
+@/do@+{f=font(cur_p);
+act_width=act_width+char_width(f, char_info(f, character(cur_p)));
+cur_p=link(cur_p);
+}@+ while (!(!is_char_node(cur_p)));
+}
+
+@ When node |cur_p| is a glue node, we look at |prev_p| to see whether or not
+a breakpoint is legal at |cur_p|, as explained above.
+
+@<If node |cur_p| is a legal breakpoint, call...@>=
+if (auto_breaking)
+ {@+if (is_char_node(prev_p)) try_break(0, unhyphenated);
+ else if (precedes_break(prev_p)) try_break(0, unhyphenated);
+ else if ((type(prev_p)==kern_node)&&(subtype(prev_p)!=explicit))
+ try_break(0, unhyphenated);
+ }
+check_shrinkage(glue_ptr(cur_p));q=glue_ptr(cur_p);
+act_width=act_width+width(q);@|
+active_width[2+stretch_order(q)]=@|
+ active_width[2+stretch_order(q)]+stretch(q);@/
+active_width[6]=active_width[6]+shrink(q)
+
+@ The following code knows that discretionary texts contain
+only character nodes, kern nodes, box nodes, rule nodes, and ligature nodes.
+
+@<Try to break after a discretionary fragment...@>=
+{@+s=pre_break(cur_p);disc_width=0;
+if (s==null) try_break(ex_hyphen_penalty, hyphenated);
+else{@+@/do@+{@<Add the width of node |s| to |disc_width|@>;
+ s=link(s);
+ }@+ while (!(s==null));
+ act_width=act_width+disc_width;
+ try_break(hyphen_penalty, hyphenated);
+ act_width=act_width-disc_width;
+ }
+r=replace_count(cur_p);s=link(cur_p);
+while (r > 0)
+ {@+@<Add the width of node |s| to |act_width|@>;
+ decr(r);s=link(s);
+ }
+prev_p=cur_p;cur_p=s;goto done5;
+}
+
+@ @<Add the width of node |s| to |disc_width|@>=
+if (is_char_node(s))
+ {@+f=font(s);
+ disc_width=disc_width+char_width(f, char_info(f, character(s)));
+ }
+else switch (type(s)) {
+ case ligature_node: {@+f=font(lig_char(s));
+ disc_width=disc_width+
+ char_width(f, char_info(f, character(lig_char(s))));
+ } @+break;
+ case hlist_node: case vlist_node: case rule_node:
+ case kern_node:
+ disc_width=disc_width+width(s);@+break;
+ default:confusion("disc3");
+@:this can't happen disc3}{\quad disc3@>
+ }
+
+@ @<Add the width of node |s| to |act_width|@>=
+if (is_char_node(s))
+ {@+f=font(s);
+ act_width=act_width+char_width(f, char_info(f, character(s)));
+ }
+else switch (type(s)) {
+ case ligature_node: {@+f=font(lig_char(s));
+ act_width=act_width+
+ char_width(f, char_info(f, character(lig_char(s))));
+ } @+break;
+ case hlist_node: case vlist_node: case rule_node:
+ case kern_node:
+ act_width=act_width+width(s);@+break;
+ default:confusion("disc4");
+@:this can't happen disc4}{\quad disc4@>
+ }
+
+@ The forced line break at the paragraph's end will reduce the list of
+breakpoints so that all active nodes represent breaks at |cur_p==null|.
+On the first pass, we insist on finding an active node that has the
+correct ``looseness.'' On the final pass, there will be at least one active
+node, and we will match the desired looseness as well as we can.
+
+The global variable |best_bet| will be set to the active node for the best
+way to break the paragraph, and a few other variables are used to
+help determine what is best.
+
+@<Glob...@>=
+static pointer @!best_bet; /*use this passive node and its predecessors*/
+static int @!fewest_demerits; /*the demerits associated with |best_bet|*/
+static halfword @!best_line; /*line number following the last line of the new paragraph*/
+static int @!actual_looseness; /*the difference between |line_number(best_bet)|
+ and the optimum |best_line|*/
+static int @!line_diff; /*the difference between the current line number and
+ the optimum |best_line|*/
+
+@ @<Try the final line break at the end of the paragraph...@>=
+{@+try_break(eject_penalty, hyphenated);
+if (link(active)!=last_active)
+ {@+@<Find an active node with fewest demerits@>;
+ if (looseness==0) goto done;
+ @<Find the best active node for the desired looseness@>;
+ if ((actual_looseness==looseness)||final_pass) goto done;
+ }
+}
+
+@ @<Find an active node...@>=
+r=link(active);fewest_demerits=awful_bad;
+@/do@+{if (type(r)!=delta_node) if (total_demerits(r) < fewest_demerits)
+ {@+fewest_demerits=total_demerits(r);best_bet=r;
+ }
+r=link(r);
+}@+ while (!(r==last_active));
+best_line=line_number(best_bet)
+
+@ The adjustment for a desired looseness is a slightly more complicated
+version of the loop just considered. Note that if a paragraph is broken
+into segments by displayed equations, each segment will be subject to the
+looseness calculation, independently of the other segments.
+
+@<Find the best active node...@>=
+{@+r=link(active);actual_looseness=0;
+@/do@+{if (type(r)!=delta_node)
+ {@+line_diff=line_number(r)-best_line;
+ if (((line_diff < actual_looseness)&&(looseness <= line_diff))||@|
+ ((line_diff > actual_looseness)&&(looseness >= line_diff)))
+ {@+best_bet=r;actual_looseness=line_diff;
+ fewest_demerits=total_demerits(r);
+ }
+ else if ((line_diff==actual_looseness)&&@|
+ (total_demerits(r) < fewest_demerits))
+ {@+best_bet=r;fewest_demerits=total_demerits(r);
+ }
+ }
+r=link(r);
+}@+ while (!(r==last_active));
+best_line=line_number(best_bet);
+}
+
+@ Once the best sequence of breakpoints has been found (hurray), we call on the
+procedure |post_line_break| to finish the remainder of the work.
+(By introducing this subprocedure, we are able to keep |line_break|
+from getting extremely long.)
+
+@<Break the paragraph at the chosen...@>=
+post_line_break(final_widow_penalty)
+
+@ The total number of lines that will be set by |post_line_break|
+is |best_line-prev_graf-1|. The last breakpoint is specified by
+|break_node(best_bet)|, and this passive node points to the other breakpoints
+via the |prev_break| links. The finishing-up phase starts by linking the
+relevant passive nodes in forward order, changing |prev_break| to
+|next_break|. (The |next_break| fields actually reside in the same memory
+space as the |prev_break| fields did, but we give them a new name because
+of their new significance.) Then the lines are justified, one by one.
+
+@d next_break prev_break /*new name for |prev_break| after links are reversed*/
+
+@<Declare subprocedures for |line_break|@>=
+static void post_line_break(int @!final_widow_penalty)
+{@+
+pointer q, @!r, @!s; /*temporary registers for list manipulation*/
+bool @!disc_break; /*was the current break at a discretionary node?*/
+bool @!post_disc_break; /*and did it have a nonempty post-break part?*/
+scaled @!cur_width; /*width of line number |cur_line|*/
+scaled @!cur_indent; /*left margin of line number |cur_line|*/
+quarterword @!t; /*used for replacement counts in discretionary nodes*/
+int @!pen; /*use when calculating penalties between lines*/
+halfword @!cur_line; /*the current line number being justified*/
+@<Reverse the links of the relevant passive nodes, setting |cur_p| to the
+first breakpoint@>;
+cur_line=prev_graf+1;
+@/do@+{@<Justify the line ending at breakpoint |cur_p|, and append it to the
+current vertical list, together with associated penalties and other insertions@>;
+incr(cur_line);cur_p=next_break(cur_p);
+if (cur_p!=null) if (!post_disc_break)
+ @<Prune unwanted nodes at the beginning of the next line@>;
+}@+ while (!(cur_p==null));
+if ((cur_line!=best_line)||(link(temp_head)!=null))
+ confusion("line breaking");
+@:this can't happen line breaking}{\quad line breaking@>
+prev_graf=best_line-1;
+}
+
+@ The job of reversing links in a list is conveniently regarded as the job
+of taking items off one stack and putting them on another. In this case we
+take them off a stack pointed to by |q| and having |prev_break| fields;
+we put them on a stack pointed to by |cur_p| and having |next_break| fields.
+Node |r| is the passive node being moved from stack to stack.
+
+@<Reverse the links of the relevant passive nodes...@>=
+q=break_node(best_bet);cur_p=null;
+@/do@+{r=q;q=prev_break(q);next_break(r)=cur_p;cur_p=r;
+}@+ while (!(q==null))
+
+@ Glue and penalty and kern and math nodes are deleted at the beginning of
+a line, except in the anomalous case that the node to be deleted is actually
+one of the chosen breakpoints. Otherwise
+the pruning done here is designed to match
+the lookahead computation in |try_break|, where the |break_width| values
+are computed for non-discretionary breakpoints.
+
+@<Prune unwanted nodes at the beginning of the next line@>=
+{@+r=temp_head;
+loop@+{@+q=link(r);
+ if (q==cur_break(cur_p)) goto done1;
+ /*|cur_break(cur_p)| is the next breakpoint*/
+ /*now |q| cannot be |null|*/
+ if (is_char_node(q)) goto done1;
+ if (non_discardable(q)) goto done1;
+ if (type(q)==kern_node) if (subtype(q)!=explicit) goto done1;
+ r=q; /*now |type(q)==glue_node|, |kern_node|, |math_node|, or |penalty_node|*/
+ }
+done1: if (r!=temp_head)
+ {@+link(r)=null;flush_node_list(link(temp_head));
+ link(temp_head)=q;
+ }
+}
+
+@ The current line to be justified appears in a horizontal list starting
+at |link(temp_head)| and ending at |cur_break(cur_p)|. If |cur_break(cur_p)| is
+a glue node, we reset the glue to equal the |right_skip| glue; otherwise
+we append the |right_skip| glue at the right. If |cur_break(cur_p)| is a
+discretionary node, we modify the list so that the discretionary break
+is compulsory, and we set |disc_break| to |true|. We also append
+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...@>=
+@<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|@>;
+@<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
+special nodes taken out of the box by the packager@>;
+@<Append a penalty node, if a nonzero penalty is appropriate@>@;
+
+@ At the end of the following code, |q| will point to the final node on the
+list about to be justified.
+
+@<Modify the end of the line...@>=
+q=cur_break(cur_p);disc_break=false;post_disc_break=false;
+if (q!=null) /*|q| cannot be a |char_node|*/
+ if (type(q)==glue_node)
+ {@+delete_glue_ref(glue_ptr(q));
+ glue_ptr(q)=right_skip;
+ subtype(q)=right_skip_code+1;add_glue_ref(right_skip);
+ goto done;
+ }
+ else{@+if (type(q)==disc_node)
+ @<Change discretionary to compulsory and set |disc_break:=true|@>@;
+ else if ((type(q)==math_node)||(type(q)==kern_node)) width(q)=0;
+ }
+else{@+q=temp_head;
+ while (link(q)!=null) q=link(q);
+ }
+@<Put the \(r)\.{\\rightskip} glue after node |q|@>;
+done:
+
+@ @<Change discretionary to compulsory...@>=
+{@+t=replace_count(q);
+@<Destroy the |t| nodes following |q|, and make |r| point to the following
+node@>;
+if (post_break(q)!=null) @<Transplant the post-break list@>;
+if (pre_break(q)!=null) @<Transplant the pre-break list@>;
+link(q)=r;disc_break=true;
+}
+
+@ @<Destroy the |t| nodes following |q|...@>=
+if (t==0) r=link(q);
+else{@+r=q;
+ while (t > 1)
+ {@+r=link(r);decr(t);
+ }
+ s=link(r);
+ r=link(s);link(s)=null;
+ flush_node_list(link(q));replace_count(q)=0;
+ }
+
+@ We move the post-break list from inside node |q| to the main list by
+re\-attaching it just before the present node |r|, then resetting |r|.
+
+@<Transplant the post-break list@>=
+{@+s=post_break(q);
+while (link(s)!=null) s=link(s);
+link(s)=r;r=post_break(q);post_break(q)=null;post_disc_break=true;
+}
+
+@ We move the pre-break list from inside node |q| to the main list by
+re\-attaching it just after the present node |q|, then resetting |q|.
+
+@<Transplant the pre-break list@>=
+{@+s=pre_break(q);link(q)=s;
+while (link(s)!=null) s=link(s);
+pre_break(q)=null;q=s;
+}
+
+@ @<Put the \(r)\.{\\rightskip} glue after node |q|@>=
+r=new_param_glue(right_skip_code);link(r)=link(q);link(q)=r;q=r
+
+@ The following code begins with |q| at the end of the list to be
+justified. It ends with |q| at the beginning of that list, and with
+|link(temp_head)| pointing to the remainder of the paragraph, if any.
+
+@<Put the \(l)\.{\\leftskip} glue at the left...@>=
+r=link(q);link(q)=null;q=link(temp_head);link(temp_head)=r;
+if (left_skip!=zero_glue)
+ {@+r=new_param_glue(left_skip_code);
+ link(r)=q;q=r;
+ }
+
+@ @<Append the new box to the current vertical list...@>=
+append_to_vlist(just_box);
+if (adjust_head!=adjust_tail)
+ {@+link(tail)=link(adjust_head);tail=adjust_tail;
+ }
+adjust_tail=null
+
+@ Now |q| points to the hlist that represents the current line of the
+paragraph. We need to compute the appropriate line width, pack the
+line into a box of this size, and shift the box by the appropriate
+amount of indentation.
+
+@<Call the packaging subroutine...@>=
+if (cur_line > last_special_line)
+ {@+cur_width=second_width;cur_indent=second_indent;
+ }
+else if (par_shape_ptr==null)
+ {@+cur_width=first_width;cur_indent=first_indent;
+ }
+else{@+cur_width=mem[par_shape_ptr+2*cur_line].sc;
+ cur_indent=mem[par_shape_ptr+2*cur_line-1].sc;
+ }
+adjust_tail=adjust_head;just_box=hpack(q, cur_width, exactly);
+shift_amount(just_box)=cur_indent
+
+@ Penalties between the lines of a paragraph come from club and widow lines,
+from the |inter_line_penalty| parameter, and from lines that end at
+discretionary breaks. Breaking between lines of a two-line paragraph gets
+both club-line and widow-line penalties. The local variable |pen| will
+be set to the sum of all relevant penalties for the current line, except
+that the final line is never penalized.
+
+@<Append a penalty node, if a nonzero penalty is appropriate@>=
+if (cur_line+1!=best_line)
+ {@+pen=inter_line_penalty;
+ if (cur_line==prev_graf+1) pen=pen+club_penalty;
+ if (cur_line+2==best_line) pen=pen+final_widow_penalty;
+ if (disc_break) pen=pen+broken_penalty;
+ if (pen!=0)
+ {@+r=new_penalty(pen);
+ link(tail)=r;tail=r;
+ }
+ }
+
+@* Pre-hyphenation.
+When the line-breaking routine is unable to find a feasible sequence of
+breakpoints, it makes a second pass over the paragraph, attempting to
+hyphenate the hyphenatable words. The goal of hyphenation is to insert
+discretionary material into the paragraph so that there are more
+potential places to break.
+
+The general rules for hyphenation are somewhat complex and technical,
+because we want to be able to hyphenate words that are preceded or
+followed by punctuation marks, and because we want the rules to work
+for languages other than English. We also must contend with the fact
+that hyphens might radically alter the ligature and kerning structure
+of a word.
+
+A sequence of characters will be considered for hyphenation only if it
+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 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
+expanded into the original non-ligature characters; the kern nodes and
+whatsits are ignored. Each character |c| is now classified as either a
+nonletter (if |lc_code(c)==0|), a lowercase letter (if
+|lc_code(c)==c|), or an uppercase letter (otherwise); an uppercase letter
+is treated as if it were |lc_code(c)| for purposes of hyphenation. The
+characters generated by $p_1\ldots p_{m-1}$ may begin with nonletters; let
+$c_1$ be the first letter that is not in the middle of a ligature. Whatsit
+nodes preceding $c_1$ are ignored; a whatsit found after $c_1$ will be the
+terminating node $p_m$. All characters that do not have the same font as
+$c_1$ will be treated as nonletters. The |hyphen_char| for that font
+must be between 0 and 255, otherwise hyphenation will not be attempted.
+\TeX\ looks ahead for as many consecutive letters $c_1\ldots c_n$ as
+possible; however, |n| must be less than 64, so a character that would
+otherwise be $c_{64}$ is effectively not a letter. Furthermore $c_n$ must
+not be in the middle of a ligature. In this way we obtain a string of
+letters $c_1\ldots c_n$ that are generated by nodes $p_a\ldots p_b$, where
+|1 <= a <= b+1 <= m|. If |n >= l_hyf+r_hyf|, this string qualifies for hyphenation;
+however, |uc_hyph| must be positive, if $c_1$ is uppercase.
+
+The hyphenation process takes place in three stages. First, the candidate
+sequence $c_1\ldots c_n$ is found; then potential positions for hyphens
+are determined by referring to hyphenation tables; and finally, the nodes
+$p_a\ldots p_b$ are replaced by a new sequence of nodes that includes the
+discretionary breaks found.
+
+Fortunately, we do not have to do all this calculation very often, because
+of the way it has been taken out of \TeX's inner loop. For example, when
+the second edition of the author's 700-page book {\sl Seminumerical
+Algorithms} was typeset by \TeX, only about 1.2 hyphenations needed to be
+@^Knuth, Donald Ervin@>
+tried per paragraph, since the line breaking algorithm needed to use two
+passes on only about 5 per cent of the paragraphs.
+
+@<Initialize for hyphenating...@>=
+{
+#ifdef @!INIT
+if (trie_not_ready) init_trie();
+#endif
+@;@/
+cur_lang=init_cur_lang;l_hyf=init_l_hyf;r_hyf=init_r_hyf;
+set_hyph_index;
+}
+
+@ The letters $c_1\ldots c_n$ that are candidates for hyphenation are placed
+into an array called |hc|; the number |n| is placed into |hn|; pointers to
+nodes $p_{a-1}$ and~$p_b$ in the description above are placed into variables
+|ha| and |hb|; and the font number is placed into |hf|.
+
+@<Glob...@>=
+static int16_t @!hc[66]; /*word to be hyphenated*/
+static int @!hn; /*the number of positions occupied in |hc|;
+ not always a |small_number|*/
+static pointer @!ha, @!hb; /*nodes |ha dotdot hb| should be replaced by the hyphenated result*/
+static internal_font_number @!hf; /*font number of the letters in |hc|*/
+static int16_t @!hu[64]; /*like |hc|, before conversion to lowercase*/
+static int @!hyf_char; /*hyphen character of the relevant font*/
+static ASCII_code @!cur_lang, @!init_cur_lang; /*current hyphenation table of interest*/
+static int @!l_hyf, @!r_hyf, @!init_l_hyf, @!init_r_hyf; /*limits on fragment sizes*/
+static halfword @!hyf_bchar; /*boundary character after $c_n$*/
+
+@ Hyphenation routines need a few more local variables.
+
+@<Local variables for line...@>=
+small_number @!j; /*an index into |hc| or |hu|*/
+int @!c; /*character being considered for hyphenation*/
+
+@ When the following code is activated, the |line_break| procedure is in its
+second pass, and |cur_p| points to a glue node.
+
+@<Try to hyphenate...@>=
+{@+prev_s=cur_p;s=link(prev_s);
+if (s!=null)
+ {@+@<Skip to node |ha|, or |goto done1| if no hyphenation should be attempted@>;
+ if (l_hyf+r_hyf > 63) goto done1;
+ @<Skip to node |hb|, putting letters into |hu| and |hc|@>;
+ @<Check that the nodes following |hb| permit hyphenation and that at least
+|l_hyf+r_hyf| letters have been found, otherwise |goto done1|@>;
+ hyphenate();
+ }
+done1: ;}
+
+@ @<Declare subprocedures for |line_break|@>=
+@t\4@>@<Declare the function called |reconstitute|@>@;
+static void hyphenate(void)
+{@+
+@<Local variables for hyphenation@>@;
+@<Find hyphen locations for the word in |hc|, or |return|@>;
+@<If no hyphens were found, |return|@>;
+@<Replace nodes |ha..hb| by a sequence of nodes that includes the discretionary
+hyphens@>;
+}
+
+@ The first thing we need to do is find the node |ha| just before the
+first letter.
+
+@<Skip to node |ha|, or |goto done1|...@>=
+loop@+{@+if (is_char_node(s))
+ {@+c=qo(character(s));hf=font(s);
+ }
+ else if (type(s)==ligature_node)
+ if (lig_ptr(s)==null) goto resume;
+ else{@+q=lig_ptr(s);c=qo(character(q));hf=font(q);
+ }
+ else if ((type(s)==kern_node)&&(subtype(s)==normal)) goto resume;
+ else if (type(s)==whatsit_node)
+ {@+@<Advance \(p)past a whatsit node in the \(p)pre-hyphenation loop@>;
+ goto resume;
+ }
+ else goto done1;
+ set_lc_code(c);
+ if (hc[0]!=0)
+ if ((hc[0]==c)||(uc_hyph > 0)) goto done2;
+ else goto done1;
+resume: prev_s=s;s=link(prev_s);
+ }
+done2: hyf_char=hyphen_char[hf];
+if (hyf_char < 0) goto done1;
+if (hyf_char > 255) goto done1;
+ha=prev_s
+
+@ The word to be hyphenated is now moved to the |hu| and |hc| arrays.
+
+@<Skip to node |hb|, putting letters...@>=
+hn=0;
+loop@+{@+if (is_char_node(s))
+ {@+if (font(s)!=hf) goto done3;
+ hyf_bchar=character(s);c=qo(hyf_bchar);
+ set_lc_code(c);
+ if (hc[0]==0) goto done3;
+ if (hn==63) goto done3;
+ hb=s;incr(hn);hu[hn]=c;hc[hn]=hc[0];hyf_bchar=non_char;
+ }
+ else if (type(s)==ligature_node)
+ @<Move the characters of a ligature node to |hu| and |hc|; but |goto done3|
+if they are not all letters@>@;
+ else if ((type(s)==kern_node)&&(subtype(s)==normal))
+ {@+hb=s;
+ hyf_bchar=font_bchar[hf];
+ }
+ else goto done3;
+ s=link(s);
+ }
+done3:
+
+@ We let |j| be the index of the character being stored when a ligature node
+is being expanded, since we do not want to advance |hn| until we are sure
+that the entire ligature consists of letters. Note that it is possible
+to get to |done3| with |hn==0| and |hb| not set to any value.
+
+@<Move the characters of a ligature node to |hu| and |hc|...@>=
+{@+if (font(lig_char(s))!=hf) goto done3;
+j=hn;q=lig_ptr(s);@+if (q > null) hyf_bchar=character(q);
+while (q > null)
+ {@+c=qo(character(q));
+ set_lc_code(c);
+ if (hc[0]==0) goto done3;
+ if (j==63) goto done3;
+ incr(j);hu[j]=c;hc[j]=hc[0];@/
+ q=link(q);
+ }
+hb=s;hn=j;
+if (odd(subtype(s))) hyf_bchar=font_bchar[hf];@+else hyf_bchar=non_char;
+}
+
+@ @<Check that the nodes following |hb| permit hyphenation...@>=
+if (hn < l_hyf+r_hyf) goto done1; /*|l_hyf| and |r_hyf| are | >= 1|*/
+loop@+{@+if (!(is_char_node(s)))
+ switch (type(s)) {
+ case ligature_node: do_nothing;@+break;
+ case kern_node: if (subtype(s)!=normal) goto done4;@+break;
+ case whatsit_node: case glue_node:
+ case penalty_node: case ins_node: case adjust_node:
+ case mark_node:
+ goto done4;
+ default:goto done1;
+ }
+ s=link(s);
+ }
+done4:
+
+@* Post-hyphenation.
+If a hyphen may be inserted between |hc[j]| and |hc[j+1]|, the hyphenation
+procedure will set |hyf[j]| to some small odd number. But before we look
+at \TeX's hyphenation procedure, which is independent of the rest of the
+line-breaking algorithm, let us consider what we will do with the hyphens
+it finds, since it is better to work on this part of the program before
+forgetting what |ha| and |hb|, etc., are all about.
+
+@<Glob...@>=
+static int8_t @!hyf[65]; /*odd values indicate discretionary hyphens*/
+static pointer @!init_list; /*list of punctuation characters preceding the word*/
+static bool @!init_lig; /*does |init_list| represent a ligature?*/
+static bool @!init_lft; /*if so, did the ligature involve a left boundary?*/
+
+@ @<Local variables for hyphenation@>=
+int @!i, @!j, @!l; /*indices into |hc| or |hu|*/
+pointer @!q, @!r, @!s; /*temporary registers for list manipulation*/
+halfword @!bchar; /*boundary character of hyphenated word, or |non_char|*/
+
+@ \TeX\ will never insert a hyphen that has fewer than
+\.{\\lefthyphenmin} letters before it or fewer than
+\.{\\righthyphenmin} after it; hence, a short word has
+comparatively little chance of being hyphenated. If no hyphens have
+been found, we can save time by not having to make any changes to the
+paragraph.
+
+@<If no hyphens were found, |return|@>=
+for (j=l_hyf; j<=hn-r_hyf; j++) if (odd(hyf[j])) goto found1;
+return;
+found1:
+
+@ If hyphens are in fact going to be inserted, \TeX\ first deletes the
+subsequence of nodes between |ha| and~|hb|. An attempt is made to
+preserve the effect that implicit boundary characters and punctuation marks
+had on ligatures inside the hyphenated word, by storing a left boundary or
+preceding character in |hu[0]| and by storing a possible right boundary
+in |bchar|. We set |j=0| if |hu[0]| is to be part of the reconstruction;
+otherwise |j=1|.
+The variable |s| will point to the tail of the current hlist, and
+|q| will point to the node following |hb|, so that
+things can be hooked up after we reconstitute the hyphenated word.
+
+@<Replace nodes |ha..hb| by a sequence of nodes...@>=
+q=link(hb);link(hb)=null;r=link(ha);link(ha)=null;bchar=hyf_bchar;
+if (is_char_node(ha))
+ if (font(ha)!=hf) goto found2;
+ else{@+init_list=ha;init_lig=false;hu[0]=qo(character(ha));
+ }
+else if (type(ha)==ligature_node)
+ if (font(lig_char(ha))!=hf) goto found2;
+ else{@+init_list=lig_ptr(ha);init_lig=true;init_lft=(subtype(ha) > 1);
+ hu[0]=qo(character(lig_char(ha)));
+ if (init_list==null) if (init_lft)
+ {@+hu[0]=256;init_lig=false;
+ } /*in this case a ligature will be reconstructed from scratch*/
+ free_node(ha, small_node_size);
+ }
+else{@+ /*no punctuation found; look for left boundary*/
+ if (!is_char_node(r)) if (type(r)==ligature_node)
+ if (subtype(r) > 1) goto found2;
+ j=1;s=ha;init_list=null;goto common_ending;
+ }
+s=cur_p; /*we have |cur_p!=ha| because |type(cur_p)==glue_node|*/
+while (link(s)!=ha) s=link(s);
+j=0;goto common_ending;
+found2: s=ha;j=0;hu[0]=256;init_lig=false;init_list=null;
+common_ending: flush_node_list(r);
+@<Reconstitute nodes for the hyphenated word, inserting discretionary hyphens@>;
+flush_list(init_list)
+
+@ We must now face the fact that the battle is not over, even though the
+{\def\!{\kern-1pt}%
+hyphens have been found: The process of reconstituting a word can be nontrivial
+because ligatures might change when a hyphen is present. {\sl The \TeX book\/}
+discusses the difficulties of the word ``difficult'', and
+the discretionary material surrounding a
+hyphen can be considerably more complex than that. Suppose
+\.{abcdef} is a word in a font for which the only ligatures are \.{b\!c},
+\.{c\!d}, \.{d\!e}, and \.{e\!f}. If this word permits hyphenation
+between \.b and \.c, the two patterns with and without hyphenation are
+$\.a\,\.b\,\.-\,\.{c\!d}\,\.{e\!f}$ and $\.a\,\.{b\!c}\,\.{d\!e}\,\.f$.
+Thus the insertion of a hyphen might cause effects to ripple arbitrarily
+far into the rest of the word. A further complication arises if additional
+hyphens appear together with such rippling, e.g., if the word in the
+example just given could also be hyphenated between \.c and \.d; \TeX\
+avoids this by simply ignoring the additional hyphens in such weird cases.}
+
+Still further complications arise in the presence of ligatures that do not
+delete the original characters. When punctuation precedes the word being
+hyphenated, \TeX's method is not perfect under all possible scenarios,
+because punctuation marks and letters can propagate information back and forth.
+For example, suppose the original pre-hyphenation pair
+\.{*a} changes to \.{*y} via a \.{\@@=:} ligature, which changes to \.{xy}
+via a \.{=:\@@} ligature; if $p_{a-1}=\.x$ and $p_a=\.y$, the reconstitution
+procedure isn't smart enough to obtain \.{xy} again. In such cases the
+font designer should include a ligature that goes from \.{xa} to \.{xy}.
+
+@ The processing is facilitated by a subroutine called |reconstitute|. Given
+a string of characters $x_j\ldots x_n$, there is a smallest index $m\ge j$
+such that the ``translation'' of $x_j\ldots x_n$ by ligatures and kerning
+has the form $y_1\ldots y_t$ followed by the translation of $x_{m+1}\ldots x_n$,
+where $y_1\ldots y_t$ is some nonempty sequence of character, ligature, and
+kern nodes. We call $x_j\ldots x_m$ a ``cut prefix'' of $x_j\ldots x_n$.
+For example, if $x_1x_2x_3=\.{fly}$, and if the font contains `fl' as a
+ligature and a kern between `fl' and `y', then $m=2$, $t=2$, and $y_1$ will
+be a ligature node for `fl' followed by an appropriate kern node~$y_2$.
+In the most common case, $x_j$~forms no ligature with $x_{j+1}$ and we
+simply have $m=j$, $y_1=x_j$. If $m<n$ we can repeat the procedure on
+$x_{m+1}\ldots x_n$ until the entire translation has been found.
+
+The |reconstitute| function returns the integer $m$ and puts the nodes
+$y_1\ldots y_t$ into a linked list starting at |link(hold_head)|,
+getting the input $x_j\ldots x_n$ from the |hu| array. If $x_j=256$,
+we consider $x_j$ to be an implicit left boundary character; in this
+case |j| must be strictly less than~|n|. There is a
+parameter |bchar|, which is either 256 or an implicit right boundary character
+assumed to be present just following~$x_n$. (The value |hu[n+1]| is never
+explicitly examined, but the algorithm imagines that |bchar| is there.)
+
+If there exists an index |k| in the range $j\le k\le m$ such that |hyf[k]|
+is odd and such that the result of |reconstitute| would have been different
+if $x_{k+1}$ had been |hchar|, then |reconstitute| sets |hyphen_passed|
+to the smallest such~|k|. Otherwise it sets |hyphen_passed| to zero.
+
+A special convention is used in the case |j==0|: Then we assume that the
+translation of |hu[0]| appears in a special list of charnodes starting at
+|init_list|; moreover, if |init_lig| is |true|, then |hu[0]| will be
+a ligature character, involving a left boundary if |init_lft| is |true|.
+This facility is provided for cases when a hyphenated
+word is preceded by punctuation (like single or double quotes) that might
+affect the translation of the beginning of the word.
+
+@<Glob...@>=
+static small_number @!hyphen_passed; /*first hyphen in a ligature, if any*/
+
+@ @<Declare the function called |reconstitute|@>=
+static
+ small_number reconstitute(small_number @!j, small_number @!n, halfword @!bchar, halfword @!hchar)
+{@+
+pointer @!p; /*temporary register for list manipulation*/
+pointer @!t; /*a node being appended to*/
+four_quarters @!q; /*character information or a lig/kern instruction*/
+halfword @!cur_rh; /*hyphen character for ligature testing*/
+halfword @!test_char; /*hyphen or other character for ligature testing*/
+scaled @!w; /*amount of kerning*/
+font_index @!k; /*position of current lig/kern instruction*/
+hyphen_passed=0;t=hold_head;w=0;link(hold_head)=null;
+ /*at this point |ligature_present==lft_hit==rt_hit==false|*/
+@<Set up data structures with the cursor following position |j|@>;
+resume: @<If there's a ligature or kern at the cursor position, update the
+data structures, possibly advancing~|j|; continue until the cursor moves@>;
+@<Append a ligature and/or kern to the translation; |goto resume| if the
+stack of inserted ligatures is nonempty@>;
+return j;
+}
+
+@ The reconstitution procedure shares many of the global data structures
+by which \TeX\ has processed the words before they were hyphenated.
+There is an implied ``cursor'' between characters |cur_l| and |cur_r|;
+these characters will be tested for possible ligature activity. If
+|ligature_present| then |cur_l| is a ligature character formed from the
+original characters following |cur_q| in the current translation list.
+There is a ``ligature stack'' between the cursor and character |j+1|,
+consisting of pseudo-ligature nodes linked together by their |link| fields.
+This stack is normally empty unless a ligature command has created a new
+character that will need to be processed later. A pseudo-ligature is
+a special node having a |character| field that represents a potential
+ligature and a |lig_ptr| field that points to a |char_node| or is |null|.
+We have
+$$|cur_r|=\cases{|character(lig_stack)|,&if |lig_stack > null|;\cr
+ \hbox{|qi(hu[j+1])|},&if |lig_stack==null| and |j < n|;\cr
+ bchar,&if |lig_stack==null| and |j==n|.\cr}$$
+
+@<Glob...@>=
+static halfword @!cur_l, @!cur_r; /*characters before and after the cursor*/
+static pointer @!cur_q; /*where a ligature should be detached*/
+static pointer @!lig_stack; /*unfinished business to the right of the cursor*/
+static bool @!ligature_present; /*should a ligature node be made for |cur_l|?*/
+static bool @!lft_hit, @!rt_hit; /*did we hit a ligature with a boundary character?*/
+
+@ @d append_charnode_to_t(A) {@+link(t)=get_avail();t=link(t);
+ font(t)=hf;character(t)=A;
+ }
+@d set_cur_r {@+if (j < n) cur_r=qi(hu[j+1]);@+else cur_r=bchar;
+ if (odd(hyf[j])) cur_rh=hchar;@+else cur_rh=non_char;
+ }
+
+@<Set up data structures with the cursor following position |j|@>=
+cur_l=qi(hu[j]);cur_q=t;
+if (j==0)
+ {@+ligature_present=init_lig;p=init_list;
+ if (ligature_present) lft_hit=init_lft;
+ while (p > null)
+ {@+append_charnode_to_t(character(p));p=link(p);
+ }
+ }
+else if (cur_l < non_char) append_charnode_to_t(cur_l);
+lig_stack=null;set_cur_r
+
+@ We may want to look at the lig/kern program twice, once for a hyphen
+and once for a normal letter. (The hyphen might appear after the letter
+in the program, so we'd better not try to look for both at once.)
+
+@<If there's a ligature or kern at the cursor position, update...@>=
+if (cur_l==non_char)
+ {@+k=bchar_label[hf];
+ if (k==non_address) goto done;@+else q=font_info[k].qqqq;
+ }
+else{@+q=char_info(hf, cur_l);
+ if (char_tag(q)!=lig_tag) goto done;
+ k=lig_kern_start(hf, q);q=font_info[k].qqqq;
+ if (skip_byte(q) > stop_flag)
+ {@+k=lig_kern_restart(hf, q);q=font_info[k].qqqq;
+ }
+ } /*now |k| is the starting address of the lig/kern program*/
+if (cur_rh < non_char) test_char=cur_rh;@+else test_char=cur_r;
+loop@+{@+if (next_char(q)==test_char) if (skip_byte(q) <= stop_flag)
+ if (cur_rh < non_char)
+ {@+hyphen_passed=j;hchar=non_char;cur_rh=non_char;
+ goto resume;
+ }
+ else{@+if (hchar < non_char) if (odd(hyf[j]))
+ {@+hyphen_passed=j;hchar=non_char;
+ }
+ if (op_byte(q) < kern_flag)
+ @<Carry out a ligature replacement, updating the cursor structure and
+possibly advancing~|j|; |goto resume| if the cursor doesn't advance, otherwise
+|goto done|@>;
+ w=char_kern(hf, q);goto done; /*this kern will be inserted below*/
+ }
+ if (skip_byte(q) >= stop_flag)
+ if (cur_rh==non_char) goto done;
+ else{@+cur_rh=non_char;goto resume;
+ }
+ k=k+qo(skip_byte(q))+1;q=font_info[k].qqqq;
+ }
+done:
+
+@ @d wrap_lig(A) if (ligature_present)
+ {@+p=new_ligature(hf, cur_l, link(cur_q));
+ if (lft_hit)
+ {@+subtype(p)=2;lft_hit=false;
+ }
+ if (A) if (lig_stack==null)
+ {@+incr(subtype(p));rt_hit=false;
+ }
+ link(cur_q)=p;t=p;ligature_present=false;
+ }
+@d pop_lig_stack {@+if (lig_ptr(lig_stack) > null)
+ {@+link(t)=lig_ptr(lig_stack); /*this is a charnode for |hu[j+1]|*/
+ t=link(t);incr(j);
+ }
+ p=lig_stack;lig_stack=link(p);free_node(p, small_node_size);
+ if (lig_stack==null) set_cur_r@;@+else cur_r=character(lig_stack);
+ } /*if |lig_stack| isn't |null| we have |cur_rh==non_char|*/
+
+@<Append a ligature and/or kern to the translation...@>=
+wrap_lig(rt_hit);
+if (w!=0)
+ {@+link(t)=new_kern(w);t=link(t);w=0;
+ }
+if (lig_stack > null)
+ {@+cur_q=t;cur_l=character(lig_stack);ligature_present=true;
+ pop_lig_stack;goto resume;
+ }
+
+@ @<Carry out a ligature replacement, updating the cursor structure...@>=
+{@+if (cur_l==non_char) lft_hit=true;
+if (j==n) if (lig_stack==null) rt_hit=true;
+check_interrupt; /*allow a way out in case there's an infinite ligature loop*/
+switch (op_byte(q)) {
+case qi(1): case qi(5): {@+cur_l=rem_byte(q); /*\.{=:\@@}, \.{=:\@@>}*/
+ ligature_present=true;
+ } @+break;
+case qi(2): case qi(6): {@+cur_r=rem_byte(q); /*\.{\@@=:}, \.{\@@=:>}*/
+ if (lig_stack > null) character(lig_stack)=cur_r;
+ else{@+lig_stack=new_lig_item(cur_r);
+ if (j==n) bchar=non_char;
+ else{@+p=get_avail();lig_ptr(lig_stack)=p;
+ character(p)=qi(hu[j+1]);font(p)=hf;
+ }
+ }
+ } @+break;
+case qi(3): {@+cur_r=rem_byte(q); /*\.{\@@=:\@@}*/
+ p=lig_stack;lig_stack=new_lig_item(cur_r);link(lig_stack)=p;
+ } @+break;
+case qi(7): case qi(11): {@+wrap_lig(false); /*\.{\@@=:\@@>}, \.{\@@=:\@@>>}*/
+ cur_q=t;cur_l=rem_byte(q);ligature_present=true;
+ } @+break;
+default:{@+cur_l=rem_byte(q);ligature_present=true; /*\.{=:}*/
+ if (lig_stack > null) pop_lig_stack@;
+ else if (j==n) goto done;
+ else{@+append_charnode_to_t(cur_r);incr(j);set_cur_r;
+ }
+ }
+}
+if (op_byte(q) > qi(4)) if (op_byte(q)!=qi(7)) goto done;
+goto resume;
+}
+
+@ Okay, we're ready to insert the potential hyphenations that were found.
+When the following program is executed, we want to append the word
+|hu[1 dotdot hn]| after node |ha|, and node |q| should be appended to the result.
+During this process, the variable |i| will be a temporary
+index into |hu|; the variable |j| will be an index to our current position
+in |hu|; the variable |l| will be the counterpart of |j|, in a discretionary
+branch; the variable |r| will point to new nodes being created; and
+we need a few new local variables:
+
+@<Local variables for hyph...@>=
+pointer @!major_tail, @!minor_tail; /*the end of lists in the main and
+ discretionary branches being reconstructed*/
+ASCII_code @!c; /*character temporarily replaced by a hyphen*/
+int @!c_loc; /*where that character came from*/
+int @!r_count; /*replacement count for discretionary*/
+pointer @!hyf_node; /*the hyphen, if it exists*/
+
+@ When the following code is performed, |hyf[0]| and |hyf[hn]| will be zero.
+
+@<Reconstitute nodes for the hyphenated word...@>=
+@/do@+{l=j;j=reconstitute(j, hn, bchar, qi(hyf_char))+1;
+if (hyphen_passed==0)
+ {@+link(s)=link(hold_head);
+ while (link(s) > null) s=link(s);
+ if (odd(hyf[j-1]))
+ {@+l=j;hyphen_passed=j-1;link(hold_head)=null;
+ }
+ }
+if (hyphen_passed > 0)
+ @<Create and append a discretionary node as an alternative to the unhyphenated
+word, and continue to develop both branches until they become equivalent@>;
+}@+ while (!(j > hn));
+link(s)=q
+
+@ In this repeat loop we will insert another discretionary if |hyf[j-1]| is
+odd, when both branches of the previous discretionary end at position |j-1|.
+Strictly speaking, we aren't justified in doing this, because we don't know
+that a hyphen after |j-1| is truly independent of those branches. But in almost
+all applications we would rather not lose a potentially valuable hyphenation
+point. (Consider the word `difficult', where the letter `c' is in position |j|.)
+
+@d advance_major_tail {@+major_tail=link(major_tail);incr(r_count);
+ }
+
+@<Create and append a discretionary node as an alternative...@>=
+@/do@+{r=get_node(small_node_size);
+link(r)=link(hold_head);type(r)=disc_node;
+major_tail=r;r_count=0;
+while (link(major_tail) > null) advance_major_tail;
+i=hyphen_passed;hyf[i]=0;
+@<Put the \(c)characters |hu[l..i]| and a hyphen into |pre_break(r)|@>;
+@<Put the \(c)characters |hu[i+1..]| into |post_break(r)|, appending to this
+list and to |major_tail| until synchronization has been achieved@>;
+@<Move pointer |s| to the end of the current list, and set |replace_count(r)|
+appropriately@>;
+hyphen_passed=j-1;link(hold_head)=null;
+}@+ while (!(!odd(hyf[j-1])))
+
+@ The new hyphen might combine with the previous character via ligature
+or kern. At this point we have |l-1 <= i < j| and |i < hn|.
+
+@<Put the \(c)characters |hu[l..i]| and a hyphen into |pre_break(r)|@>=
+minor_tail=null;pre_break(r)=null;hyf_node=new_character(hf, hyf_char);
+if (hyf_node!=null)
+ {@+incr(i);c=hu[i];hu[i]=hyf_char;free_avail(hyf_node);
+ }
+while (l <= i)
+ {@+l=reconstitute(l, i, font_bchar[hf], non_char)+1;
+ if (link(hold_head) > null)
+ {@+if (minor_tail==null) pre_break(r)=link(hold_head);
+ else link(minor_tail)=link(hold_head);
+ minor_tail=link(hold_head);
+ while (link(minor_tail) > null) minor_tail=link(minor_tail);
+ }
+ }
+if (hyf_node!=null)
+ {@+hu[i]=c; /*restore the character in the hyphen position*/
+ l=i;decr(i);
+ }
+
+@ The synchronization algorithm begins with |l==i+1 <= j|.
+
+@<Put the \(c)characters |hu[i+1..]| into |post_break(r)|...@>=
+minor_tail=null;post_break(r)=null;c_loc=0;
+if (bchar_label[hf]!=non_address) /*put left boundary at beginning of new line*/
+ {@+decr(l);c=hu[l];c_loc=l;hu[l]=256;
+ }
+while (l < j)
+ {@+@/do@+{l=reconstitute(l, hn, bchar, non_char)+1;
+ if (c_loc > 0)
+ {@+hu[c_loc]=c;c_loc=0;
+ }
+ if (link(hold_head) > null)
+ {@+if (minor_tail==null) post_break(r)=link(hold_head);
+ else link(minor_tail)=link(hold_head);
+ minor_tail=link(hold_head);
+ while (link(minor_tail) > null) minor_tail=link(minor_tail);
+ }
+ }@+ while (!(l >= j));
+ while (l > j)
+ @<Append characters of |hu[j..]| to |major_tail|, advancing~|j|@>;
+ }
+
+@ @<Append characters of |hu[j..]|...@>=
+{@+j=reconstitute(j, hn, bchar, non_char)+1;
+link(major_tail)=link(hold_head);
+while (link(major_tail) > null) advance_major_tail;
+}
+
+@ Ligature insertion can cause a word to grow exponentially in size. Therefore
+we must test the size of |r_count| here, even though the hyphenated text
+was at most 63 characters long.
+
+@<Move pointer |s| to the end of the current list...@>=
+if (r_count > 127) /*we have to forget the discretionary hyphen*/
+ {@+link(s)=link(r);link(r)=null;flush_node_list(r);
+ }
+else{@+link(s)=r;replace_count(r)=r_count;
+ }
+s=major_tail
+
+@* Hyphenation.
+When a word |hc[1 dotdot hn]| has been set up to contain a candidate for hyphenation,
+\TeX\ first looks to see if it is in the user's exception dictionary. If not,
+hyphens are inserted based on patterns that appear within the given word,
+using an algorithm due to Frank~M. Liang.
+@^Liang, Franklin Mark@>
+
+Let's consider Liang's method first, since it is much more interesting than the
+exception-lookup routine. The algorithm begins by setting |hyf[j]| to zero
+for all |j|, and invalid characters are inserted into |hc[0]|
+and |hc[hn+1]| to serve as delimiters. Then a reasonably fast method is
+used to see which of a given set of patterns occurs in the word
+|hc[0 dotdot(hn+1)]|. Each pattern $p_1\ldots p_k$ of length |k| has an associated
+sequence of |k+1| numbers $n_0\ldots n_k$; and if the pattern occurs in
+|hc[(j+1)dotdot(j+k)]|, \TeX\ will set |hyf[j+i]=@tmax@>(hyf[j+i],@t$n\_i$@>)| for
+|0 <= i <= k|. After this has been done for each pattern that occurs, a
+discretionary hyphen will be inserted between |hc[j]| and |hc[j+1]| when
+|hyf[j]| is odd, as we have already seen.
+
+The set of patterns $p_1\ldots p_k$ and associated numbers $n_0\ldots n_k$
+depends, of course, on the language whose words are being hyphenated, and
+on the degree of hyphenation that is desired. A method for finding
+appropriate |p|'s and |n|'s, from a given dictionary of words and acceptable
+hyphenations, is discussed in Liang's Ph.D. thesis (Stanford University,
+1983); \TeX\ simply starts with the patterns and works from there.
+
+@ The patterns are stored in a compact table that is also efficient for
+retrieval, using a variant of ``trie memory'' [cf.\ {\sl The Art of
+Computer Programming \bf3} (1973), 481--505]. We can find each pattern
+$p_1\ldots p_k$ by letting $z_0$ be one greater than the relevant language
+index and then, for |1 <= i <= k|,
+setting |@t$z\_i$@>=trie_link@t$(z\_{i-1})+p\_i$@>|; the pattern will be
+identified by the number $z_k$. Since all the pattern information is
+packed together into a single |trie_link| array, it is necessary to
+prevent confusion between the data from inequivalent patterns, so another
+table is provided such that |trie_char@t$(z\_i)=p\_i$@>| for all |i|. There
+is also a table |trie_op|$(z_k)$ to identify the numbers $n_0\ldots n_k$
+associated with $p_1\ldots p_k$.
+
+Comparatively few different number sequences $n_0\ldots n_k$ actually occur,
+since most of the |n|'s are generally zero. Therefore the number sequences
+are encoded in such a way that |trie_op|$(z_k)$ is only one byte long.
+If |trie_op(@t$z\_k$@>)!=min_quarterword|, when $p_1\ldots p_k$ has matched
+the letters in |hc[(l-k+1)dotdot l@,]| of language |t|,
+we perform all of the required operations
+for this pattern by carrying out the following little program: Set
+|v=trie_op(@t$z\_k$@>)|. Then set |v=v+op_start[t]|,
+|hyf[l-hyf_distance[v]]=@tmax@>(hyf[l-hyf_distance[v]], hyf_num[v])|,
+and |v=hyf_next[v]|; repeat, if necessary, until |v==min_quarterword|.
+
+@<Types...@>=
+typedef int32_t trie_pointer; /*an index into |trie|*/
+
+@ @d trie_link(A) trie[A].rh /*``downward'' link in a trie*/
+@d trie_char(A) trie[A].b1 /*character matched at this trie location*/
+@d trie_op(A) trie[A].b0 /*program for hyphenation at this trie location*/
+
+@<Glob...@>=
+static two_halves @!trie[trie_size+1]; /*|trie_link|, |trie_char|, |trie_op|*/
+static small_number @!hyf_distance0[trie_op_size],
+ *const @!hyf_distance = @!hyf_distance0-1; /*position |k-j| of $n_j$*/
+static small_number @!hyf_num0[trie_op_size],
+ *const @!hyf_num = @!hyf_num0-1; /*value of $n_j$*/
+static quarterword @!hyf_next0[trie_op_size],
+ *const @!hyf_next = @!hyf_next0-1; /*continuation code*/
+static uint16_t @!op_start[256]; /*offset for current language*/
+
+@ @<Local variables for hyph...@>=
+trie_pointer @!z; /*an index into |trie|*/
+int @!v; /*an index into |hyf_distance|, etc.*/
+
+@ Assuming that these auxiliary tables have been set up properly, the
+hyphenation algorithm is quite short. In the following code we set |hc[hn+2]|
+to the impossible value 256, in order to guarantee that |hc[hn+3]| will
+never be fetched.
+
+@<Find hyphen locations for the word in |hc|...@>=
+for (j=0; j<=hn; j++) hyf[j]=0;
+@<Look for the word |hc[1..hn]| in the exception table, and |goto found| (with
+|hyf| containing the hyphens) if an entry is found@>;
+if (trie_char(cur_lang+1)!=qi(cur_lang)) return; /*no patterns for |cur_lang|*/
+hc[0]=0;hc[hn+1]=0;hc[hn+2]=256; /*insert delimiters*/
+for (j=0; j<=hn-r_hyf+1; j++)
+ {@+z=trie_link(cur_lang+1)+hc[j];l=j;
+ while (hc[l]==qo(trie_char(z)))
+ {@+if (trie_op(z)!=min_quarterword)
+ @<Store \(m)maximum values in the |hyf| table@>;
+ incr(l);z=trie_link(z)+hc[l];
+ }
+ }
+found: for (j=0; j<=l_hyf-1; j++) hyf[j]=0;
+for (j=0; j<=r_hyf-1; j++) hyf[hn-j]=0
+
+@ @<Store \(m)maximum values in the |hyf| table@>=
+{@+v=trie_op(z);
+@/do@+{v=v+op_start[cur_lang];i=l-hyf_distance[v];
+if (hyf_num[v] > hyf[i]) hyf[i]=hyf_num[v];
+v=hyf_next[v];
+}@+ while (!(v==min_quarterword));
+}
+
+@ The exception table that is built by \TeX's \.{\\hyphenation} primitive is
+organized as an ordered hash table [cf.\ Amble and Knuth, {\sl The Computer
+@^Amble, Ole@> @^Knuth, Donald Ervin@>
+Journal\/ \bf17} (1974), 135--142] using linear probing. If $\alpha$ and
+$\beta$ are words, we will say that $\alpha<\beta$ if $\vert\alpha\vert<
+\vert\beta\vert$ or if $\vert\alpha\vert=\vert\beta\vert$ and
+$\alpha$ is lexicographically smaller than $\beta$. (The notation $\vert
+\alpha\vert$ stands for the length of $\alpha$.) The idea of ordered hashing
+is to arrange the table so that a given word $\alpha$ can be sought by computing
+a hash address $h=h(\alpha)$ and then looking in table positions |h|, |h-1|,
+\dots, until encountering the first word $\le\alpha$. If this word is
+different from $\alpha$, we can conclude that $\alpha$ is not in the table.
+
+The words in the table point to lists in |mem| that specify hyphen positions
+in their |info| fields. The list for $c_1\ldots c_n$ contains the number |k| if
+the word $c_1\ldots c_n$ has a discretionary hyphen between $c_k$ and
+$c_{k+1}$.
+
+@<Types...@>=
+typedef int16_t hyph_pointer; /*an index into the ordered hash table*/
+
+@ @<Glob...@>=
+static str_number @!hyph_word[hyph_size+1]; /*exception words*/
+static pointer @!hyph_list[hyph_size+1]; /*lists of hyphen positions*/
+static hyph_pointer @!hyph_count; /*the number of words in the exception dictionary*/
+
+@ @<Local variables for init...@>=
+int @!z; /*runs through the exception dictionary*/
+
+@ @<Set init...@>=
+for (z=0; z<=hyph_size; z++)
+ {@+hyph_word[z]=0;hyph_list[z]=null;
+ }
+hyph_count=0;
+
+@ The algorithm for exception lookup is quite simple, as soon as we have
+a few more local variables to work with.
+
+@<Local variables for hyph...@>=
+hyph_pointer @!h; /*an index into |hyph_word| and |hyph_list|*/
+str_number @!k; /*an index into |str_start|*/
+pool_pointer @!u; /*an index into |str_pool|*/
+
+@ First we compute the hash code |h|, then we search until we either
+find the word or we don't. Words from different languages are kept
+separate by appending the language code to the string.
+
+@<Look for the word |hc[1...@>=
+h=hc[1];incr(hn);hc[hn]=cur_lang;
+for (j=2; j<=hn; j++) h=(h+h+hc[j])%hyph_size;
+loop@+{@+@<If the string |hyph_word[h]| is less than \(hc)|hc[1..hn]|, |goto
+not_found|; but if the two strings are equal, set |hyf| to the hyphen positions
+and |goto found|@>;
+ if (h > 0) decr(h);@+else h=hyph_size;
+ }
+not_found: decr(hn)
+
+@ @<If the string |hyph_word[h]| is less than \(hc)...@>=
+k=hyph_word[h];if (k==0) goto not_found;
+if (length(k) < hn) goto not_found;
+if (length(k)==hn)
+ {@+j=1;u=str_start[k];
+ @/do@+{if (so(str_pool[u]) < hc[j]) goto not_found;
+ if (so(str_pool[u]) > hc[j]) goto done;
+ incr(j);incr(u);
+ }@+ while (!(j > hn));
+ @<Insert hyphens as specified in |hyph_list[h]|@>;
+ decr(hn);goto found;
+ }
+done:
+
+@ @<Insert hyphens as specified...@>=
+s=hyph_list[h];
+while (s!=null)
+ {@+hyf[info(s)]=1;s=link(s);
+ }
+
+@ @<Search |hyph_list| for pointers to |p|@>=
+for (q=0; q<=hyph_size; q++)
+ {@+if (hyph_list[q]==p)
+ {@+print_nl("HYPH(");print_int(q);print_char(')');
+ }
+ }
+
+@ We have now completed the hyphenation routine, so the |line_break| procedure
+is finished at last. Since the hyphenation exception table is fresh in our
+minds, it's a good time to deal with the routine that adds new entries to it.
+
+When \TeX\ has scanned `\.{\\hyphenation}', it calls on a procedure named
+|new_hyph_exceptions| to do the right thing.
+
+@d set_cur_lang if (language <= 0) cur_lang=0;
+ else if (language > 255) cur_lang=0;
+ else cur_lang=language
+
+@p static void new_hyph_exceptions(void) /*enters new exceptions*/
+{@+
+int n; /*length of current word; not always a |small_number|*/
+int @!j; /*an index into |hc|*/
+hyph_pointer @!h; /*an index into |hyph_word| and |hyph_list|*/
+str_number @!k; /*an index into |str_start|*/
+pointer @!p; /*head of a list of hyphen positions*/
+pointer @!q; /*used when creating a new node for list |p|*/
+str_number @!s, @!t; /*strings being compared or stored*/
+pool_pointer @!u, @!v; /*indices into |str_pool|*/
+scan_left_brace(); /*a left brace must follow \.{\\hyphenation}*/
+set_cur_lang;
+#ifdef @!INIT
+if (trie_not_ready)
+ {@+hyph_index=0;goto not_found1;
+ }
+#endif
+set_hyph_index;
+not_found1:
+@<Enter as many hyphenation exceptions as are listed, until coming to a right
+brace; then |return|@>;
+}
+
+@ @<Enter as many...@>=
+n=0;p=null;
+loop@+{@+get_x_token();
+ reswitch: switch (cur_cmd) {
+ case letter: case other_char: case char_given: @<Append a new letter or
+hyphen@>@;@+break;
+ case char_num: {@+scan_char_num();cur_chr=cur_val;cur_cmd=char_given;
+ goto reswitch;
+ }
+ case spacer: case right_brace: {@+if (n > 1) @<Enter a hyphenation exception@>;
+ if (cur_cmd==right_brace) return;
+ n=0;p=null;
+ } @+break;
+ default:@<Give improper \.{\\hyphenation} error@>@;
+ }
+ }
+
+@ @<Give improper \.{\\hyph...@>=
+{@+print_err("Improper ");print_esc("hyphenation");
+@.Improper \\hyphenation...@>
+ print(" will be flushed");
+help2("Hyphenation exceptions must contain only letters",@/
+ "and hyphens. But continue; I'll forgive and forget.");
+error();
+}
+
+@ @<Append a new letter or hyphen@>=
+if (cur_chr=='-') @<Append the value |n| to list |p|@>@;
+else{@+set_lc_code(cur_chr);
+ if (hc[0]==0)
+ {@+print_err("Not a letter");
+@.Not a letter@>
+ help2("Letters in \\hyphenation words must have \\lccode>0.",@/
+ "Proceed; I'll ignore the character I just read.");
+ error();
+ }
+ else if (n < 63)
+ {@+incr(n);hc[n]=hc[0];
+ }
+ }
+
+@ @<Append the value |n| to list |p|@>=
+{@+if (n < 63)
+ {@+q=get_avail();link(q)=p;info(q)=n;p=q;
+ }
+}
+
+@ @<Enter a hyphenation exception@>=
+{@+incr(n);hc[n]=cur_lang;str_room(n);h=0;
+for (j=1; j<=n; j++)
+ {@+h=(h+h+hc[j])%hyph_size;
+ append_char(hc[j]);
+ }
+s=make_string();
+@<Insert the \(p)pair |(s,p)| into the exception table@>;
+}
+
+@ @<Insert the \(p)pair |(s,p)|...@>=
+if (hyph_count==hyph_size) overflow("exception dictionary", hyph_size);
+@:TeX capacity exceeded exception dictionary}{\quad exception dictionary@>
+incr(hyph_count);
+while (hyph_word[h]!=0)
+ {@+@<If the string |hyph_word[h]| is less than \(or)or equal to |s|, interchange
+|(hyph_word[h],hyph_list[h])| with |(s,p)|@>;
+ if (h > 0) decr(h);@+else h=hyph_size;
+ }
+hyph_word[h]=s;hyph_list[h]=p
+
+@ @<If the string |hyph_word[h]| is less than \(or)...@>=
+k=hyph_word[h];
+if (length(k) < length(s)) goto found;
+if (length(k) > length(s)) goto not_found;
+u=str_start[k];v=str_start[s];
+@/do@+{if (str_pool[u] < str_pool[v]) goto found;
+if (str_pool[u] > str_pool[v]) goto not_found;
+incr(u);incr(v);
+}@+ while (!(u==str_start[k+1]));
+found: q=hyph_list[h];hyph_list[h]=p;p=q;@/
+t=hyph_word[h];hyph_word[h]=s;s=t;
+not_found:
+
+@* Initializing the hyphenation tables.
+The trie for \TeX's hyphenation algorithm is built from a sequence of
+patterns following a \.{\\patterns} specification. Such a specification
+is allowed only in \.{INITEX}, since the extra memory for auxiliary tables
+and for the initialization program itself would only clutter up the
+production version of \TeX\ with a lot of deadwood.
+
+The first step is to build a trie that is linked, instead of packed
+into sequential storage, so that insertions are readily made.
+After all patterns have been processed, \.{INITEX}
+compresses the linked trie by identifying common subtries. Finally the
+trie is packed into the efficient sequential form that the hyphenation
+algorithm actually uses.
+
+@<Declare subprocedures for |line_break|@>=
+#ifdef @!INIT
+@<Declare procedures for preprocessing hyphenation patterns@>@;
+#endif
+
+@ Before we discuss trie building in detail, let's consider the simpler
+problem of creating the |hyf_distance|, |hyf_num|, and |hyf_next| arrays.
+
+Suppose, for example, that \TeX\ reads the pattern `\.{ab2cde1}'. This is
+a pattern of length 5, with $n_0\ldots n_5=0\,0\,2\,0\,0\,1$ in the
+notation above. We want the corresponding |trie_op| code |v| to have
+|hyf_distance[v]==3|, |hyf_num[v]==2|, and |hyf_next[v]==@t$v^\prime$@>|,
+where the auxiliary |trie_op| code $v^\prime$ has
+|hyf_distance[@t$v^\prime$@>]==0|, |hyf_num[@t$v^\prime$@>]==1|, and
+|hyf_next[@t$v^\prime$@>]==min_quarterword|.
+
+\TeX\ computes an appropriate value |v| with the |new_trie_op| subroutine
+below, by setting
+$$\hbox{|@t$v^\prime$@>=new_trie_op(0, 1, min_quarterword)|,\qquad
+|v=new_trie_op(3, 2,@t$v^\prime$@>)|.}$$
+This subroutine looks up its three
+parameters in a special hash table, assigning a new value only if these
+three have not appeared before for the current language.
+
+The hash table is called |trie_op_hash|, and the number of entries it contains
+is |trie_op_ptr|.
+
+@<Glob...@>=
+#ifdef @!INIT
+static uint16_t @!trie_op_hash0[trie_op_size+trie_op_size+1],
+ *const @!trie_op_hash = @!trie_op_hash0+trie_op_size;
+ /*trie op codes for quadruples*/
+static quarterword @!trie_used[256];
+ /*largest opcode used so far for this language*/
+static ASCII_code @!trie_op_lang0[trie_op_size],
+ *const @!trie_op_lang = @!trie_op_lang0-1;
+ /*language part of a hashed quadruple*/
+static quarterword @!trie_op_val0[trie_op_size],
+ *const @!trie_op_val = @!trie_op_val0-1;
+ /*opcode corresponding to a hashed quadruple*/
+static int @!trie_op_ptr; /*number of stored ops so far*/
+#endif
+
+@ It's tempting to remove the |overflow| stops in the following procedure;
+|new_trie_op| could return |min_quarterword| (thereby simply ignoring
+part of a hyphenation pattern) instead of aborting the job. However, that would
+lead to different hyphenation results on different installations of \TeX\
+using the same patterns. The |overflow| stops are necessary for portability
+of patterns.
+
+@<Declare procedures for preprocessing hyph...@>=
+static quarterword new_trie_op(small_number @!d, small_number @!n, quarterword @!v)
+{@+
+int h; /*trial hash location*/
+quarterword @!u; /*trial op code*/
+int @!l; /*pointer to stored data*/
+h=abs(n+313*d+361*v+1009*cur_lang)%(trie_op_size+trie_op_size)
+ -trie_op_size;
+loop@+{@+l=trie_op_hash[h];
+ if (l==0) /*empty position found for a new op*/
+ {@+if (trie_op_ptr==trie_op_size)
+ overflow("pattern memory ops", trie_op_size);
+ u=trie_used[cur_lang];
+ if (u==max_quarterword)
+ overflow("pattern memory ops per language",
+ max_quarterword-min_quarterword);
+ incr(trie_op_ptr);incr(u);trie_used[cur_lang]=u;
+ hyf_distance[trie_op_ptr]=d;
+ hyf_num[trie_op_ptr]=n;hyf_next[trie_op_ptr]=v;
+ trie_op_lang[trie_op_ptr]=cur_lang;trie_op_hash[h]=trie_op_ptr;
+ trie_op_val[trie_op_ptr]=u;return u;
+ }
+ if ((hyf_distance[l]==d)&&(hyf_num[l]==n)&&(hyf_next[l]==v)
+ &&(trie_op_lang[l]==cur_lang))
+ {@+return trie_op_val[l];
+ }
+ if (h > -trie_op_size) decr(h);@+else h=trie_op_size;
+ }
+}
+
+@ After |new_trie_op| has compressed the necessary opcode information,
+plenty of information is available to unscramble the data into the
+final form needed by our hyphenation algorithm.
+
+@<Sort \(t)the hyphenation op tables into proper order@>=
+op_start[0]=-min_quarterword;
+for (j=1; j<=255; j++) op_start[j]=op_start[j-1]+qo(trie_used[j-1]);
+for (j=1; j<=trie_op_ptr; j++)
+ trie_op_hash[j]=op_start[trie_op_lang[j]]+trie_op_val[j]; /*destination*/
+for (j=1; j<=trie_op_ptr; j++) while (trie_op_hash[j] > j)
+ {@+k=trie_op_hash[j];@/
+ t=hyf_distance[k];hyf_distance[k]=hyf_distance[j];hyf_distance[j]=t;@/
+ t=hyf_num[k];hyf_num[k]=hyf_num[j];hyf_num[j]=t;@/
+ t=hyf_next[k];hyf_next[k]=hyf_next[j];hyf_next[j]=t;@/
+ trie_op_hash[j]=trie_op_hash[k];trie_op_hash[k]=k;
+ }
+
+@ Before we forget how to initialize the data structures that have been
+mentioned so far, let's write down the code that gets them started.
+
+@<Initialize table entries...@>=
+for (k=-trie_op_size; k<=trie_op_size; k++) trie_op_hash[k]=0;
+for (k=0; k<=255; k++) trie_used[k]=min_quarterword;
+trie_op_ptr=0;
+
+@ The linked trie that is used to preprocess hyphenation patterns appears
+in several global arrays. Each node represents an instruction of the form
+``if you see character |c|, then perform operation |o|, move to the
+next character, and go to node |l|; otherwise go to node |r|.''
+The four quantities |c|, |o|, |l|, and |r| are stored in four arrays
+|trie_c|, |trie_o|, |trie_l|, and |trie_r|. The root of the trie
+is |trie_l[0]|, and the number of nodes is |trie_ptr|. Null trie
+pointers are represented by zero. To initialize the trie, we simply
+set |trie_l[0]| and |trie_ptr| to zero. We also set |trie_c[0]| to some
+arbitrary value, since the algorithm may access it.
+
+The algorithms maintain the condition
+$$\hbox{|trie_c[trie_r[z]] > trie_c[z]|\qquad
+whenever |z!=0| and |trie_r[z]!=0|};$$ in other words, sibling nodes are
+ordered by their |c| fields.
+
+@d trie_root trie_l[0] /*root of the linked trie*/
+
+@<Glob...@>=
+#ifdef @!INIT
+static packed_ASCII_code @!trie_c[trie_size+1];
+ /*characters to match*/
+@t\hskip10pt@>static quarterword @!trie_o[trie_size+1];
+ /*operations to perform*/
+@t\hskip10pt@>static trie_pointer @!trie_l[trie_size+1];
+ /*left subtrie links*/
+@t\hskip10pt@>static trie_pointer @!trie_r[trie_size+1];
+ /*right subtrie links*/
+@t\hskip10pt@>static trie_pointer @!trie_ptr; /*the number of nodes in the trie*/
+@t\hskip10pt@>static trie_pointer @!trie_hash[trie_size+1];
+ /*used to identify equivalent subtries*/
+#endif
+
+@ Let us suppose that a linked trie has already been constructed.
+Experience shows that we can often reduce its size by recognizing common
+subtries; therefore another hash table is introduced for this purpose,
+somewhat similar to |trie_op_hash|. The new hash table will be
+initialized to zero.
+
+The function |trie_node(p)| returns |p| if |p| is distinct from other nodes
+that it has seen, otherwise it returns the number of the first equivalent
+node that it has seen.
+
+Notice that we might make subtries equivalent even if they correspond to
+patterns for different languages, in which the trie ops might mean quite
+different things. That's perfectly all right.
+
+@<Declare procedures for preprocessing hyph...@>=
+static trie_pointer trie_node(trie_pointer @!p) /*converts
+ to a canonical form*/
+{@+
+trie_pointer h; /*trial hash location*/
+trie_pointer @!q; /*trial trie node*/
+h=abs(trie_c[p]+1009*trie_o[p]+@|
+ 2718*trie_l[p]+3142*trie_r[p])%trie_size;
+loop@+{@+q=trie_hash[h];
+ if (q==0)
+ {@+trie_hash[h]=p;return p;
+ }
+ if ((trie_c[q]==trie_c[p])&&(trie_o[q]==trie_o[p])&&@|
+ (trie_l[q]==trie_l[p])&&(trie_r[q]==trie_r[p]))
+ {@+return q;
+ }
+ if (h > 0) decr(h);@+else h=trie_size;
+ }
+}
+
+@ A neat recursive procedure is now able to compress a trie by
+traversing it and applying |trie_node| to its nodes in ``bottom up''
+fashion. We will compress the entire trie by clearing |trie_hash| to
+zero and then saying `|trie_root=compress_trie(trie_root)|'.
+@^recursion@>
+
+@<Declare procedures for preprocessing hyph...@>=
+static trie_pointer compress_trie(trie_pointer @!p)
+{@+if (p==0) return 0;
+else{@+trie_l[p]=compress_trie(trie_l[p]);
+ trie_r[p]=compress_trie(trie_r[p]);
+ return trie_node(p);
+ }
+}
+
+@ The compressed trie will be packed into the |trie| array using a
+``top-down first-fit'' procedure. This is a little tricky, so the reader
+should pay close attention: The |trie_hash| array is cleared to zero
+again and renamed |trie_ref| for this phase of the operation; later on,
+|trie_ref[p]| will be nonzero only if the linked trie node |p| is the
+smallest character
+in a family and if the characters |c| of that family have been allocated to
+locations |trie_ref[p]+c| in the |trie| array. Locations of |trie| that
+are in use will have |trie_link==0|, while the unused holes in |trie|
+will be doubly linked with |trie_link| pointing to the next larger vacant
+location and |trie_back| pointing to the next smaller one. This double
+linking will have been carried out only as far as |trie_max|, where
+|trie_max| is the largest index of |trie| that will be needed.
+To save time at the low end of the trie, we maintain array entries
+|trie_min[c]| pointing to the smallest hole that is greater than~|c|.
+Another array |trie_taken| tells whether or not a given location is
+equal to |trie_ref[p]| for some |p|; this array is used to ensure that
+distinct nodes in the compressed trie will have distinct |trie_ref|
+entries.
+
+@d trie_ref trie_hash /*where linked trie families go into |trie|*/
+@d trie_back(A) trie[A].lh /*backward links in |trie| holes*/
+
+@<Glob...@>=
+#ifdef @!INIT
+static bool @!trie_taken0[trie_size],
+ *const @!trie_taken = @!trie_taken0-1;
+ /*does a family start here?*/
+@t\hskip10pt@>static trie_pointer @!trie_min[256];
+ /*the first possible slot for each character*/
+@t\hskip10pt@>static trie_pointer @!trie_max; /*largest location used in |trie|*/
+@t\hskip10pt@>static bool @!trie_not_ready; /*is the trie still in linked form?*/
+#endif
+
+@ Each time \.{\\patterns} appears, it contributes further patterns to
+the future trie, which will be built only when hyphenation is attempted or
+when a format file is dumped. The boolean variable |trie_not_ready|
+will change to |false| when the trie is compressed; this will disable
+further patterns.
+
+@<Initialize table entries...@>=
+trie_not_ready=true;trie_root=0;trie_c[0]=si(0);trie_ptr=0;
+
+@ Here is how the trie-compression data structures are initialized.
+If storage is tight, it would be possible to overlap |trie_op_hash|,
+|trie_op_lang|, and |trie_op_val| with |trie|, |trie_hash|, and |trie_taken|,
+because we finish with the former just before we need the latter.
+
+@<Get ready to compress the trie@>=
+@<Sort \(t)the hyphenation...@>;
+for (p=0; p<=trie_size; p++) trie_hash[p]=0;
+hyph_root=compress_trie(hyph_root);
+trie_root=compress_trie(trie_root); /*identify equivalent subtries*/
+for (p=0; p<=trie_ptr; p++) trie_ref[p]=0;
+for (p=0; p<=255; p++) trie_min[p]=p+1;
+trie_link(0)=1;trie_max=0
+
+@ The |first_fit| procedure finds the smallest hole |z| in |trie| such that
+a trie family starting at a given node |p| will fit into vacant positions
+starting at |z|. If |c==trie_c[p]|, this means that location |z-c| must
+not already be taken by some other family, and that |z-c+@t$c^\prime$@>|
+must be vacant for all characters $c^\prime$ in the family. The procedure
+sets |trie_ref[p]| to |z-c| when the first fit has been found.
+
+@<Declare procedures for preprocessing hyph...@>=
+static void first_fit(trie_pointer @!p) /*packs a family into |trie|*/
+{@+
+trie_pointer h; /*candidate for |trie_ref[p]|*/
+trie_pointer @!z; /*runs through holes*/
+trie_pointer @!q; /*runs through the family starting at |p|*/
+ASCII_code @!c; /*smallest character in the family*/
+trie_pointer @!l, @!r; /*left and right neighbors*/
+int @!ll; /*upper limit of |trie_min| updating*/
+c=so(trie_c[p]);
+z=trie_min[c]; /*get the first conceivably good hole*/
+loop@+{@+h=z-c;@/
+ @<Ensure that |trie_max>=h+256|@>;
+ if (trie_taken[h]) goto not_found;
+ @<If all characters of the family fit relative to |h|, then |goto found|,\30\
+otherwise |goto not_found|@>;
+ not_found: z=trie_link(z); /*move to the next hole*/
+ }
+found: @<Pack the family into |trie| relative to |h|@>;
+}
+
+@ By making sure that |trie_max| is at least |h+256|, we can be sure that
+|trie_max > z|, since |h==z-c|. It follows that location |trie_max| will
+never be occupied in |trie|, and we will have |trie_max >= trie_link(z)|.
+
+@<Ensure that |trie_max>=h+256|@>=
+if (trie_max < h+256)
+ {@+if (trie_size <= h+256) overflow("pattern memory", trie_size);
+@:TeX capacity exceeded pattern memory}{\quad pattern memory@>
+ @/do@+{incr(trie_max);trie_taken[trie_max]=false;
+ trie_link(trie_max)=trie_max+1;trie_back(trie_max)=trie_max-1;
+ }@+ while (!(trie_max==h+256));
+ }
+
+@ @<If all characters of the family fit relative to |h|...@>=
+q=trie_r[p];
+while (q > 0)
+ {@+if (trie_link(h+so(trie_c[q]))==0) goto not_found;
+ q=trie_r[q];
+ }
+goto found
+
+@ @<Pack the family into |trie| relative to |h|@>=
+trie_taken[h]=true;trie_ref[p]=h;q=p;
+@/do@+{z=h+so(trie_c[q]);l=trie_back(z);r=trie_link(z);
+trie_back(r)=l;trie_link(l)=r;trie_link(z)=0;
+if (l < 256)
+ {@+if (z < 256) ll=z;@+else ll=256;
+ @/do@+{trie_min[l]=r;incr(l);
+ }@+ while (!(l==ll));
+ }
+q=trie_r[q];
+}@+ while (!(q==0))
+
+@ To pack the entire linked trie, we use the following recursive procedure.
+@^recursion@>
+
+@<Declare procedures for preprocessing hyph...@>=
+static void trie_pack(trie_pointer @!p) /*pack subtries of a family*/
+{@+trie_pointer q; /*a local variable that need not be saved on recursive calls*/
+@/do@+{q=trie_l[p];
+if ((q > 0)&&(trie_ref[q]==0))
+ {@+first_fit(q);trie_pack(q);
+ }
+p=trie_r[p];
+}@+ while (!(p==0));
+}
+
+@ When the whole trie has been allocated into the sequential table, we
+must go through it once again so that |trie| contains the correct
+information. Null pointers in the linked trie will be represented by the
+value~0, which properly implements an ``empty'' family.
+
+@<Move the data into |trie|@>=
+h.rh=0;h.b0=min_quarterword;h.b1=min_quarterword; /*|trie_link=0|,
+ |trie_op=min_quarterword|, |trie_char=qi(0)|*/
+if (trie_max==0) /*no patterns were given*/
+ {@+for (r=0; r<=256; r++) trie[r]=h;
+ trie_max=256;
+ }
+else{@+if (hyph_root > 0) trie_fix(hyph_root);
+ if (trie_root > 0) trie_fix(trie_root); /*this fixes the non-holes in |trie|*/
+ r=0; /*now we will zero out all the holes*/
+ @/do@+{s=trie_link(r);trie[r]=h;r=s;
+ }@+ while (!(r > trie_max));
+ }
+trie_char(0)=qi('?'); /*make |trie_char(c)!=c| for all |c|*/
+
+@ The fixing-up procedure is, of course, recursive. Since the linked trie
+usually has overlapping subtries, the same data may be moved several
+times; but that causes no harm, and at most as much work is done as it
+took to build the uncompressed trie.
+@^recursion@>
+
+@<Declare procedures for preprocessing hyph...@>=
+static void trie_fix(trie_pointer @!p) /*moves |p| and its siblings into |trie|*/
+{@+trie_pointer q; /*a local variable that need not be saved on recursive calls*/
+ASCII_code @!c; /*another one that need not be saved*/
+trie_pointer @!z; /*|trie| reference; this local variable must be saved*/
+z=trie_ref[p];
+@/do@+{q=trie_l[p];c=so(trie_c[p]);
+trie_link(z+c)=trie_ref[q];trie_char(z+c)=qi(c);trie_op(z+c)=trie_o[p];
+if (q > 0) trie_fix(q);
+p=trie_r[p];
+}@+ while (!(p==0));
+}
+
+@ Now let's go back to the easier problem, of building the linked
+trie. When \.{INITEX} has scanned the `\.{\\patterns}' control
+sequence, it calls on |new_patterns| to do the right thing.
+
+@<Declare procedures for preprocessing hyph...@>=
+static void new_patterns(void) /*initializes the hyphenation pattern data*/
+{@+
+int k, @!l; /*indices into |hc| and |hyf|;
+ not always in |small_number| range*/
+bool @!digit_sensed; /*should the next digit be treated as a letter?*/
+quarterword @!v; /*trie op code*/
+trie_pointer @!p, @!q; /*nodes of trie traversed during insertion*/
+bool @!first_child; /*is |p==trie_l[q]|?*/
+int @!c; /*character being inserted*/
+if (trie_not_ready)
+ {@+set_cur_lang;scan_left_brace(); /*a left brace must follow \.{\\patterns}*/
+ @<Enter all of the patterns into a linked trie, until coming to a right
+brace@>;
+ if (saving_hyph_codes > 0)
+ @<Store hyphenation codes for current language@>;
+ }
+else{@+print_err("Too late for ");print_esc("patterns");
+ help1("All patterns must be given before typesetting begins.");
+ error();link(garbage)=scan_toks(false, false);flush_list(def_ref);
+ }
+}
+
+@ Novices are not supposed to be using \.{\\patterns}, so the error
+messages are terse. (Note that all error messages appear in \TeX's string
+pool, even if they are used only by \.{INITEX}.)
+
+@<Enter all of the patterns into a linked trie...@>=
+k=0;hyf[0]=0;digit_sensed=false;
+loop@+{@+get_x_token();
+ switch (cur_cmd) {
+ case letter: case other_char: @<Append a new letter or a hyphen level@>@;@+break;
+ case spacer: case right_brace: {@+if (k > 0)
+ @<Insert a new pattern into the linked trie@>;
+ if (cur_cmd==right_brace) goto done;
+ k=0;hyf[0]=0;digit_sensed=false;
+ } @+break;
+ default:{@+print_err("Bad ");print_esc("patterns");
+@.Bad \\patterns@>
+ help1("(See Appendix H.)");error();
+ }
+ }
+ }
+done:
+
+@ @<Append a new letter or a hyphen level@>=
+if (digit_sensed||(cur_chr < '0')||(cur_chr > '9'))
+ {@+if (cur_chr=='.') cur_chr=0; /*edge-of-word delimiter*/
+ else{@+cur_chr=lc_code(cur_chr);
+ if (cur_chr==0)
+ {@+print_err("Nonletter");
+@.Nonletter@>
+ help1("(See Appendix H.)");error();
+ }
+ }
+ if (k < 63)
+ {@+incr(k);hc[k]=cur_chr;hyf[k]=0;digit_sensed=false;
+ }
+ }
+else if (k < 63)
+ {@+hyf[k]=cur_chr-'0';digit_sensed=true;
+ }
+
+@ When the following code comes into play, the pattern $p_1\ldots p_k$
+appears in |hc[1 dotdot k]|, and the corresponding sequence of numbers $n_0\ldots
+n_k$ appears in |hyf[0 dotdot k]|.
+
+@<Insert a new pattern into the linked trie@>=
+{@+@<Compute the trie op code, |v|, and set |l:=0|@>;
+q=0;hc[0]=cur_lang;
+while (l <= k)
+ {@+c=hc[l];incr(l);p=trie_l[q];first_child=true;
+ while ((p > 0)&&(c > so(trie_c[p])))
+ {@+q=p;p=trie_r[q];first_child=false;
+ }
+ if ((p==0)||(c < so(trie_c[p])))
+ @<Insert a new trie node between |q| and |p|, and make |p| point to it@>;
+ q=p; /*now node |q| represents $p_1\ldots p_{l-1}$*/
+ }
+if (trie_o[q]!=min_quarterword)
+ {@+print_err("Duplicate pattern");
+@.Duplicate pattern@>
+ help1("(See Appendix H.)");error();
+ }
+trie_o[q]=v;
+}
+
+@ @<Insert a new trie node between |q| and |p|...@>=
+{@+if (trie_ptr==trie_size) overflow("pattern memory", trie_size);
+@:TeX capacity exceeded pattern memory}{\quad pattern memory@>
+incr(trie_ptr);trie_r[trie_ptr]=p;p=trie_ptr;trie_l[p]=0;
+if (first_child) trie_l[q]=p;@+else trie_r[q]=p;
+trie_c[p]=si(c);trie_o[p]=min_quarterword;
+}
+
+@ @<Compute the trie op code, |v|...@>=
+if (hc[1]==0) hyf[0]=0;
+if (hc[k]==0) hyf[k]=0;
+l=k;v=min_quarterword;
+loop@+{@+if (hyf[l]!=0) v=new_trie_op(k-l, hyf[l], v);
+ if (l > 0) decr(l);@+else goto done1;
+ }
+done1:
+
+@ Finally we put everything together: Here is how the trie gets to its
+final, efficient form.
+The following packing routine is rigged so that the root of the linked
+tree gets mapped into location 1 of |trie|, as required by the hyphenation
+algorithm. This happens because the first call of |first_fit| will
+``take'' location~1.
+
+@<Declare procedures for preprocessing hyphenation patterns@>=
+static void init_trie(void)
+{@+int @!p; /*pointer for initialization*/
+int @!j, @!k, @!t; /*all-purpose registers for initialization*/
+int @!r, @!s; /*used to clean up the packed |trie|*/
+two_halves @!h; /*template used to zero out |trie|'s holes*/
+@<Local variables to save the profiling context@>@;
+@<Charge the time used here on |init_trie|@>@;
+@<Get ready to compress the trie@>;
+if (trie_root!=0)
+ {@+first_fit(trie_root);trie_pack(trie_root);
+ }
+if (hyph_root!=0) @<Pack all stored |hyph_codes|@>;
+@<Move the data into |trie|@>;
+trie_not_ready=false;
+@<restore the previous current file, line, and command@>@;
+}
+
+@* Breaking vertical lists into pages.
+The |vsplit| procedure, which implements \TeX's \.{\\vsplit} operation,
+is considerably simpler than |line_break| because it doesn't have to
+worry about hyphenation, and because its mission is to discover a single
+break instead of an optimum sequence of breakpoints. But before we get
+into the details of |vsplit|, we need to consider a few more basic things.
+
+@ A subroutine called |prune_page_top| takes a pointer to a vlist and
+returns a pointer to a modified vlist in which all glue, kern, and penalty nodes
+have been deleted before the first box or rule node. However, the first
+box or rule is actually preceded by a newly created glue node designed so that
+the topmost baseline will be at distance |split_top_skip| from the top,
+whenever this is possible without backspacing.
+
+When the second argument |s| is |false| the deleted nodes are destroyed,
+otherwise they are collected in a list starting at |split_disc|.
+
+In this routine and those that follow, we make use of the fact that a
+vertical list contains no character nodes, hence the |type| field exists
+for each node in the list.
+@^data structure assumptions@>
+
+@p static pointer prune_page_top(pointer @!p, bool @!s)
+ /*adjust top after page break*/
+{@+pointer prev_p; /*lags one step behind |p|*/
+pointer @!q, @!r; /*temporary variables for list manipulation*/
+prev_p=temp_head;link(temp_head)=p;
+while (p!=null)
+ switch (type(p)) {
+ case hlist_node: case vlist_node: case rule_node: @<Insert glue for |split_top_skip|
+and set~|p:=null|@>@;@+break;
+ case whatsit_node: case mark_node: case ins_node: {@+prev_p=p;p=link(prev_p);
+ } @+break;
+ case glue_node: case kern_node: case penalty_node: {@+q=p;p=link(q);link(q)=null;
+ link(prev_p)=p;
+ if (s)
+ {@+if (split_disc==null) split_disc=q;@+else link(r)=q;
+ r=q;
+ }
+ else flush_node_list(q);
+ } @+break;
+ default:confusion("pruning");
+@:this can't happen pruning}{\quad pruning@>
+ }
+return link(temp_head);
+}
+
+@ @<Insert glue for |split_top_skip|...@>=
+{@+q=new_skip_param(split_top_skip_code);link(prev_p)=q;link(q)=p;
+ /*now |temp_ptr==glue_ptr(q)|*/
+if (width(temp_ptr) > height(p)) width(temp_ptr)=width(temp_ptr)-height(p);
+else width(temp_ptr)=0;
+p=null;
+}
+
+@ The next subroutine finds the best place to break a given vertical list
+so as to obtain a box of height~|h|, with maximum depth~|d|.
+A pointer to the beginning of the vertical list is given,
+and a pointer to the optimum breakpoint is returned. The list is effectively
+followed by a forced break, i.e., a penalty node with the |eject_penalty|;
+if the best break occurs at this artificial node, the value |null| is returned.
+
+An array of six |scaled| distances is used to keep track of the height
+from the beginning of the list to the current place, just as in |line_break|.
+In fact, we use one of the same arrays, only changing its name to reflect
+its new significance.
+
+@d active_height active_width /*new name for the six distance variables*/
+@d cur_height active_height[1] /*the natural height*/
+@d set_height_zero(A) active_height[A]=0 /*initialize the height to zero*/
+@#
+@p static pointer vert_break(pointer @!p, scaled @!h, scaled @!d)
+ /*finds optimum page break*/
+{@+
+pointer prev_p; /*if |p| is a glue node, |type(prev_p)| determines
+ whether |p| is a legal breakpoint*/
+pointer @!q, @!r; /*glue specifications*/
+int @!pi; /*penalty value*/
+int @!b; /*badness at a trial breakpoint*/
+int @!least_cost; /*the smallest badness plus penalties found so far*/
+pointer @!best_place; /*the most recent break that leads to |least_cost|*/
+scaled @!prev_dp; /*depth of previous box in the list*/
+small_number @!t; /*|type| of the node following a kern*/
+prev_p=p; /*an initial glue node is not a legal breakpoint*/
+least_cost=awful_bad;do_all_six(set_height_zero);prev_dp=0;
+loop@+{@+@<If node |p| is a legal breakpoint, check if this break is the best
+known, and |goto done| if |p| is null or if the page-so-far is already too
+full to accept more stuff@>;
+ prev_p=p;p=link(prev_p);
+ }
+done: return best_place;
+}
+
+@ A global variable |best_height_plus_depth| will be set to the natural size
+of the box that corresponds to the optimum breakpoint found by |vert_break|.
+(This value is used by the insertion-splitting algorithm of the page builder.)
+
+@<Glob...@>=
+static scaled @!best_height_plus_depth; /*height of the best box, without stretching or
+ shrinking*/
+
+@ A subtle point to be noted here is that the maximum depth~|d| might be
+negative, so |cur_height| and |prev_dp| might need to be corrected even
+after a glue or kern node.
+
+@<If node |p| is a legal breakpoint, check...@>=
+if (p==null) pi=eject_penalty;
+else@<Use node |p| to update the current height and depth measurements; if
+this node is not a legal breakpoint, |goto not_found| or |update_heights|,
+otherwise set |pi| to the associated penalty at the break@>;
+@<Check if node |p| is a new champion breakpoint; then \(go)|goto done| if
+|p| is a forced break or if the page-so-far is already too full@>;
+if ((type(p) < glue_node)||(type(p) > kern_node)) goto not_found;
+update_heights: @<Update the current height and depth measurements with respect
+to a glue or kern node~|p|@>;
+not_found: if (prev_dp > d)
+ {@+cur_height=cur_height+prev_dp-d;
+ prev_dp=d;
+ }
+
+@ @<Use node |p| to update the current height and depth measurements...@>=
+switch (type(p)) {
+case hlist_node: case vlist_node: case rule_node: {@+@t@>@;@/
+ cur_height=cur_height+prev_dp+height(p);prev_dp=depth(p);
+ goto not_found;
+ }
+case whatsit_node: @<Process whatsit |p| in |vert_break| loop, |goto not_found|@>;
+case glue_node: if (precedes_break(prev_p)) pi=0;
+ else goto update_heights;@+break;
+case kern_node: {@+if (link(p)==null) t=penalty_node;
+ else t=type(link(p));
+ if (t==glue_node) pi=0;@+else goto update_heights;
+ } @+break;
+case penalty_node: pi=penalty(p);@+break;
+case mark_node: case ins_node: goto not_found;
+default:confusion("vertbreak");
+@:this can't happen vertbreak}{\quad vertbreak@>
+}
+
+@ @d deplorable 100000 /*more than |inf_bad|, but less than |awful_bad|*/
+
+@<Check if node |p| is a new champion breakpoint; then \(go)...@>=
+if (pi < inf_penalty)
+ {@+@<Compute the badness, |b|, using |awful_bad| if the box is too full@>;
+ if (b < awful_bad)
+ if (pi <= eject_penalty) b=pi;
+ else if (b < inf_bad) b=b+pi;
+ else b=deplorable;
+ if (b <= least_cost)
+ {@+best_place=p;least_cost=b;
+ best_height_plus_depth=cur_height+prev_dp;
+ }
+ if ((b==awful_bad)||(pi <= eject_penalty)) goto done;
+ }
+
+@ @<Compute the badness, |b|, using |awful_bad| if the box is too full@>=
+if (cur_height < h)
+ if ((active_height[3]!=0)||(active_height[4]!=0)||
+ (active_height[5]!=0)) b=0;
+ else b=badness(h-cur_height, active_height[2]);
+else if (cur_height-h > active_height[6]) b=awful_bad;
+else b=badness(cur_height-h, active_height[6])
+
+@ Vertical lists that are subject to the |vert_break| procedure should not
+contain infinite shrinkability, since that would permit any amount of
+information to ``fit'' on one page.
+
+@<Update the current height and depth measurements with...@>=
+if (type(p)==kern_node) q=p;
+else{@+q=glue_ptr(p);
+ active_height[2+stretch_order(q)]=@|
+ active_height[2+stretch_order(q)]+stretch(q);@/
+ active_height[6]=active_height[6]+shrink(q);
+ if ((shrink_order(q)!=normal)&&(shrink(q)!=0))
+ {@+@t@>@;@/
+ print_err("Infinite glue shrinkage found in box being split");@/
+@.Infinite glue shrinkage...@>
+ help4("The box you are \\vsplitting contains some infinitely",@/
+ "shrinkable glue, e.g., `\\vss' or `\\vskip 0pt minus 1fil'.",@/
+ "Such glue doesn't belong there; but you can safely proceed,",@/
+ "since the offensive shrinkability has been made finite.");
+ error();r=new_spec(q);shrink_order(r)=normal;delete_glue_ref(q);
+ glue_ptr(p)=r;q=r;
+ }
+ }
+cur_height=cur_height+prev_dp+width(q);prev_dp=0
+
+@ Now we are ready to consider |vsplit| itself. Most of
+its work is accomplished by the two subroutines that we have just considered.
+
+Given the number of a vlist box |n|, and given a desired page height |h|,
+the |vsplit| function finds the best initial segment of the vlist and
+returns a box for a page of height~|h|. The remainder of the vlist, if
+any, replaces the original box, after removing glue and penalties and
+adjusting for |split_top_skip|. Mark nodes in the split-off box are used to
+set the values of |split_first_mark| and |split_bot_mark|; we use the
+fact that |split_first_mark==null| if and only if |split_bot_mark==null|.
+
+The original box becomes ``void'' if and only if it has been entirely
+extracted. The extracted box is ``void'' if and only if the original
+box was void (or if it was, erroneously, an hlist box).
+
+@p @t\4@>@<Declare the function called |do_marks|@>@;
+static pointer vsplit(halfword @!n, scaled @!h)
+ /*extracts a page of height |h| from box |n|*/
+{@+
+pointer v; /*the box to be split*/
+pointer p; /*runs through the vlist*/
+pointer q; /*points to where the break occurs*/
+cur_val=n;fetch_box(v);
+flush_node_list(split_disc);split_disc=null;
+if (sa_mark!=null)
+ if (do_marks(vsplit_init, 0, sa_mark)) sa_mark=null;
+if (split_first_mark!=null)
+ {@+delete_token_ref(split_first_mark);split_first_mark=null;
+ delete_token_ref(split_bot_mark);split_bot_mark=null;
+ }
+@<Dispense with trivial cases of void or bad boxes@>;
+q=vert_break(list_ptr(v), h, split_max_depth);
+@<Look at all the marks in nodes before the break, and set the final link
+to |null| at the break@>;
+q=prune_page_top(q, saving_vdiscards > 0);
+p=list_ptr(v);free_node(v, box_node_size);
+if (q!=null) q=vpack(q, natural);
+change_box(q); /*the |eq_level| of the box stays the same*/
+return vpackage(p, h, exactly, split_max_depth);
+}
+
+@ @<Dispense with trivial cases of void or bad boxes@>=
+if (v==null)
+ {@+return null;
+ }
+if (type(v)!=vlist_node)
+ {@+print_err("");print_esc("vsplit");print(" needs a ");
+ print_esc("vbox");
+@:vsplit\_}{\.{\\vsplit needs a \\vbox}@>
+ help2("The box you are trying to split is an \\hbox.",@/
+ "I can't split such a box, so I'll leave it alone.");
+ error();return null;
+ }
+
+@ It's possible that the box begins with a penalty node that is the
+``best'' break, so we must be careful to handle this special case correctly.
+
+@<Look at all the marks...@>=
+p=list_ptr(v);
+if (p==q) list_ptr(v)=null;
+else loop@+{@+if (type(p)==mark_node)
+ if (mark_class(p)!=0) @<Update the current marks for |vsplit|@>@;
+ else if (split_first_mark==null)
+ {@+split_first_mark=mark_ptr(p);
+ split_bot_mark=split_first_mark;
+ token_ref_count(split_first_mark)=@|
+ token_ref_count(split_first_mark)+2;
+ }
+ else{@+delete_token_ref(split_bot_mark);
+ split_bot_mark=mark_ptr(p);
+ add_token_ref(split_bot_mark);
+ }
+ if (link(p)==q)
+ {@+link(p)=null;goto done;
+ }
+ p=link(p);
+ }
+done:
+
+@* The page builder.
+When \TeX\ appends new material to its main vlist in vertical mode, it uses
+a method something like |vsplit| to decide where a page ends, except that
+the calculations are done ``on line'' as new items come in.
+The main complication in this process is that insertions must be put
+into their boxes and removed from the vlist, in a more-or-less optimum manner.
+
+We shall use the term ``current page'' for that part of the main vlist that
+is being considered as a candidate for being broken off and sent to the
+user's output routine. The current page starts at |link(page_head)|, and
+it ends at |page_tail|. We have |page_head==page_tail| if this list is empty.
+@^current page@>
+
+Utter chaos would reign if the user kept changing page specifications
+while a page is being constructed, so the page builder keeps the pertinent
+specifications frozen as soon as the page receives its first box or
+insertion. The global variable |page_contents| is |empty| when the
+current page contains only mark nodes and content-less whatsit nodes; it
+is |inserts_only| if the page contains only insertion nodes in addition to
+marks and whatsits. Glue nodes, kern nodes, and penalty nodes are
+discarded until a box or rule node appears, at which time |page_contents|
+changes to |box_there|. As soon as |page_contents| becomes non-|empty|,
+the current |vsize| and |max_depth| are squirreled away into |page_goal|
+and |page_max_depth|; the latter values will be used until the page has
+been forwarded to the user's output routine. The \.{\\topskip} adjustment
+is made when |page_contents| changes to |box_there|.
+
+Although |page_goal| starts out equal to |vsize|, it is decreased by the
+scaled natural height-plus-depth of the insertions considered so far, and by
+the \.{\\skip} corrections for those insertions. Therefore it represents
+the size into which the non-inserted material should fit, assuming that
+all insertions in the current page have been made.
+
+The global variables |best_page_break| and |least_page_cost| correspond
+respectively to the local variables |best_place| and |least_cost| in the
+|vert_break| routine that we have already studied; i.e., they record the
+location and value of the best place currently known for breaking the
+current page. The value of |page_goal| at the time of the best break is
+stored in |best_size|.
+
+@d inserts_only 1
+ /*|page_contents| when an insert node has been contributed, but no boxes*/
+@d box_there 2 /*|page_contents| when a box or rule has been contributed*/
+
+@<Glob...@>=
+static pointer @!page_tail; /*the final node on the current page*/
+static int @!page_contents; /*what is on the current page so far?*/
+static scaled @!page_max_depth; /*maximum box depth on page being built*/
+static pointer @!best_page_break; /*break here to get the best page known so far*/
+static int @!least_page_cost; /*the score for this currently best page*/
+static scaled @!best_size; /*its |page_goal|*/
+
+@ The page builder has another data structure to keep track of insertions.
+This is a list of four-word nodes, starting and ending at |page_ins_head|.
+That is, the first element of the list is node |r@t$\_1$@>==link(page_ins_head)|;
+node $r_j$ is followed by |r@t$\_{j+1}$@>==link(r@t$\_j$@>)|; and if there are
+|n| items we have |r@t$\_{n+1}$@>==page_ins_head|. The |subtype| field of
+each node in this list refers to an insertion number; for example, `\.{\\insert
+250}' would correspond to a node whose |subtype| is |qi(250)|
+(the same as the |subtype| field of the relevant |ins_node|). These |subtype|
+fields are in increasing order, and |subtype(page_ins_head)==
+qi(255)|, so |page_ins_head| serves as a convenient sentinel
+at the end of the list. A record is present for each insertion number that
+appears in the current page.
+
+The |type| field in these nodes distinguishes two possibilities that
+might occur as we look ahead before deciding on the optimum page break.
+If |type(r)==inserting|, then |height(r)| contains the total of the
+height-plus-depth dimensions of the box and all its inserts seen so far.
+If |type(r)==split_up|, then no more insertions will be made into this box,
+because at least one previous insertion was too big to fit on the current
+page; |broken_ptr(r)| points to the node where that insertion will be
+split, if \TeX\ decides to split it, |broken_ins(r)| points to the
+insertion node that was tentatively split, and |height(r)| includes also the
+natural height plus depth of the part that would be split off.
+
+In both cases, |last_ins_ptr(r)| points to the last |ins_node|
+encountered for box |qo(subtype(r))| that would be at least partially
+inserted on the next page; and |best_ins_ptr(r)| points to the last
+such |ins_node| that should actually be inserted, to get the page with
+minimum badness among all page breaks considered so far. We have
+|best_ins_ptr(r)==null| if and only if no insertion for this box should
+be made to produce this optimum page.
+
+The data structure definitions here use the fact that the |@!height| field
+appears in the fourth word of a box node.
+@^data structure assumptions@>
+
+@d page_ins_node_size 4 /*number of words for a page insertion node*/
+@d inserting 0 /*an insertion class that has not yet overflowed*/
+@d split_up 1 /*an overflowed insertion class*/
+@d broken_ptr(A) link(A+1)
+ /*an insertion for this class will break here if anywhere*/
+@d broken_ins(A) info(A+1) /*this insertion might break at |broken_ptr|*/
+@d last_ins_ptr(A) link(A+2) /*the most recent insertion for this |subtype|*/
+@d best_ins_ptr(A) info(A+2) /*the optimum most recent insertion*/
+
+@<Initialize the special list heads...@>=
+subtype(page_ins_head)=qi(255);
+type(page_ins_head)=split_up;link(page_ins_head)=page_ins_head;
+
+@ An array |page_so_far| records the heights and depths of everything
+on the current page. This array contains six |scaled| numbers, like the
+similar arrays already considered in |line_break| and |vert_break|; and it
+also contains |page_goal| and |page_depth|, since these values are
+all accessible to the user via |set_page_dimen| commands. The
+value of |page_so_far[1]| is also called |page_total|. The stretch
+and shrink components of the \.{\\skip} corrections for each insertion are
+included in |page_so_far|, but the natural space components of these
+corrections are not, since they have been subtracted from |page_goal|.
+
+The variable |page_depth| records the depth of the current page; it has been
+adjusted so that it is at most |page_max_depth|. The variable
+|last_glue| points to the glue specification of the most recent node
+contributed from the contribution list, if this was a glue node; otherwise
+|last_glue==max_halfword|. (If the contribution list is nonempty,
+however, the value of |last_glue| is not necessarily accurate.)
+The variables |last_penalty|, |last_kern|, and |last_node_type|
+are similar. And
+finally, |insert_penalties| holds the sum of the penalties associated with
+all split and floating insertions.
+
+@d page_goal page_so_far[0] /*desired height of information on page being built*/
+@d page_total page_so_far[1] /*height of the current page*/
+@d page_shrink page_so_far[6] /*shrinkability of the current page*/
+@d page_depth page_so_far[7] /*depth of the current page*/
+
+@<Glob...@>=
+static scaled @!page_so_far[8]; /*height and glue of the current page*/
+static pointer @!last_glue; /*used to implement \.{\\lastskip}*/
+static int @!last_penalty; /*used to implement \.{\\lastpenalty}*/
+static scaled @!last_kern; /*used to implement \.{\\lastkern}*/
+static int @!last_node_type; /*used to implement \.{\\lastnodetype}*/
+static int @!insert_penalties; /*sum of the penalties for insertions
+ that were held over*/
+
+@ @<Put each...@>=
+primitive("pagegoal", set_page_dimen, 0);
+@!@:page\_goal\_}{\.{\\pagegoal} primitive@>
+primitive("pagetotal", set_page_dimen, 1);
+@!@:page\_total\_}{\.{\\pagetotal} primitive@>
+primitive("pagestretch", set_page_dimen, 2);
+@!@:page\_stretch\_}{\.{\\pagestretch} primitive@>
+primitive("pagefilstretch", set_page_dimen, 3);
+@!@:page\_fil\_stretch\_}{\.{\\pagefilstretch} primitive@>
+primitive("pagefillstretch", set_page_dimen, 4);
+@!@:page\_fill\_stretch\_}{\.{\\pagefillstretch} primitive@>
+primitive("pagefilllstretch", set_page_dimen, 5);
+@!@:page\_filll\_stretch\_}{\.{\\pagefilllstretch} primitive@>
+primitive("pageshrink", set_page_dimen, 6);
+@!@:page\_shrink\_}{\.{\\pageshrink} primitive@>
+primitive("pagedepth", set_page_dimen, 7);
+@!@:page\_depth\_}{\.{\\pagedepth} primitive@>
+
+@ @<Cases of |print_cmd_chr|...@>=
+case set_page_dimen: switch (chr_code) {
+case 0: print_esc("pagegoal");@+break;
+case 1: print_esc("pagetotal");@+break;
+case 2: print_esc("pagestretch");@+break;
+case 3: print_esc("pagefilstretch");@+break;
+case 4: print_esc("pagefillstretch");@+break;
+case 5: print_esc("pagefilllstretch");@+break;
+case 6: print_esc("pageshrink");@+break;
+default:print_esc("pagedepth");
+} @+break;
+
+@ @d print_plus(A, B) if (page_so_far[A]!=0)
+ {@+print(" plus ");print_scaled(page_so_far[A]);print(B);@+}
+
+@p static void print_totals(void)
+{@+print_scaled(page_total);
+print_plus(2,"");
+print_plus(3,"fil");
+print_plus(4,"fill");
+print_plus(5,"filll");
+if (page_shrink!=0)
+ {@+print(" minus ");print_scaled(page_shrink);
+ }
+}
+
+@ @<Show the status of the current page@>=
+if (page_head!=page_tail)
+ {@+print_nl("### current page:");
+ if (output_active) print(" (held over for next output)");
+@.held over for next output@>
+ show_box(link(page_head));
+ if (page_contents > empty)
+ {@+print_nl("total height ");print_totals();
+@:total\_height}{\.{total height}@>
+ print_nl(" goal height ");print_scaled(page_goal);
+@.goal height@>
+ r=link(page_ins_head);
+ while (r!=page_ins_head)
+ {@+print_ln();print_esc("insert");t=qo(subtype(r));
+ print_int(t);print(" adds ");
+ if (count(t)==1000) t=height(r);
+ else t=x_over_n(height(r), 1000)*count(t);
+ print_scaled(t);
+ if (type(r)==split_up)
+ {@+q=page_head;t=0;
+ @/do@+{q=link(q);
+ if ((type(q)==ins_node)&&(subtype(q)==subtype(r))) incr(t);
+ }@+ while (!(q==broken_ins(r)));
+ print(", #");print_int(t);print(" might split");
+ }
+ r=link(r);
+ }
+ }
+ }
+
+@ Here is a procedure that is called when the |page_contents| is changing
+from |empty| to |inserts_only| or |box_there|.
+
+@d set_page_so_far_zero(A) page_so_far[A]=0
+
+@p static void freeze_page_specs(small_number @!s)
+{@+page_contents=s;
+page_goal=vsize;page_max_depth=max_depth;
+page_depth=0;do_all_six(set_page_so_far_zero);
+least_page_cost=awful_bad;
+#ifdef @!STAT
+if (tracing_pages > 0)
+ {@+begin_diagnostic();
+ print_nl("%% goal height=");print_scaled(page_goal);
+@.goal height@>
+ print(", max depth=");print_scaled(page_max_depth);
+ end_diagnostic(false);
+ } @;
+#endif
+@;@/
+}
+
+@ Pages are built by appending nodes to the current list in \TeX's
+vertical mode, which is at the outermost level of the semantic nest. This
+vlist is split into two parts; the ``current page'' that we have been
+talking so much about already, and the ``contribution list'' that receives
+new nodes as they are created. The current page contains everything that
+the page builder has accounted for in its data structures, as described
+above, while the contribution list contains other things that have been
+generated by other parts of \TeX\ but have not yet been
+seen by the page builder.
+The contribution list starts at |link(contrib_head)|, and it ends at the
+current node in \TeX's vertical mode.
+
+When \TeX\ has appended new material in vertical mode, it calls the procedure
+|build_page|, which tries to catch up by moving nodes from the contribution
+list to the current page. This procedure will succeed in its goal of
+emptying the contribution list, unless a page break is discovered, i.e.,
+unless the current page has grown to the point where the optimum next
+page break has been determined. In the latter case, the nodes after the
+optimum break will go back onto the contribution list, and control will
+effectively pass to the user's output routine.
+
+We make |type(page_head)==glue_node|, so that an initial glue node on
+the current page will not be considered a valid breakpoint.
+
+@<Initialize the special list...@>=
+type(page_head)=glue_node;subtype(page_head)=normal;
+
+@ The global variable |output_active| is true during the time the
+user's output routine is driving \TeX.
+
+@<Glob...@>=
+static bool @!output_active; /*are we in the midst of an output routine?*/
+
+@ @<Set init...@>=
+output_active=false;insert_penalties=0;
+
+@ The page builder is ready to start a fresh page if we initialize
+the following state variables. (However, the page insertion list is initialized
+elsewhere.)
+
+@<Start a new current page@>=
+page_contents=empty;page_tail=page_head;link(page_head)=null;@/
+last_glue=max_halfword;last_penalty=0;last_kern=0;
+last_node_type=-1;
+page_depth=0;page_max_depth=0
+
+@ At certain times box 255 is supposed to be void (i.e., |null|),
+or an insertion box is supposed to be ready to accept a vertical list.
+If not, an error message is printed, and the following subroutine
+flushes the unwanted contents, reporting them to the user.
+
+@p static void box_error(eight_bits @!n)
+{@+error();begin_diagnostic();
+print_nl("The following box has been deleted:");
+@.The following...deleted@>
+show_box(box(n));end_diagnostic(true);
+flush_node_list(box(n));box(n)=null;
+}
+
+@ The following procedure guarantees that a given box register
+does not contain an \.{\\hbox}.
+
+@p static void ensure_vbox(eight_bits @!n)
+{@+pointer p; /*the box register contents*/
+p=box(n);
+if (p!=null) if (type(p)==hlist_node)
+ {@+print_err("Insertions can only be added to a vbox");
+@.Insertions can only...@>
+ help3("Tut tut: You're trying to \\insert into a",@/
+ "\\box register that now contains an \\hbox.",@/
+ "Proceed, and I'll discard its present contents.");
+ box_error(n);
+ }
+}
+
+@ \TeX\ is not always in vertical mode at the time |build_page|
+is called; the current mode reflects what \TeX\ should return to, after
+the contribution list has been emptied. A call on |build_page| should
+be immediately followed by `|goto big_switch|', which is \TeX's central
+control point.
+
+@p @t\4@>@<Declare the procedure called |fire_up|@>@;@/
+static void build_page(void) /*append contributions to the current page*/
+{@+
+pointer p; /*the node being appended*/
+pointer @!q, @!r; /*nodes being examined*/
+int @!b, @!c; /*badness and cost of current page*/
+int @!pi; /*penalty to be added to the badness*/
+int @!n; /*insertion box number*/
+scaled @!delta, @!h, @!w; /*sizes used for insertion calculations*/
+@<Local variables to save the profiling context@>@;
+if ((link(contrib_head)==null)||output_active) return;
+@<Charge the time used here on |build_page|@>@;
+@/do@+{resume: p=link(contrib_head);@/
+@<Update the values of |last_glue|, |last_penalty|, and |last_kern|@>;
+@<Move node |p| to the current page; if it is time for a page break, put the
+nodes following the break back onto the contribution list, and |return| to
+the user's output routine if there is one@>;
+}@+ while (!(link(contrib_head)==null));
+@<Make the contribution list empty by setting its tail to |contrib_head|@>;
+@<restore the previous current file, line, and command@>@;
+}
+
+@ @d contrib_tail nest[0].tail_field /*tail of the contribution list*/
+
+@<Make the contribution list empty...@>=
+if (nest_ptr==0) tail=contrib_head; /*vertical mode*/
+else contrib_tail=contrib_head /*other modes*/
+
+@ @<Update the values of |last_glue|...@>=
+if (last_glue!=max_halfword) delete_glue_ref(last_glue);
+last_penalty=0;last_kern=0;
+last_node_type=type(p)+1;
+if (type(p)==glue_node)
+ {@+last_glue=glue_ptr(p);add_glue_ref(last_glue);
+ }
+else{@+last_glue=max_halfword;
+ if (type(p)==penalty_node) last_penalty=penalty(p);
+ else if (type(p)==kern_node) last_kern=width(p);
+ }
+
+@ The code here is an example of a many-way switch into routines that
+merge together in different places. Some people call this unstructured
+programming, but the author doesn't see much wrong with it, as long as
+@^Knuth, Donald Ervin@>
+the various labels have a well-understood meaning.
+
+@<Move node |p| to the current page;...@>=
+@<If the current page is empty and node |p| is to be deleted, |goto done1|;
+otherwise use node |p| to update the state of the current page; if this node
+is an insertion, |goto contribute|; otherwise if this node is not a legal
+breakpoint, |goto contribute| or |update_heights|; otherwise set |pi| to the
+penalty associated with this breakpoint@>;
+@<Check if node |p| is a new champion breakpoint; then \(if)if it is time
+for a page break, prepare for output, and either fire up the user's output
+routine and |return| or ship out the page and |goto done|@>;
+if ((type(p) < glue_node)||(type(p) > kern_node)) goto contribute;
+update_heights: @<Update the current page measurements with respect to the
+glue or kern specified by node~|p|@>;
+contribute: @<Make sure that |page_max_depth| is not exceeded@>;
+@<Link node |p| into the current page and |goto done|@>;
+done1: @<Recycle node |p|@>;
+done:
+
+@ @<Link node |p| into the current page and |goto done|@>=
+link(page_tail)=p;page_tail=p;
+link(contrib_head)=link(p);link(p)=null;goto done
+
+@ @<Recycle node |p|@>=
+link(contrib_head)=link(p);link(p)=null;
+if (saving_vdiscards > 0)
+ {@+if (page_disc==null) page_disc=p;@+else link(tail_page_disc)=p;
+ tail_page_disc=p;
+ }
+else flush_node_list(p)
+
+@ The title of this section is already so long, it seems best to avoid
+making it more accurate but still longer, by mentioning the fact that a
+kern node at the end of the contribution list will not be contributed until
+we know its successor.
+
+@<If the current page is empty...@>=
+switch (type(p)) {
+case hlist_node: case vlist_node: case rule_node: if (page_contents < box_there)
+ @<Initialize the current page, insert the \.{\\topskip} glue ahead of
+|p|, and |goto resume|@>@;
+ else@<Prepare to move a box or rule node to the current page, then |goto
+contribute|@>@;@+break;
+case whatsit_node: @<Prepare to move whatsit |p| to the current page, then
+|goto contribute|@>;
+case glue_node: if (page_contents < box_there) goto done1;
+ else if (precedes_break(page_tail)) pi=0;
+ else goto update_heights;@+break;
+case kern_node: if (page_contents < box_there) goto done1;
+ else if (link(p)==null) return;
+ else if (type(link(p))==glue_node) pi=0;
+ else goto update_heights;@+break;
+case penalty_node: if (page_contents < box_there) goto done1;@+else pi=penalty(p);@+break;
+case mark_node: goto contribute;
+case ins_node: @<Append an insertion to the current page and |goto contribute|@>@;
+default:confusion("page");
+@:this can't happen page}{\quad page@>
+}
+
+@ @<Initialize the current page, insert the \.{\\topskip} glue...@>=
+{@+if (page_contents==empty) freeze_page_specs(box_there);
+else page_contents=box_there;
+q=new_skip_param(top_skip_code); /*now |temp_ptr==glue_ptr(q)|*/
+if (width(temp_ptr) > height(p)) width(temp_ptr)=width(temp_ptr)-height(p);
+else width(temp_ptr)=0;
+link(q)=p;link(contrib_head)=q;goto resume;
+}
+
+@ @<Prepare to move a box or rule node to the current page...@>=
+{@+page_total=page_total+page_depth+height(p);
+page_depth=depth(p);
+goto contribute;
+}
+
+@ @<Make sure that |page_max_depth| is not exceeded@>=
+if (page_depth > page_max_depth)
+ {@+page_total=@|
+ page_total+page_depth-page_max_depth;@/
+ page_depth=page_max_depth;
+ }
+
+@ @<Update the current page measurements with respect to the glue...@>=
+if (type(p)==kern_node) q=p;
+else{@+q=glue_ptr(p);
+ page_so_far[2+stretch_order(q)]=@|
+ page_so_far[2+stretch_order(q)]+stretch(q);@/
+ page_shrink=page_shrink+shrink(q);
+ if ((shrink_order(q)!=normal)&&(shrink(q)!=0))
+ {@+@t@>@;@/
+ print_err("Infinite glue shrinkage found on current page");@/
+@.Infinite glue shrinkage...@>
+ help4("The page about to be output contains some infinitely",@/
+ "shrinkable glue, e.g., `\\vss' or `\\vskip 0pt minus 1fil'.",@/
+ "Such glue doesn't belong there; but you can safely proceed,",@/
+ "since the offensive shrinkability has been made finite.");
+ error();
+ r=new_spec(q);shrink_order(r)=normal;delete_glue_ref(q);
+ glue_ptr(p)=r;q=r;
+ }
+ }
+page_total=page_total+page_depth+width(q);page_depth=0
+
+@ @<Check if node |p| is a new champion breakpoint; then \(if)...@>=
+if (pi < inf_penalty)
+ {@+@<Compute the badness, |b|, of the current page, using |awful_bad| if
+the box is too full@>;
+ if (b < awful_bad)
+ if (pi <= eject_penalty) c=pi;
+ else if (b < inf_bad) c=b+pi+insert_penalties;
+ else c=deplorable;
+ else c=b;
+ if (insert_penalties >= 10000) c=awful_bad;
+#ifdef @!STAT
+ if (tracing_pages > 0) @<Display the page break cost@>;
+#endif
+@;@/
+ if (c <= least_page_cost)
+ {@+best_page_break=p;best_size=page_goal;
+ least_page_cost=c;
+ r=link(page_ins_head);
+ while (r!=page_ins_head)
+ {@+best_ins_ptr(r)=last_ins_ptr(r);
+ r=link(r);
+ }
+ }
+ if ((c==awful_bad)||(pi <= eject_penalty))
+ {@+fire_up(p); /*output the current page at the best place*/
+ if (output_active)
+ { @<restore the previous current file, line, and command@>@;
+ return; /*user's output routine will act*/
+ }
+ goto done; /*the page has been shipped out by default output routine*/
+ }
+ }
+
+@ @<Display the page break cost@>=
+{@+begin_diagnostic();print_nl("%");
+print(" t=");print_totals();@/
+print(" g=");print_scaled(page_goal);@/
+print(" b=");
+if (b==awful_bad) print_char('*');@+else print_int(b);
+@.*\relax@>
+print(" p=");print_int(pi);
+print(" c=");
+if (c==awful_bad) print_char('*');@+else print_int(c);
+if (c <= least_page_cost) print_char('#');
+end_diagnostic(false);
+}
+
+@ @<Compute the badness, |b|, of the current page...@>=
+if (page_total < page_goal)
+ if ((page_so_far[3]!=0)||(page_so_far[4]!=0)||@|
+ (page_so_far[5]!=0)) b=0;
+ else b=badness(page_goal-page_total, page_so_far[2]);
+else if (page_total-page_goal > page_shrink) b=awful_bad;
+else b=badness(page_total-page_goal, page_shrink)
+
+@ @<Append an insertion to the current page and |goto contribute|@>=
+{@+if (page_contents==empty) freeze_page_specs(inserts_only);
+n=subtype(p);r=page_ins_head;
+while (n >= subtype(link(r))) r=link(r);
+n=qo(n);
+if (subtype(r)!=qi(n))
+ @<Create a page insertion node with |subtype(r)=qi(n)|, and include the
+glue correction for box |n| in the current page state@>;
+if (type(r)==split_up) insert_penalties=insert_penalties+float_cost(p);
+else{@+last_ins_ptr(r)=p;
+ delta=page_goal-page_total-page_depth+page_shrink;
+ /*this much room is left if we shrink the maximum*/
+ if (count(n)==1000) h=height(p);
+ else h=x_over_n(height(p), 1000)*count(n); /*this much room is needed*/
+ if (((h <= 0)||(h <= delta))&&(height(p)+height(r) <= dimen(n)))
+ {@+page_goal=page_goal-h;height(r)=height(r)+height(p);
+ }
+ else@<Find the best way to split the insertion, and change |type(r)| to
+|split_up|@>;
+ }
+goto contribute;
+}
+
+@ We take note of the value of \.{\\skip} |n| and the height plus depth
+of \.{\\box}~|n| only when the first \.{\\insert}~|n| node is
+encountered for a new page. A user who changes the contents of \.{\\box}~|n|
+after that first \.{\\insert}~|n| had better be either extremely careful
+or extremely lucky, or both.
+
+@<Create a page insertion node...@>=
+{@+q=get_node(page_ins_node_size);link(q)=link(r);link(r)=q;r=q;
+subtype(r)=qi(n);type(r)=inserting;ensure_vbox(n);
+if (box(n)==null) height(r)=0;
+else height(r)=height(box(n))+depth(box(n));
+best_ins_ptr(r)=null;@/
+q=skip(n);
+if (count(n)==1000) h=height(r);
+else h=x_over_n(height(r), 1000)*count(n);
+page_goal=page_goal-h-width(q);@/
+page_so_far[2+stretch_order(q)]=@|page_so_far[2+stretch_order(q)]+stretch(q);@/
+page_shrink=page_shrink+shrink(q);
+if ((shrink_order(q)!=normal)&&(shrink(q)!=0))
+ {@+print_err("Infinite glue shrinkage inserted from ");print_esc("skip");
+@.Infinite glue shrinkage...@>
+ print_int(n);
+ help3("The correction glue for page breaking with insertions",@/
+ "must have finite shrinkability. But you may proceed,",@/
+ "since the offensive shrinkability has been made finite.");
+ error();
+ }
+}
+
+@ Here is the code that will split a long footnote between pages, in an
+emergency. The current situation deserves to be recapitulated: Node |p|
+is an insertion into box |n|; the insertion will not fit, in its entirety,
+either because it would make the total contents of box |n| greater than
+\.{\\dimen} |n|, or because it would make the incremental amount of growth
+|h| greater than the available space |delta|, or both. (This amount |h| has
+been weighted by the insertion scaling factor, i.e., by \.{\\count} |n|
+over 1000.) Now we will choose the best way to break the vlist of the
+insertion, using the same criteria as in the \.{\\vsplit} operation.
+
+@<Find the best way to split the insertion...@>=
+{@+if (count(n) <= 0) w=max_dimen;
+else{@+w=page_goal-page_total-page_depth;
+ if (count(n)!=1000) w=x_over_n(w, count(n))*1000;
+ }
+if (w > dimen(n)-height(r)) w=dimen(n)-height(r);
+q=vert_break(ins_ptr(p), w, depth(p));
+height(r)=height(r)+best_height_plus_depth;
+#ifdef @!STAT
+if (tracing_pages > 0) @<Display the insertion split cost@>;
+#endif
+@;@/
+if (count(n)!=1000)
+ best_height_plus_depth=x_over_n(best_height_plus_depth, 1000)*count(n);
+page_goal=page_goal-best_height_plus_depth;
+type(r)=split_up;broken_ptr(r)=q;broken_ins(r)=p;
+if (q==null) insert_penalties=insert_penalties+eject_penalty;
+else if (type(q)==penalty_node) insert_penalties=insert_penalties+penalty(q);
+}
+
+@ @<Display the insertion split cost@>=
+{@+begin_diagnostic();print_nl("% split");print_int(n);
+@.split@>
+print(" to ");print_scaled(w);
+print_char(',');print_scaled(best_height_plus_depth);@/
+print(" p=");
+if (q==null) print_int(eject_penalty);
+else if (type(q)==penalty_node) print_int(penalty(q));
+else print_char('0');
+end_diagnostic(false);
+}
+
+@ When the page builder has looked at as much material as could appear before
+the next page break, it makes its decision. The break that gave minimum
+badness will be used to put a completed ``page'' into box 255, with insertions
+appended to their other boxes.
+
+We also set the values of |top_mark|, |first_mark|, and |bot_mark|. The
+program uses the fact that |bot_mark!=null| implies |first_mark!=null|;
+it also knows that |bot_mark==null| implies |top_mark==first_mark==null|.
+
+The |fire_up| subroutine prepares to output the current page at the best
+place; then it fires up the user's output routine, if there is one,
+or it simply ships out the page. There is one parameter, |c|, which represents
+the node that was being contributed to the page when the decision to
+force an output was made.
+
+@<Declare the procedure called |fire_up|@>=
+static void fire_up(pointer @!c)
+{@+
+pointer p, @!q, @!r, @!s; /*nodes being examined and/or changed*/
+pointer @!prev_p; /*predecessor of |p|*/
+int @!n; /*insertion box number*/
+bool @!wait; /*should the present insertion be held over?*/
+int @!save_vbadness; /*saved value of |vbadness|*/
+scaled @!save_vfuzz; /*saved value of |vfuzz|*/
+pointer @!save_split_top_skip; /*saved value of |split_top_skip|*/
+@<Set the value of |output_penalty|@>;
+if (sa_mark!=null)
+ if (do_marks(fire_up_init, 0, sa_mark)) sa_mark=null;
+if (bot_mark!=null)
+ {@+if (top_mark!=null) delete_token_ref(top_mark);
+ top_mark=bot_mark;add_token_ref(top_mark);
+ delete_token_ref(first_mark);first_mark=null;
+ }
+@<Put the \(o)optimal current page into box 255, update |first_mark| and |bot_mark|,
+append insertions to their boxes, and put the remaining nodes back on the
+contribution list@>;
+if (sa_mark!=null)
+ if (do_marks(fire_up_done, 0, sa_mark)) sa_mark=null;
+if ((top_mark!=null)&&(first_mark==null))
+ {@+first_mark=top_mark;add_token_ref(top_mark);
+ }
+if (output_routine!=null)
+ if (dead_cycles >= max_dead_cycles)
+ @<Explain that too many dead cycles have occurred in a row@>@;
+ else@<Fire up the user's output routine and |return|@>;
+@<Perform the default output routine@>;
+}
+
+@ @<Set the value of |output_penalty|@>=
+if (type(best_page_break)==penalty_node)
+ {@+geq_word_define(int_base+output_penalty_code, penalty(best_page_break));
+ penalty(best_page_break)=inf_penalty;
+ }
+else geq_word_define(int_base+output_penalty_code, inf_penalty)
+
+@ As the page is finally being prepared for output,
+pointer |p| runs through the vlist, with |prev_p| trailing behind;
+pointer |q| is the tail of a list of insertions that
+are being held over for a subsequent page.
+
+@<Put the \(o)optimal current page into box 255...@>=
+if (c==best_page_break) best_page_break=null; /*|c| not yet linked in*/
+@<Ensure that box 255 is empty before output@>;
+insert_penalties=0; /*this will count the number of insertions held over*/
+save_split_top_skip=split_top_skip;
+if (holding_inserts <= 0)
+ @<Prepare all the boxes involved in insertions to act as queues@>;
+q=hold_head;link(q)=null;prev_p=page_head;p=link(prev_p);
+while (p!=best_page_break)
+ {@+if (type(p)==ins_node)
+ {@+if (holding_inserts <= 0)
+ @<Either insert the material specified by node |p| into the appropriate
+box, or hold it for the next page; also delete node |p| from the current page@>;
+ }
+ else if (type(p)==mark_node)
+ if (mark_class(p)!=0) @<Update the current marks for |fire_up|@>@;
+ else@<Update the values of |first_mark| and |bot_mark|@>;
+ prev_p=p;p=link(prev_p);
+ }
+split_top_skip=save_split_top_skip;
+@<Break the current page at node |p|, put it in box~255, and put the remaining
+nodes on the contribution list@>;
+@<Delete \(t)the page-insertion nodes@>@;
+
+@ @<Ensure that box 255 is empty before output@>=
+if (box(255)!=null)
+ {@+print_err("");print_esc("box");print("255 is not void");
+@:box255}{\.{\\box255 is not void}@>
+ help2("You shouldn't use \\box255 except in \\output routines.",@/
+ "Proceed, and I'll discard its present contents.");
+ box_error(255);
+ }
+
+@ @<Update the values of |first_mark| and |bot_mark|@>=
+{@+if (first_mark==null)
+ {@+first_mark=mark_ptr(p);
+ add_token_ref(first_mark);
+ }
+if (bot_mark!=null) delete_token_ref(bot_mark);
+bot_mark=mark_ptr(p);add_token_ref(bot_mark);
+}
+
+@ When the following code is executed, the current page runs from node
+|link(page_head)| to node |prev_p|, and the nodes from |p| to |page_tail|
+are to be placed back at the front of the contribution list. Furthermore
+the heldover insertions appear in a list from |link(hold_head)| to |q|; we
+will put them into the current page list for safekeeping while the user's
+output routine is active. We might have |q==hold_head|; and |p==null| if
+and only if |prev_p==page_tail|. Error messages are suppressed within
+|vpackage|, since the box might appear to be overfull or underfull simply
+because the stretch and shrink from the \.{\\skip} registers for inserts
+are not actually present in the box.
+
+@<Break the current page at node |p|, put it...@>=
+if (p!=null)
+ {@+if (link(contrib_head)==null)
+ if (nest_ptr==0) tail=page_tail;
+ else contrib_tail=page_tail;
+ link(page_tail)=link(contrib_head);
+ link(contrib_head)=p;
+ link(prev_p)=null;
+ }
+save_vbadness=vbadness;vbadness=inf_bad;
+save_vfuzz=vfuzz;vfuzz=max_dimen; /*inhibit error messages*/
+box(255)=vpackage(link(page_head), best_size, exactly, page_max_depth);
+vbadness=save_vbadness;vfuzz=save_vfuzz;
+if (last_glue!=max_halfword) delete_glue_ref(last_glue);
+@<Start a new current page@>; /*this sets |last_glue=max_halfword|*/
+if (q!=hold_head)
+ {@+link(page_head)=link(hold_head);page_tail=q;
+ }
+
+@ If many insertions are supposed to go into the same box, we want to know
+the position of the last node in that box, so that we don't need to waste time
+when linking further information into it. The |last_ins_ptr| fields of the
+page insertion nodes are therefore used for this purpose during the
+packaging phase.
+
+@<Prepare all the boxes involved in insertions to act as queues@>=
+{@+r=link(page_ins_head);
+while (r!=page_ins_head)
+ {@+if (best_ins_ptr(r)!=null)
+ {@+n=qo(subtype(r));ensure_vbox(n);
+ if (box(n)==null) box(n)=new_null_box();
+ p=box(n)+list_offset;
+ while (link(p)!=null) p=link(p);
+ last_ins_ptr(r)=p;
+ }
+ r=link(r);
+ }
+}
+
+@ @<Delete \(t)the page-insertion nodes@>=
+r=link(page_ins_head);
+while (r!=page_ins_head)
+ {@+q=link(r);free_node(r, page_ins_node_size);r=q;
+ }
+link(page_ins_head)=page_ins_head
+
+@ We will set |best_ins_ptr=null| and package the box corresponding to
+insertion node~|r|, just after making the final insertion into that box.
+If this final insertion is `|split_up|', the remainder after splitting
+and pruning (if any) will be carried over to the next page.
+
+@<Either insert the material specified by node |p| into...@>=
+{@+r=link(page_ins_head);
+while (subtype(r)!=subtype(p)) r=link(r);
+if (best_ins_ptr(r)==null) wait=true;
+else{@+wait=false;s=last_ins_ptr(r);link(s)=ins_ptr(p);
+ if (best_ins_ptr(r)==p)
+ @<Wrap up the box specified by node |r|, splitting node |p| if called
+for; set |wait:=true| if node |p| holds a remainder after splitting@>@;
+ else{@+while (link(s)!=null) s=link(s);
+ last_ins_ptr(r)=s;
+ }
+ }
+@<Either append the insertion node |p| after node |q|, and remove it from
+the current page, or delete |node(p)|@>;
+}
+
+@ @<Wrap up the box specified by node |r|, splitting node |p| if...@>=
+{@+if (type(r)==split_up)
+ if ((broken_ins(r)==p)&&(broken_ptr(r)!=null))
+ {@+while (link(s)!=broken_ptr(r)) s=link(s);
+ link(s)=null;
+ split_top_skip=split_top_ptr(p);
+ ins_ptr(p)=prune_page_top(broken_ptr(r), false);
+ if (ins_ptr(p)!=null)
+ {@+temp_ptr=vpack(ins_ptr(p), natural);
+ height(p)=height(temp_ptr)+depth(temp_ptr);
+ free_node(temp_ptr, box_node_size);wait=true;
+ }
+ }
+best_ins_ptr(r)=null;
+n=qo(subtype(r));
+temp_ptr=list_ptr(box(n));
+free_node(box(n), box_node_size);
+box(n)=vpack(temp_ptr, natural);
+}
+
+@ @<Either append the insertion node |p|...@>=
+link(prev_p)=link(p);link(p)=null;
+if (wait)
+ {@+link(q)=p;q=p;incr(insert_penalties);
+ }
+else{@+delete_glue_ref(split_top_ptr(p));
+ free_node(p, ins_node_size);
+ }
+p=prev_p
+
+@ The list of heldover insertions, running from |link(page_head)| to
+|page_tail|, must be moved to the contribution list when the user has
+specified no output routine.
+
+@<Perform the default output routine@>=
+{@+if (link(page_head)!=null)
+ {@+if (link(contrib_head)==null)
+ if (nest_ptr==0) tail=page_tail;@+else contrib_tail=page_tail;
+ else link(page_tail)=link(contrib_head);
+ link(contrib_head)=link(page_head);
+ link(page_head)=null;page_tail=page_head;
+ }
+flush_node_list(page_disc);page_disc=null;
+ship_out(box(255));box(255)=null;
+}
+
+@ @<Explain that too many dead cycles have occurred in a row@>=
+{@+print_err("Output loop---");print_int(dead_cycles);
+@.Output loop...@>
+print(" consecutive dead cycles");
+help3("I've concluded that your \\output is awry; it never does a",@/
+"\\shipout, so I'm shipping \\box255 out myself. Next time",@/
+"increase \\maxdeadcycles if you want me to be more patient!");error();
+}
+
+@ @<Fire up the user's output routine and |return|@>=
+{@+output_active=true;
+incr(dead_cycles);
+push_nest();mode=-vmode;prev_depth=ignore_depth;mode_line=-line;
+begin_token_list(output_routine, output_text);
+new_save_level(output_group);normal_paragraph();
+scan_left_brace();
+return;
+}
+
+@ When the user's output routine finishes, it has constructed a vlist
+in internal vertical mode, and \TeX\ will do the following:
+
+@<Resume the page builder after an output routine has come to an end@>=
+{@+if ((loc!=null)||
+ ((token_type!=output_text)&&(token_type!=backed_up)))
+ @<Recover from an unbalanced output routine@>;
+end_token_list(); /*conserve stack space in case more outputs are triggered*/
+end_graf();unsave();output_active=false;insert_penalties=0;@/
+@<Ensure that box 255 is empty after output@>;
+if (tail!=head) /*current list goes after heldover insertions*/
+ {@+link(page_tail)=link(head);
+ page_tail=tail;
+ }
+if (link(page_head)!=null) /*and both go before heldover contributions*/
+ {@+if (link(contrib_head)==null) contrib_tail=page_tail;
+ link(page_tail)=link(contrib_head);
+ link(contrib_head)=link(page_head);
+ link(page_head)=null;page_tail=page_head;
+ }
+flush_node_list(page_disc);page_disc=null;
+pop_nest();build_page();
+}
+
+@ @<Recover from an unbalanced output routine@>=
+{@+print_err("Unbalanced output routine");
+@.Unbalanced output routine@>
+help2("Your sneaky output routine has problematic {'s and/or }'s.",@/
+"I can't handle that very well; good luck.");error();
+@/do@+{get_token();
+}@+ while (!(loc==null));
+} /*loops forever if reading from a file, since |null==min_halfword <= 0|*/
+
+@ @<Ensure that box 255 is empty after output@>=
+if (box(255)!=null)
+ {@+print_err("Output routine didn't use all of ");
+ print_esc("box");print_int(255);
+@.Output routine didn't use...@>
+ help3("Your \\output commands should empty \\box255,",@/
+ "e.g., by saying `\\shipout\\box255'.",@/
+ "Proceed; I'll discard its present contents.");
+ box_error(255);
+ }
+
+@* The chief executive.
+We come now to the |main_control| routine, which contains the master
+switch that causes all the various pieces of \TeX\ to do their things,
+in the right order.
+
+In a sense, this is the grand climax of the program: It applies all the
+tools that we have worked so hard to construct. In another sense, this is
+the messiest part of the program: It necessarily refers to other pieces
+of code all over the place, so that a person can't fully understand what is
+going on without paging back and forth to be reminded of conventions that
+are defined elsewhere. We are now at the hub of the web, the central nervous
+system that touches most of the other parts and ties them together.
+@^brain@>
+
+The structure of |main_control| itself is quite simple. There's a label
+called |big_switch|, at which point the next token of input is fetched
+using |get_x_token|. Then the program branches at high speed into one of
+about 100 possible directions, based on the value of the current
+mode and the newly fetched command code; the sum |abs(mode)+cur_cmd|
+indicates what to do next. For example, the case `|vmode+letter|' arises
+when a letter occurs in vertical mode (or internal vertical mode); this
+case leads to instructions that initialize a new paragraph and enter
+horizontal mode.
+
+The big |case| statement that contains this multiway switch has been labeled
+|reswitch|, so that the program can |goto reswitch| when the next token
+has already been fetched. Most of the cases are quite short; they call
+an ``action procedure'' that does the work for that case, and then they
+either |goto reswitch| or they ``fall through'' to the end of the |case|
+statement, which returns control back to |big_switch|. Thus, |main_control|
+is not an extremely large procedure, in spite of the multiplicity of things
+it must do; it is small enough to be handled by \PASCAL\ compilers that put
+severe restrictions on procedure size.
+@!@^action procedure@>
+
+One case is singled out for special treatment, because it accounts for most
+of \TeX's activities in typical applications. The process of reading simple
+text and converting it into |char_node| records, while looking for ligatures
+and kerns, is part of \TeX's ``inner loop''; the whole program runs
+efficiently when its inner loop is fast, so this part has been written
+with particular care.
+
+@ We shall concentrate first on the inner loop of |main_control|, deferring
+consideration of the other cases until later.
+@^inner loop@>
+
+@p @t\4@>@<Declare action procedures for use by |main_control|@>@;
+@t\4@>@<Declare the procedure called |handle_right_brace|@>@;
+static void main_control(void) /*governs \TeX's activities*/
+{@+
+int @!t; /*general-purpose temporary variable*/
+@<Initialize profiling@>@;
+if (every_job!=null) begin_token_list(every_job, every_job_text);
+big_switch:
+ @<record timing information@>@;
+ get_x_token();@/
+big_reswitch:
+ @<set current file, line, and command for the current time slot@>@;
+@<Give diagnostic information, if requested@>;
+switch (abs(mode)+cur_cmd) {
+case hmode+letter: case hmode+other_char:
+ case hmode+char_given: goto main_loop;
+case hmode+char_num: {@+scan_char_num();cur_chr=cur_val;goto main_loop;@+}
+case hmode+no_boundary: {@+get_x_token();
+ if ((cur_cmd==letter)||(cur_cmd==other_char)||(cur_cmd==char_given)||
+ (cur_cmd==char_num)) cancel_boundary=true;
+ goto big_reswitch;
+ }
+case hmode+spacer: if (space_factor==1000) goto append_normal_space;
+ else app_space();@+break;
+case hmode+ex_space: case mmode+ex_space: goto append_normal_space;
+@t\4@>@<Cases of |main_control| that are not part of the inner loop@>@;
+} /*of the big |case| statement*/
+goto big_switch;
+main_loop:
+@<record timing information@>@;
+@<set current file, line, and command for the current time slot@>@;
+@<Append character |cur_chr| and the following characters (if~any)
+to the current hlist in the current font; |goto big_reswitch| when a non-character
+has been fetched@>;
+append_normal_space: @<Append a normal inter-word space to the current list,
+then |goto big_switch|@>;
+}
+
+@ When a new token has just been fetched at |big_switch|, we have an
+ideal place to monitor \TeX's activity.
+@^debugging@>
+
+@<Give diagnostic information, if requested@>=
+if (interrupt!=0) if (OK_to_interrupt)
+ {@+back_input();check_interrupt;goto big_switch;
+ }
+#ifdef @!DEBUG
+if (panicking) check_mem(false);@;
+#endif
+if (tracing_commands > 0) show_cur_cmd_chr()
+
+@ The following part of the program was first written in a structured
+manner, according to the philosophy that ``premature optimization is
+the root of all evil.'' Then it was rearranged into pieces of
+spaghetti so that the most common actions could proceed with little or
+no redundancy.
+
+The original unoptimized form of this algorithm resembles the
+|reconstitute| procedure, which was described earlier in connection with
+hyphenation. Again we have an implied ``cursor'' between characters
+|cur_l| and |cur_r|. The main difference is that the |lig_stack| can now
+contain a charnode as well as pseudo-ligatures; that stack is now
+usually nonempty, because the next character of input (if any) has been
+appended to it. In |main_control| we have
+$$|cur_r|=\cases{|character(lig_stack)|,&if |lig_stack > null|;\cr
+ |font_bchar[cur_font]|,&otherwise;\cr}$$
+except when |character(lig_stack)==font_false_bchar[cur_font]|.
+Several additional global variables are needed.
+
+@<Glob...@>=
+static internal_font_number @!main_f; /*the current font*/
+static four_quarters @!main_i; /*character information bytes for |cur_l|*/
+static four_quarters @!main_j; /*ligature/kern command*/
+static font_index @!main_k; /*index into |font_info|*/
+static pointer @!main_p; /*temporary register for list manipulation*/
+static int @!main_s; /*space factor value*/
+static halfword @!bchar; /*boundary character of current font, or |non_char|*/
+static halfword @!false_bchar; /*nonexistent character matching |bchar|, or |non_char|*/
+static bool @!cancel_boundary; /*should the left boundary be ignored?*/
+static bool @!ins_disc; /*should we insert a discretionary node?*/
+
+@ The boolean variables of the main loop are normally false, and always reset
+to false before the loop is left. That saves us the extra work of initializing
+each time.
+
+@<Set init...@>=
+ligature_present=false;cancel_boundary=false;lft_hit=false;rt_hit=false;
+ins_disc=false;
+
+@ We leave the |space_factor| unchanged if |sf_code(cur_chr)==0|; otherwise we
+set it equal to |sf_code(cur_chr)|, except that it should never change
+from a value less than 1000 to a value exceeding 1000. The most common
+case is |sf_code(cur_chr)==1000|, so we want that case to be fast.
+
+The overall structure of the main loop is presented here. Some program labels
+are inside the individual sections.
+@^inner loop@>
+
+@d adjust_space_factor @t@>@;@/
+ main_s=sf_code(cur_chr);
+ if (main_s==1000) space_factor=1000;
+ else if (main_s < 1000)
+ {@+if (main_s > 0) space_factor=main_s;
+ }
+ else if (space_factor < 1000) space_factor=1000;
+ else space_factor=main_s
+
+@<Append character |cur_chr|...@>=
+adjust_space_factor;@/
+main_f=cur_font;
+bchar=font_bchar[main_f];false_bchar=font_false_bchar[main_f];
+if (mode > 0) if (language!=clang) fix_language();
+fast_get_avail(lig_stack);font(lig_stack)=main_f;cur_l=qi(cur_chr);
+character(lig_stack)=cur_l;@/
+cur_q=tail;
+if (cancel_boundary)
+ {@+cancel_boundary=false;main_k=non_address;
+ }
+else main_k=bchar_label[main_f];
+if (main_k==non_address) goto main_loop_move2; /*no left boundary processing*/
+cur_r=cur_l;cur_l=non_char;
+goto main_lig_loop1; /*begin with cursor after left boundary*/
+@#
+main_loop_wrapup: @<Make a ligature node, if |ligature_present|; insert a
+null discretionary, if appropriate@>;
+main_loop_move: @<If the cursor is immediately followed by the right boundary,
+|goto big_reswitch|; if it's followed by an invalid character, |goto big_switch|;
+otherwise move the cursor one step to the right and |goto main_lig_loop|@>;
+main_loop_lookahead: @<Look ahead for another character, or leave |lig_stack|
+empty if there's none there@>;
+main_lig_loop: @<If there's a ligature/kern command relevant to |cur_l| and
+|cur_r|, adjust the text appropriately; exit to |main_loop_wrapup|@>;
+main_loop_move_lig: @<Move the cursor past a pseudo-ligature, then |goto main_loop_lookahead|
+or |main_lig_loop|@>@;
+
+@ If |link(cur_q)| is nonnull when |wrapup| is invoked, |cur_q| points to
+the list of characters that were consumed while building the ligature
+character~|cur_l|.
+
+A discretionary break is not inserted for an explicit hyphen when we are in
+restricted horizontal mode. In particular, this avoids putting discretionary
+nodes inside of other discretionaries.
+@^inner loop@>
+
+@d pack_lig(X) /*the parameter is either |rt_hit| or |false|*/
+ {@+main_p=new_ligature(main_f, cur_l, link(cur_q));
+ if (lft_hit)
+ {@+subtype(main_p)=2;lft_hit=false;
+ }
+ if (X) if (lig_stack==null)
+ {@+incr(subtype(main_p));rt_hit=false;
+ }
+ link(cur_q)=main_p;tail=main_p;ligature_present=false;
+ }
+
+@d wrapup(A) if (cur_l < non_char)
+ {@+if (link(cur_q) > null)
+ if (character(tail)==qi(hyphen_char[main_f])) ins_disc=true;
+ if (ligature_present) pack_lig(A);
+ if (ins_disc)
+ {@+ins_disc=false;
+ if (mode > 0) tail_append(new_disc());
+ }
+ }
+
+@<Make a ligature node, if |ligature_present|;...@>=
+wrapup(rt_hit)
+
+@ @<If the cursor is immediately followed by the right boundary...@>=
+@^inner loop@>
+if (lig_stack==null) goto big_reswitch;
+cur_q=tail;cur_l=character(lig_stack);
+main_loop_move1: if (!is_char_node(lig_stack)) goto main_loop_move_lig;
+main_loop_move2: if ((cur_chr < font_bc[main_f])||(cur_chr > font_ec[main_f]))
+ {@+char_warning(main_f, cur_chr);free_avail(lig_stack);goto big_switch;
+ }
+main_i=char_info(main_f, cur_l);
+if (!char_exists(main_i))
+ {@+char_warning(main_f, cur_chr);free_avail(lig_stack);goto big_switch;
+ }
+link(tail)=lig_stack;tail=lig_stack /*|main_loop_lookahead| is next*/
+
+@ Here we are at |main_loop_move_lig|.
+When we begin this code we have |cur_q==tail| and |cur_l==character(lig_stack)|.
+
+@<Move the cursor past a pseudo-ligature...@>=
+main_p=lig_ptr(lig_stack);
+if (main_p > null) tail_append(main_p); /*append a single character*/
+temp_ptr=lig_stack;lig_stack=link(temp_ptr);
+free_node(temp_ptr, small_node_size);
+main_i=char_info(main_f, cur_l);ligature_present=true;
+if (lig_stack==null)
+ if (main_p > null) goto main_loop_lookahead;
+ else cur_r=bchar;
+else cur_r=character(lig_stack);
+goto main_lig_loop
+
+@ The result of \.{\\char} can participate in a ligature or kern, so we must
+look ahead for it.
+
+@<Look ahead for another character...@>=
+get_next(); /*set only |cur_cmd| and |cur_chr|, for speed*/
+if (cur_cmd==letter) goto main_loop_lookahead1;
+if (cur_cmd==other_char) goto main_loop_lookahead1;
+if (cur_cmd==char_given) goto main_loop_lookahead1;
+x_token(); /*now expand and set |cur_cmd|, |cur_chr|, |cur_tok|*/
+if (cur_cmd==letter) goto main_loop_lookahead1;
+if (cur_cmd==other_char) goto main_loop_lookahead1;
+if (cur_cmd==char_given) goto main_loop_lookahead1;
+if (cur_cmd==char_num)
+ {@+scan_char_num();cur_chr=cur_val;goto main_loop_lookahead1;
+ }
+if (cur_cmd==no_boundary) bchar=non_char;
+cur_r=bchar;lig_stack=null;goto main_lig_loop;
+main_loop_lookahead1: adjust_space_factor;
+fast_get_avail(lig_stack);font(lig_stack)=main_f;
+cur_r=qi(cur_chr);character(lig_stack)=cur_r;
+if (cur_r==false_bchar) cur_r=non_char /*this prevents spurious ligatures*/
+
+@ Even though comparatively few characters have a lig/kern program, several
+of the instructions here count as part of \TeX's inner loop, since a
+@^inner loop@>
+potentially long sequential search must be performed. For example, tests with
+Computer Modern Roman showed that about 40 per cent of all characters
+actually encountered in practice had a lig/kern program, and that about four
+lig/kern commands were investigated for every such character.
+
+At the beginning of this code we have |main_i==char_info(main_f, cur_l)|.
+
+@<If there's a ligature/kern command...@>=
+if (char_tag(main_i)!=lig_tag) goto main_loop_wrapup;
+if (cur_r==non_char) goto main_loop_wrapup;
+main_k=lig_kern_start(main_f, main_i);main_j=font_info[main_k].qqqq;
+if (skip_byte(main_j) <= stop_flag) goto main_lig_loop2;
+main_k=lig_kern_restart(main_f, main_j);
+main_lig_loop1: main_j=font_info[main_k].qqqq;
+main_lig_loop2: if (next_char(main_j)==cur_r)
+ if (skip_byte(main_j) <= stop_flag)
+ @<Do ligature or kern command, returning to |main_lig_loop| or |main_loop_wrapup|
+or |main_loop_move|@>;
+if (skip_byte(main_j)==qi(0)) incr(main_k);
+else{@+if (skip_byte(main_j) >= stop_flag) goto main_loop_wrapup;
+ main_k=main_k+qo(skip_byte(main_j))+1;
+ }
+goto main_lig_loop1
+
+@ When a ligature or kern instruction matches a character, we know from
+|read_font_info| that the character exists in the font, even though we
+haven't verified its existence in the normal way.
+
+This section could be made into a subroutine, if the code inside
+|main_control| needs to be shortened.
+
+\chardef\@@='174 % vertical line to indicate character retention
+
+@<Do ligature or kern command...@>=
+{@+if (op_byte(main_j) >= kern_flag)
+ {@+wrapup(rt_hit);
+ tail_append(new_kern(char_kern(main_f, main_j)));goto main_loop_move;
+ }
+if (cur_l==non_char) lft_hit=true;
+else if (lig_stack==null) rt_hit=true;
+check_interrupt; /*allow a way out in case there's an infinite ligature loop*/
+switch (op_byte(main_j)) {
+case qi(1): case qi(5): {@+cur_l=rem_byte(main_j); /*\.{=:\@@}, \.{=:\@@>}*/
+ main_i=char_info(main_f, cur_l);ligature_present=true;
+ } @+break;
+case qi(2): case qi(6): {@+cur_r=rem_byte(main_j); /*\.{\@@=:}, \.{\@@=:>}*/
+ if (lig_stack==null) /*right boundary character is being consumed*/
+ {@+lig_stack=new_lig_item(cur_r);bchar=non_char;
+ }
+ else if (is_char_node(lig_stack)) /*|link(lig_stack)==null|*/
+ {@+main_p=lig_stack;lig_stack=new_lig_item(cur_r);
+ lig_ptr(lig_stack)=main_p;
+ }
+ else character(lig_stack)=cur_r;
+ } @+break;
+case qi(3): {@+cur_r=rem_byte(main_j); /*\.{\@@=:\@@}*/
+ main_p=lig_stack;lig_stack=new_lig_item(cur_r);
+ link(lig_stack)=main_p;
+ } @+break;
+case qi(7): case qi(11): {@+wrapup(false); /*\.{\@@=:\@@>}, \.{\@@=:\@@>>}*/
+ cur_q=tail;cur_l=rem_byte(main_j);
+ main_i=char_info(main_f, cur_l);ligature_present=true;
+ } @+break;
+default:{@+cur_l=rem_byte(main_j);ligature_present=true; /*\.{=:}*/
+ if (lig_stack==null) goto main_loop_wrapup;
+ else goto main_loop_move1;
+ }
+}
+if (op_byte(main_j) > qi(4))
+ if (op_byte(main_j)!=qi(7)) goto main_loop_wrapup;
+if (cur_l < non_char) goto main_lig_loop;
+main_k=bchar_label[main_f];goto main_lig_loop1;
+}
+
+@ The occurrence of blank spaces is almost part of \TeX's inner loop,
+@^inner loop@>
+since we usually encounter about one space for every five non-blank characters.
+Therefore |main_control| gives second-highest priority to ordinary spaces.
+
+When a glue parameter like \.{\\spaceskip} is set to `\.{0pt}', we will
+see to it later that the corresponding glue specification is precisely
+|zero_glue|, not merely a pointer to some specification that happens
+to be full of zeroes. Therefore it is simple to test whether a glue parameter
+is zero or~not.
+
+@<Append a normal inter-word space...@>=
+if (space_skip==zero_glue)
+ {@+@<Find the glue specification, |main_p|, for text spaces in the current
+font@>;
+ temp_ptr=new_glue(main_p);
+ }
+else temp_ptr=new_param_glue(space_skip_code);
+link(tail)=temp_ptr;tail=temp_ptr;
+goto big_switch
+
+@ Having |font_glue| allocated for each text font saves both time and memory.
+If any of the three spacing parameters are subsequently changed by the
+use of \.{\\fontdimen}, the |find_font_dimen| procedure deallocates the
+|font_glue| specification allocated here.
+
+@<Find the glue specification...@>=
+{@+main_p=font_glue[cur_font];
+if (main_p==null)
+ {@+main_p=new_spec(zero_glue);main_k=param_base[cur_font]+space_code;
+ width(main_p)=font_info[main_k].sc; /*that's |space(cur_font)|*/
+ stretch(main_p)=font_info[main_k+1].sc; /*and |space_stretch(cur_font)|*/
+ shrink(main_p)=font_info[main_k+2].sc; /*and |space_shrink(cur_font)|*/
+ font_glue[cur_font]=main_p;
+ }
+}
+
+@ @<Declare act...@>=
+static void app_space(void) /*handle spaces when |space_factor!=1000|*/
+{@+pointer @!q; /*glue node*/
+if ((space_factor >= 2000)&&(xspace_skip!=zero_glue))
+ q=new_param_glue(xspace_skip_code);
+else{@+if (space_skip!=zero_glue) main_p=space_skip;
+ else@<Find the glue specification...@>;
+ main_p=new_spec(main_p);
+ @<Modify the glue specification in |main_p| according to the space factor@>;
+ q=new_glue(main_p);glue_ref_count(main_p)=null;
+ }
+link(tail)=q;tail=q;
+}
+
+@ @<Modify the glue specification in |main_p| according to the space factor@>=
+if (space_factor >= 2000) width(main_p)=width(main_p)+extra_space(cur_font);
+stretch(main_p)=xn_over_d(stretch(main_p), space_factor, 1000);
+shrink(main_p)=xn_over_d(shrink(main_p), 1000, space_factor)
+
+@ Whew---that covers the main loop. We can now proceed at a leisurely
+pace through the other combinations of possibilities.
+
+@d any_mode(A) case vmode+A: case hmode+A:
+ case mmode+A /*for mode-independent commands*/
+
+@<Cases of |main_control| that are not part of the inner loop@>=
+any_mode(relax): case vmode+spacer: case mmode+spacer:
+ case mmode+no_boundary: do_nothing;@+break;
+any_mode(ignore_spaces): {@+@<Get the next non-blank non-call...@>;
+ goto big_reswitch;
+ }
+case vmode+stop:
+ if (its_all_over())
+ { @<record the end of \TeX@>@;
+ return; /*this is the only way out*/
+ }
+ @+break;
+@t\4@>@<Forbidden cases detected in |main_control|@>@+@, any_mode(mac_param):
+ report_illegal_case();@+break;
+@<Math-only cases in non-math modes, or vice versa@>: insert_dollar_sign();@+break;
+@t\4@>@<Cases of |main_control| that build boxes and lists@>@;
+@t\4@>@<Cases of |main_control| that don't depend on |mode|@>@;
+@t\4@>@<Cases of |main_control| that are for extensions to \TeX@>@;
+
+@ Here is a list of cases where the user has probably gotten into or out of math
+mode by mistake. \TeX\ will insert a dollar sign and rescan the current token.
+
+@d non_math(A) case vmode+A: case hmode+A
+
+@<Math-only cases in non-math modes...@>=
+non_math(sup_mark): non_math(sub_mark): non_math(math_char_num):
+non_math(math_given): non_math(math_comp): non_math(delim_num):
+non_math(left_right): non_math(above): non_math(radical):
+non_math(math_style): non_math(math_choice): non_math(vcenter):
+non_math(non_script): non_math(mkern): non_math(limit_switch):
+non_math(mskip): non_math(math_accent):
+case mmode+endv: case mmode+par_end: case mmode+stop:
+ case mmode+vskip: case mmode+un_vbox:
+case mmode+valign: case mmode+hrule
+
+@ @<Declare action...@>=
+static void insert_dollar_sign(void)
+{@+back_input();cur_tok=math_shift_token+'$';
+print_err("Missing $ inserted");
+@.Missing \$ inserted@>
+help2("I've inserted a begin-math/end-math symbol since I think",@/
+"you left one out. Proceed, with fingers crossed.");ins_error();
+}
+
+@ When erroneous situations arise, \TeX\ usually issues an error message
+specific to the particular error. For example, `\.{\\noalign}' should
+not appear in any mode, since it is recognized by the |align_peek| routine
+in all of its legitimate appearances; a special error message is given
+when `\.{\\noalign}' occurs elsewhere. But sometimes the most appropriate
+error message is simply that the user is not allowed to do what he or she
+has attempted. For example, `\.{\\moveleft}' is allowed only in vertical mode,
+and `\.{\\lower}' only in non-vertical modes. Such cases are enumerated
+here and in the other sections referred to under `See also \dots.'
+
+@<Forbidden cases...@>=
+case vmode+vmove: case hmode+hmove: case mmode+hmove: any_mode(last_item):
+
+@ The `|you_cant|' procedure prints a line saying that the current command
+is illegal in the current mode; it identifies these things symbolically.
+
+@<Declare action...@>=
+static void you_cant(void)
+{@+print_err("You can't use `");
+@.You can't use x in y mode@>
+print_cmd_chr(cur_cmd, cur_chr);
+print("' in ");print_mode(mode);
+}
+
+@ @<Declare act...@>=
+static void report_illegal_case(void)
+{@+you_cant();
+help4("Sorry, but I'm not programmed to handle this case;",@/
+"I'll just pretend that you didn't ask for it.",@/
+"If you're in the wrong mode, you might be able to",@/
+"return to the right one by typing `I}' or `I$' or `I\\par'.");@/
+error();
+}
+
+@ Some operations are allowed only in privileged modes, i.e., in cases
+that |mode > 0|. The |privileged| function is used to detect violations
+of this rule; it issues an error message and returns |false| if the
+current |mode| is negative.
+
+@<Declare act...@>=
+static bool privileged(void)
+{@+if (mode > 0) return true;
+else{@+report_illegal_case();return false;
+ }
+}
+
+@ Either \.{\\dump} or \.{\\end} will cause |main_control| to enter the
+endgame, since both of them have `|stop|' as their command code.
+
+@<Put each...@>=
+primitive("end", stop, 0);@/
+@!@:end\_}{\.{\\end} primitive@>
+primitive("dump", stop, 1);@/
+@!@:dump\_}{\.{\\dump} primitive@>
+
+@ @<Cases of |print_cmd_chr|...@>=
+case stop: if (chr_code==1) print_esc("dump");@+else print_esc("end");@+break;
+
+@ We don't want to leave |main_control| immediately when a |stop| command
+is sensed, because it may be necessary to invoke an \.{\\output} routine
+several times before things really grind to a halt. (The output routine
+might even say `\.{\\gdef\\end\{...\}}', to prolong the life of the job.)
+Therefore |its_all_over| is |true| only when the current page
+and contribution list are empty, and when the last output was not a
+``dead cycle.''
+
+@<Declare act...@>=
+static bool its_all_over(void) /*do this when \.{\\end} or \.{\\dump} occurs*/
+{@+
+if (privileged())
+ {@+if ((page_head==page_tail)&&(head==tail)&&(dead_cycles==0))
+ {@+return true;
+ }
+ back_input(); /*we will try to end again after ejecting residual material*/
+ tail_append(new_null_box());
+ width(tail)=hsize;
+ tail_append(new_glue(fill_glue));
+ tail_append(new_penalty(-010000000000));@/
+ build_page(); /*append \.{\\hbox to \\hsize\{\}\\vfill\\penalty-'10000000000}*/
+ }
+return false;
+}
+
+@* Building boxes and lists.
+The most important parts of |main_control| are concerned with \TeX's
+chief mission of box-making. We need to control the activities that put
+entries on vlists and hlists, as well as the activities that convert
+those lists into boxes. All of the necessary machinery has already been
+developed; it remains for us to ``push the buttons'' at the right times.
+
+@ As an introduction to these routines, let's consider one of the simplest
+cases: What happens when `\.{\\hrule}' occurs in vertical mode, or
+`\.{\\vrule}' in horizontal mode or math mode? The code in |main_control|
+is short, since the |scan_rule_spec| routine already does most of what is
+required; thus, there is no need for a special action procedure.
+
+Note that baselineskip calculations are disabled after a rule in vertical
+mode, by setting |prev_depth=ignore_depth|.
+
+@<Cases of |main_control| that build...@>=
+case vmode+hrule: case hmode+vrule: case mmode+vrule: {@+tail_append(scan_rule_spec());
+ if (abs(mode)==vmode) prev_depth=ignore_depth;
+ else if (abs(mode)==hmode) space_factor=1000;
+ } @+break;
+
+@ The processing of things like \.{\\hskip} and \.{\\vskip} is slightly
+more complicated. But the code in |main_control| is very short, since
+it simply calls on the action routine |append_glue|. Similarly, \.{\\kern}
+activates |append_kern|.
+
+@<Cases of |main_control| that build...@>=
+case vmode+vskip: case hmode+hskip: case mmode+hskip:
+ case mmode+mskip: append_glue();@+break;
+any_mode(kern): case mmode+mkern: append_kern();@+break;
+
+@ The |hskip| and |vskip| command codes are used for control sequences
+like \.{\\hss} and \.{\\vfil} as well as for \.{\\hskip} and \.{\\vskip}.
+The difference is in the value of |cur_chr|.
+
+@d fil_code 0 /*identifies \.{\\hfil} and \.{\\vfil}*/
+@d fill_code 1 /*identifies \.{\\hfill} and \.{\\vfill}*/
+@d ss_code 2 /*identifies \.{\\hss} and \.{\\vss}*/
+@d fil_neg_code 3 /*identifies \.{\\hfilneg} and \.{\\vfilneg}*/
+@d skip_code 4 /*identifies \.{\\hskip} and \.{\\vskip}*/
+@d mskip_code 5 /*identifies \.{\\mskip}*/
+
+@<Put each...@>=
+primitive("hskip", hskip, skip_code);@/
+@!@:hskip\_}{\.{\\hskip} primitive@>
+primitive("hfil", hskip, fil_code);
+@!@:hfil\_}{\.{\\hfil} primitive@>
+primitive("hfill", hskip, fill_code);@/
+@!@:hfill\_}{\.{\\hfill} primitive@>
+primitive("hss", hskip, ss_code);
+@!@:hss\_}{\.{\\hss} primitive@>
+primitive("hfilneg", hskip, fil_neg_code);@/
+@!@:hfil\_neg\_}{\.{\\hfilneg} primitive@>
+primitive("vskip", vskip, skip_code);@/
+@!@:vskip\_}{\.{\\vskip} primitive@>
+primitive("vfil", vskip, fil_code);
+@!@:vfil\_}{\.{\\vfil} primitive@>
+primitive("vfill", vskip, fill_code);@/
+@!@:vfill\_}{\.{\\vfill} primitive@>
+primitive("vss", vskip, ss_code);
+@!@:vss\_}{\.{\\vss} primitive@>
+primitive("vfilneg", vskip, fil_neg_code);@/
+@!@:vfil\_neg\_}{\.{\\vfilneg} primitive@>
+primitive("mskip", mskip, mskip_code);@/
+@!@:mskip\_}{\.{\\mskip} primitive@>
+primitive("kern", kern, explicit);
+@!@:kern\_}{\.{\\kern} primitive@>
+primitive("mkern", mkern, mu_glue);@/
+@!@:mkern\_}{\.{\\mkern} primitive@>
+
+@ @<Cases of |print_cmd_chr|...@>=
+case hskip: switch (chr_code) {
+ case skip_code: print_esc("hskip");@+break;
+ case fil_code: print_esc("hfil");@+break;
+ case fill_code: print_esc("hfill");@+break;
+ case ss_code: print_esc("hss");@+break;
+ default:print_esc("hfilneg");
+ } @+break;
+case vskip: switch (chr_code) {
+ case skip_code: print_esc("vskip");@+break;
+ case fil_code: print_esc("vfil");@+break;
+ case fill_code: print_esc("vfill");@+break;
+ case ss_code: print_esc("vss");@+break;
+ default:print_esc("vfilneg");
+ } @+break;
+case mskip: print_esc("mskip");@+break;
+case kern: print_esc("kern");@+break;
+case mkern: print_esc("mkern");@+break;
+
+@ All the work relating to glue creation has been relegated to the
+following subroutine. It does not call |build_page|, because it is
+used in at least one place where that would be a mistake.
+
+@<Declare action...@>=
+static void append_glue(void)
+{@+small_number s; /*modifier of skip command*/
+s=cur_chr;
+switch (s) {
+case fil_code: cur_val=fil_glue;@+break;
+case fill_code: cur_val=fill_glue;@+break;
+case ss_code: cur_val=ss_glue;@+break;
+case fil_neg_code: cur_val=fil_neg_glue;@+break;
+case skip_code: scan_glue(glue_val);@+break;
+case mskip_code: scan_glue(mu_val);
+} /*now |cur_val| points to the glue specification*/
+tail_append(new_glue(cur_val));
+if (s >= skip_code)
+ {@+decr(glue_ref_count(cur_val));
+ if (s > skip_code) subtype(tail)=mu_glue;
+ }
+}
+
+@ @<Declare act...@>=
+static void append_kern(void)
+{@+quarterword s; /*|subtype| of the kern node*/
+s=cur_chr;scan_dimen(s==mu_glue, false, false);
+tail_append(new_kern(cur_val));subtype(tail)=s;
+}
+
+@ Many of the actions related to box-making are triggered by the appearance
+of braces in the input. For example, when the user says `\.{\\hbox}
+\.{to} \.{100pt\{$\langle\,\hbox{\rm hlist}\,\rangle$\}}' in vertical mode,
+the information about the box size (100pt, |exactly|) is put onto |save_stack|
+with a level boundary word just above it, and |cur_group=adjusted_hbox_group|;
+\TeX\ enters restricted horizontal mode to process the hlist. The right
+brace eventually causes |save_stack| to be restored to its former state,
+at which time the information about the box size (100pt, |exactly|) is
+available once again; a box is packaged and we leave restricted horizontal
+mode, appending the new box to the current list of the enclosing mode
+(in this case to the current list of vertical mode), followed by any
+vertical adjustments that were removed from the box by |hpack|.
+
+The next few sections of the program are therefore concerned with the
+treatment of left and right curly braces.
+
+@ If a left brace occurs in the middle of a page or paragraph, it simply
+introduces a new level of grouping, and the matching right brace will not have
+such a drastic effect. Such grouping affects neither the mode nor the
+current list.
+
+@<Cases of |main_control| that build...@>=
+non_math(left_brace): new_save_level(simple_group);@+break;
+any_mode(begin_group): new_save_level(semi_simple_group);@+break;
+any_mode(end_group): if (cur_group==semi_simple_group) unsave();
+ else off_save();@+break;
+
+@ We have to deal with errors in which braces and such things are not
+properly nested. Sometimes the user makes an error of commission by
+inserting an extra symbol, but sometimes the user makes an error of omission.
+\TeX\ can't always tell one from the other, so it makes a guess and tries
+to avoid getting into a loop.
+
+The |off_save| routine is called when the current group code is wrong. It tries
+to insert something into the user's input that will help clean off
+the top level.
+
+@<Declare act...@>=
+static void off_save(void)
+{@+pointer p; /*inserted token*/
+if (cur_group==bottom_level)
+ @<Drop current token and complain that it was unmatched@>@;
+else{@+back_input();p=get_avail();link(temp_head)=p;
+ print_err("Missing ");
+ @<Prepare to insert a token that matches |cur_group|, and print what it
+is@>;
+ print(" inserted");ins_list(link(temp_head));
+ help5("I've inserted something that you may have forgotten.",@/
+ "(See the <inserted text> above.)",@/
+ "With luck, this will get me unwedged. But if you",@/
+ "really didn't forget anything, try typing `2' now; then",@/
+ "my insertion and my current dilemma will both disappear.");
+ error();
+ }
+}
+
+@ At this point, |link(temp_head)==p|, a pointer to an empty one-word node.
+
+@<Prepare to insert a token that matches |cur_group|...@>=
+switch (cur_group) {
+case semi_simple_group: {@+info(p)=cs_token_flag+frozen_end_group;
+ print_esc("endgroup");
+@.Missing \\endgroup inserted@>
+ } @+break;
+case math_shift_group: {@+info(p)=math_shift_token+'$';print_char('$');
+@.Missing \$ inserted@>
+ } @+break;
+case math_left_group: {@+info(p)=cs_token_flag+frozen_right;link(p)=get_avail();
+ p=link(p);info(p)=other_token+'.';print_esc("right.");
+@.Missing \\right\hbox{.} inserted@>
+@^null delimiter@>
+ } @+break;
+default:{@+info(p)=right_brace_token+'}';print_char('}');
+@.Missing \} inserted@>
+ }
+}
+
+@ @<Drop current token and complain that it was unmatched@>=
+{@+print_err("Extra ");print_cmd_chr(cur_cmd, cur_chr);
+@.Extra x@>
+help1("Things are pretty mixed up, but I think the worst is over.");@/
+error();
+}
+
+@ The routine for a |right_brace| character branches into many subcases,
+since a variety of things may happen, depending on |cur_group|. Some
+types of groups are not supposed to be ended by a right brace; error
+messages are given in hopes of pinpointing the problem. Most branches
+of this routine will be filled in later, when we are ready to understand
+them; meanwhile, we must prepare ourselves to deal with such errors.
+
+@<Cases of |main_control| that build...@>=
+any_mode(right_brace): handle_right_brace();@+break;
+
+@ @<Declare the procedure called |handle_right_brace|@>=
+static void handle_right_brace(void)
+{@+pointer p, @!q; /*for short-term use*/
+scaled @!d; /*holds |split_max_depth| in |insert_group|*/
+int @!f; /*holds |floating_penalty| in |insert_group|*/
+switch (cur_group) {
+case simple_group: unsave();@+break;
+case bottom_level: {@+print_err("Too many }'s");
+@.Too many \}'s@>
+ help2("You've closed more groups than you opened.",@/
+ "Such booboos are generally harmless, so keep going.");error();
+ } @+break;
+case semi_simple_group: case math_shift_group:
+ case math_left_group: extra_right_brace();@+break;
+@t\4@>@<Cases of |handle_right_brace| where a |right_brace| triggers a delayed
+action@>@;
+default:confusion("rightbrace");
+@:this can't happen rightbrace}{\quad rightbrace@>
+}
+}
+
+@ @<Declare act...@>=
+static void extra_right_brace(void)
+{@+print_err("Extra }, or forgotten ");
+@.Extra \}, or forgotten x@>
+switch (cur_group) {
+case semi_simple_group: print_esc("endgroup");@+break;
+case math_shift_group: print_char('$');@+break;
+case math_left_group: print_esc("right");
+} @/
+help5("I've deleted a group-closing symbol because it seems to be",@/
+"spurious, as in `$x}$'. But perhaps the } is legitimate and",@/
+"you forgot something else, as in `\\hbox{$x}'. In such cases",@/
+"the way to recover is to insert both the forgotten and the",@/
+"deleted material, e.g., by typing `I$}'.");error();
+incr(align_state);
+}
+
+@ Here is where we clear the parameters that are supposed to revert to their
+default values after every paragraph and when internal vertical mode is entered.
+
+@<Declare act...@>=
+static void normal_paragraph(void)
+{@+if (looseness!=0) eq_word_define(int_base+looseness_code, 0);
+if (hang_indent!=0) eq_word_define(dimen_base+hang_indent_code, 0);
+if (hang_after!=1) eq_word_define(int_base+hang_after_code, 1);
+if (par_shape_ptr!=null) eq_define(par_shape_loc, shape_ref, null);
+if (inter_line_penalties_ptr!=null)
+ eq_define(inter_line_penalties_loc, shape_ref, null);
+}
+
+@ Now let's turn to the question of how \.{\\hbox} is treated. We actually
+need to consider also a slightly larger context, since constructions like
+`\.{\\setbox3=}\penalty0\.{\\hbox...}' and
+`\.{\\leaders}\penalty0\.{\\hbox...}' and
+`\.{\\lower3.8pt\\hbox...}'
+are supposed to invoke quite
+different actions after the box has been packaged. Conversely,
+constructions like `\.{\\setbox3=}' can be followed by a variety of
+different kinds of boxes, and we would like to encode such things in an
+efficient way.
+
+In other words, there are two problems: to represent the context of a box,
+and to represent its type.
+
+The first problem is solved by putting a ``context code'' on the |save_stack|,
+just below the two entries that give the dimensions produced by |scan_spec|.
+The context code is either a (signed) shift amount, or it is a large
+integer | >= box_flag|, where |box_flag==@t$2^{30}$@>|. Codes |box_flag| through
+|global_box_flag-1| represent `\.{\\setbox0}' through `\.{\\setbox32767}';
+codes |global_box_flag| through |ship_out_flag-1| represent
+`\.{\\global\\setbox0}' through `\.{\\global\\setbox32767}';
+code |ship_out_flag| represents `\.{\\shipout}'; and codes |leader_flag|
+through |leader_flag+2| represent `\.{\\leaders}', `\.{\\cleaders}',
+and `\.{\\xleaders}'.
+
+The second problem is solved by giving the command code |make_box| to all
+control sequences that produce a box, and by using the following |chr_code|
+values to distinguish between them: |box_code|, |copy_code|, |last_box_code|,
+|vsplit_code|, |vtop_code|, |vtop_code+vmode|, and |vtop_code+hmode|, where
+the latter two are used to denote \.{\\vbox} and \.{\\hbox}, respectively.
+
+@d box_flag 010000000000 /*context code for `\.{\\setbox0}'*/
+@d global_box_flag 010000100000 /*context code for `\.{\\global\\setbox0}'*/
+@d ship_out_flag 010000200000 /*context code for `\.{\\shipout}'*/
+@d leader_flag 010000200001 /*context code for `\.{\\leaders}'*/
+@d box_code 0 /*|chr_code| for `\.{\\box}'*/
+@d copy_code 1 /*|chr_code| for `\.{\\copy}'*/
+@d last_box_code 2 /*|chr_code| for `\.{\\lastbox}'*/
+@d vsplit_code 3 /*|chr_code| for `\.{\\vsplit}'*/
+@d vtop_code 4 /*|chr_code| for `\.{\\vtop}'*/
+
+@<Put each...@>=
+primitive("moveleft", hmove, 1);
+@!@:move\_left\_}{\.{\\moveleft} primitive@>
+primitive("moveright", hmove, 0);@/
+@!@:move\_right\_}{\.{\\moveright} primitive@>
+primitive("raise", vmove, 1);
+@!@:raise\_}{\.{\\raise} primitive@>
+primitive("lower", vmove, 0);
+@!@:lower\_}{\.{\\lower} primitive@>
+@#
+primitive("box", make_box, box_code);
+@!@:box\_}{\.{\\box} primitive@>
+primitive("copy", make_box, copy_code);
+@!@:copy\_}{\.{\\copy} primitive@>
+primitive("lastbox", make_box, last_box_code);
+@!@:last\_box\_}{\.{\\lastbox} primitive@>
+primitive("vsplit", make_box, vsplit_code);
+@!@:vsplit\_}{\.{\\vsplit} primitive@>
+primitive("vtop", make_box, vtop_code);@/
+@!@:vtop\_}{\.{\\vtop} primitive@>
+primitive("vbox", make_box, vtop_code+vmode);
+@!@:vbox\_}{\.{\\vbox} primitive@>
+primitive("hbox", make_box, vtop_code+hmode);@/
+@!@:hbox\_}{\.{\\hbox} primitive@>
+primitive("shipout", leader_ship, a_leaders-1); /*|ship_out_flag==leader_flag-1|*/
+@!@:ship\_out\_}{\.{\\shipout} primitive@>
+primitive("leaders", leader_ship, a_leaders);
+@!@:leaders\_}{\.{\\leaders} primitive@>
+primitive("cleaders", leader_ship, c_leaders);
+@!@:c\_leaders\_}{\.{\\cleaders} primitive@>
+primitive("xleaders", leader_ship, x_leaders);
+@!@:x\_leaders\_}{\.{\\xleaders} primitive@>
+
+@ @<Cases of |print_cmd_chr|...@>=
+case hmove: if (chr_code==1) print_esc("moveleft");@+else print_esc("moveright");@+break;
+case vmove: if (chr_code==1) print_esc("raise");@+else print_esc("lower");@+break;
+case make_box: switch (chr_code) {
+ case box_code: print_esc("box");@+break;
+ case copy_code: print_esc("copy");@+break;
+ case last_box_code: print_esc("lastbox");@+break;
+ case vsplit_code: print_esc("vsplit");@+break;
+ case vtop_code: print_esc("vtop");@+break;
+ case vtop_code+vmode: print_esc("vbox");@+break;
+ default:print_esc("hbox");
+ } @+break;
+case leader_ship: if (chr_code==a_leaders) print_esc("leaders");
+ else if (chr_code==c_leaders) print_esc("cleaders");
+ else if (chr_code==x_leaders) print_esc("xleaders");
+ else print_esc("shipout");@+break;
+
+@ Constructions that require a box are started by calling |scan_box| with
+a specified context code. The |scan_box| routine verifies
+that a |make_box| command comes next and then it calls |begin_box|.
+
+@<Cases of |main_control| that build...@>=
+case vmode+hmove: case hmode+vmove: case mmode+vmove: {@+t=cur_chr;
+ scan_normal_dimen;
+ if (t==0) scan_box(cur_val);@+else scan_box(-cur_val);
+ } @+break;
+any_mode(leader_ship): scan_box(leader_flag-a_leaders+cur_chr);@+break;
+any_mode(make_box): begin_box(0);@+break;
+
+@ The global variable |cur_box| will point to a newly made box. If the box
+is void, we will have |cur_box==null|. Otherwise we will have
+|type(cur_box)==hlist_node| or |vlist_node| or |rule_node|; the |rule_node|
+case can occur only with leaders.
+
+@<Glob...@>=
+static pointer @!cur_box; /*box to be placed into its context*/
+
+@ The |box_end| procedure does the right thing with |cur_box|, if
+|box_context| represents the context as explained above.
+
+@<Declare act...@>=
+static void box_end(int @!box_context)
+{@+pointer p; /*|ord_noad| for new box in math mode*/
+small_number @!a; /*global prefix*/
+if (box_context < box_flag) @<Append box |cur_box| to the current list, shifted
+by |box_context|@>@;
+else if (box_context < ship_out_flag) @<Store \(c)|cur_box| in a box register@>@;
+else if (cur_box!=null)
+ if (box_context > ship_out_flag) @<Append a new leader node that uses |cur_box|@>@;
+ else ship_out(cur_box);
+}
+
+@ The global variable |adjust_tail| will be non-null if and only if the
+current box might include adjustments that should be appended to the
+current vertical list.
+
+@<Append box |cur_box| to the current...@>=
+{@+if (cur_box!=null)
+ {@+shift_amount(cur_box)=box_context;
+ if (abs(mode)==vmode)
+ {@+append_to_vlist(cur_box);
+ if (adjust_tail!=null)
+ {@+if (adjust_head!=adjust_tail)
+ {@+link(tail)=link(adjust_head);tail=adjust_tail;
+ }
+ adjust_tail=null;
+ }
+ if (mode > 0) build_page();
+ }
+ else{@+if (abs(mode)==hmode) space_factor=1000;
+ else{@+p=new_noad();
+ math_type(nucleus(p))=sub_box;
+ info(nucleus(p))=cur_box;cur_box=p;
+ }
+ link(tail)=cur_box;tail=cur_box;
+ }
+ }
+}
+
+@ @<Store \(c)|cur_box| in a box register@>=
+{@+if (box_context < global_box_flag)
+ {@+cur_val=box_context-box_flag;a=0;
+ }
+else{@+cur_val=box_context-global_box_flag;a=4;
+ }
+if (cur_val < 256) define(box_base+cur_val, box_ref, cur_box);
+else sa_def_box;
+}
+
+@ @<Append a new leader node...@>=
+{@+@<Get the next non-blank non-relax...@>;
+if (((cur_cmd==hskip)&&(abs(mode)!=vmode))||@|
+ ((cur_cmd==vskip)&&(abs(mode)==vmode)))
+ {@+append_glue();subtype(tail)=box_context-(leader_flag-a_leaders);
+ leader_ptr(tail)=cur_box;
+ }
+else{@+print_err("Leaders not followed by proper glue");
+@.Leaders not followed by...@>
+ help3("You should say `\\leaders <box or rule><hskip or vskip>'.",@/
+ "I found the <box or rule>, but there's no suitable",@/
+ "<hskip or vskip>, so I'm ignoring these leaders.");back_error();
+ flush_node_list(cur_box);
+ }
+}
+
+@ Now that we can see what eventually happens to boxes, we can consider
+the first steps in their creation. The |begin_box| routine is called when
+|box_context| is a context specification, |cur_chr| specifies the type of
+box desired, and |cur_cmd==make_box|.
+
+@<Declare act...@>=
+static void begin_box(int @!box_context)
+{@+
+pointer @!p, @!q; /*run through the current list*/
+int @!m; /*the length of a replacement list*/
+halfword @!k; /*0 or |vmode| or |hmode|*/
+halfword @!n; /*a box number*/
+switch (cur_chr) {
+case box_code: {@+scan_register_num();fetch_box(cur_box);
+ change_box(null); /*the box becomes void, at the same level*/
+ } @+break;
+case copy_code: {@+scan_register_num();fetch_box(q);cur_box=copy_node_list(q);
+ } @+break;
+case last_box_code: @<If the current list ends with a box node, delete it
+from the list and make |cur_box| point to it; otherwise set |cur_box:=null|@>@;@+break;
+case vsplit_code: @<Split off part of a vertical box, make |cur_box| point
+to it@>@;@+break;
+default:@<Initiate the construction of an hbox or vbox, then |return|@>@;
+} @/
+box_end(box_context); /*in simple cases, we use the box immediately*/
+}
+
+@ Note that the condition |!is_char_node(tail)| implies that |head!=tail|,
+since |head| is a one-word node.
+
+@<If the current list ends with a box node, delete it...@>=
+{@+cur_box=null;
+if (abs(mode)==mmode)
+ {@+you_cant();help1("Sorry; this \\lastbox will be void.");error();
+ }
+else if ((mode==vmode)&&(head==tail))
+ {@+you_cant();
+ help2("Sorry...I usually can't take things from the current page.",@/
+ "This \\lastbox will therefore be void.");error();
+ }
+else{@+if (!is_char_node(tail))
+ if ((type(tail)==hlist_node)||(type(tail)==vlist_node))
+ @<Remove the last box, unless it's part of a discretionary@>;
+ }
+}
+
+@ @<Remove the last box...@>=
+{@+q=head;
+@/do@+{p=q;
+if (!is_char_node(q)) if (type(q)==disc_node)
+ {@+for (m=1; m<=replace_count(q); m++) p=link(p);
+ if (p==tail) goto done;
+ }
+q=link(p);
+}@+ while (!(q==tail));
+cur_box=tail;shift_amount(cur_box)=0;
+tail=p;link(p)=null;
+done: ;}
+
+@ Here we deal with things like `\.{\\vsplit 13 to 100pt}'.
+
+@<Split off part of a vertical box, make |cur_box| point to it@>=
+{@+scan_register_num();n=cur_val;
+if (!scan_keyword("to"))
+@.to@>
+ {@+print_err("Missing `to' inserted");
+@.Missing `to' inserted@>
+ help2("I'm working on `\\vsplit<box number> to <dimen>';",@/
+ "will look for the <dimen> next.");error();
+ }
+scan_normal_dimen;
+cur_box=vsplit(n, cur_val);
+}
+
+@ Here is where we enter restricted horizontal mode or internal vertical
+mode, in order to make a box.
+
+@<Initiate the construction of an hbox or vbox, then |return|@>=
+{@+k=cur_chr-vtop_code;saved(0)=box_context;
+if (k==hmode)
+ if ((box_context < box_flag)&&(abs(mode)==vmode))
+ scan_spec(adjusted_hbox_group, true);
+ else scan_spec(hbox_group, true);
+else{@+if (k==vmode) scan_spec(vbox_group, true);
+ else{@+scan_spec(vtop_group, true);k=vmode;
+ }
+ normal_paragraph();
+ }
+push_nest();mode=-k;
+if (k==vmode)
+ {@+prev_depth=ignore_depth;
+ if (every_vbox!=null) begin_token_list(every_vbox, every_vbox_text);
+ }
+else{@+space_factor=1000;
+ if (every_hbox!=null) begin_token_list(every_hbox, every_hbox_text);
+ }
+return;
+}
+
+@ @<Declare act...@>=
+static void scan_box(int @!box_context)
+ /*the next input should specify a box or perhaps a rule*/
+{@+@<Get the next non-blank non-relax...@>;
+if (cur_cmd==make_box) begin_box(box_context);
+else if ((box_context >= leader_flag)&&((cur_cmd==hrule)||(cur_cmd==vrule)))
+ {@+cur_box=scan_rule_spec();box_end(box_context);
+ }
+else{@+@t@>@;@/
+ print_err("A <box> was supposed to be here");@/
+@.A <box> was supposed to...@>
+ help3("I was expecting to see \\hbox or \\vbox or \\copy or \\box or",@/
+ "something like that. So you might find something missing in",@/
+ "your output. But keep trying; you can fix this later.");back_error();
+ }
+}
+
+@ When the right brace occurs at the end of an \.{\\hbox} or \.{\\vbox} or
+\.{\\vtop} construction, the |package| routine comes into action. We might
+also have to finish a paragraph that hasn't ended.
+
+@<Cases of |handle...@>=
+case hbox_group: package(0);@+break;
+case adjusted_hbox_group: {@+adjust_tail=adjust_head;package(0);
+ } @+break;
+case vbox_group: {@+end_graf();package(0);
+ } @+break;
+case vtop_group: {@+end_graf();package(vtop_code);
+ } @+break;
+
+@ @<Declare action...@>=
+static void package(small_number @!c)
+{@+scaled h; /*height of box*/
+pointer @!p; /*first node in a box*/
+scaled @!d; /*max depth*/
+d=box_max_depth;unsave();save_ptr=save_ptr-3;
+if (mode==-hmode) cur_box=hpack(link(head), saved(2), saved(1));
+else{@+cur_box=vpackage(link(head), saved(2), saved(1), d);
+ if (c==vtop_code) @<Readjust the height and depth of |cur_box|, for \.{\\vtop}@>;
+ }
+pop_nest();box_end(saved(0));
+}
+
+@ The height of a `\.{\\vtop}' box is inherited from the first item on its list,
+if that item is an |hlist_node|, |vlist_node|, or |rule_node|; otherwise
+the \.{\\vtop} height is zero.
+
+
+@<Readjust the height...@>=
+{@+h=0;p=list_ptr(cur_box);
+if (p!=null) if (type(p) <= rule_node) h=height(p);
+depth(cur_box)=depth(cur_box)-h+height(cur_box);height(cur_box)=h;
+}
+
+@ A paragraph begins when horizontal-mode material occurs in vertical mode,
+or when the paragraph is explicitly started by `\.{\\indent}' or
+`\.{\\noindent}'.
+
+@<Put each...@>=
+primitive("indent", start_par, 1);
+@!@:indent\_}{\.{\\indent} primitive@>
+primitive("noindent", start_par, 0);
+@!@:no\_indent\_}{\.{\\noindent} primitive@>
+
+@ @<Cases of |print_cmd_chr|...@>=
+case start_par: if (chr_code==0) print_esc("noindent");@+else print_esc("indent");@+break;
+
+@ @<Cases of |main_control| that build...@>=
+case vmode+start_par: new_graf(cur_chr > 0);@+break;
+case vmode+letter: case vmode+other_char:
+ case vmode+char_num: case vmode+char_given:
+ case vmode+math_shift: case vmode+un_hbox:
+ case vmode+vrule:
+ case vmode+accent: case vmode+discretionary:
+ case vmode+hskip: case vmode+valign:
+ case vmode+ex_space: case vmode+no_boundary: @t@>@;@/
+ {@+back_input();new_graf(true);
+ } @+break;
+
+@ @<Declare act...@>=
+static small_number norm_min(int @!h)
+{@+if (h <= 0) return 1;@+else if (h >= 63) return 63;@+
+else return h;
+}
+@#
+static void new_graf(bool @!indented)
+{@+prev_graf=0;
+if ((mode==vmode)||(head!=tail))
+ tail_append(new_param_glue(par_skip_code));
+push_nest();mode=hmode;space_factor=1000;set_cur_lang;clang=cur_lang;
+prev_graf=(norm_min(left_hyphen_min)*0100+norm_min(right_hyphen_min))
+ *0200000+cur_lang;
+if (indented)
+ {@+tail=new_null_box();link(head)=tail;width(tail)=par_indent;@+
+ }
+if (every_par!=null) begin_token_list(every_par, every_par_text);
+if (nest_ptr==1) build_page(); /*put |par_skip| glue on current page*/
+}
+
+@ @<Cases of |main_control| that build...@>=
+case hmode+start_par: case mmode+start_par: indent_in_hmode();@+break;
+
+@ @<Declare act...@>=
+static void indent_in_hmode(void)
+{@+pointer p, @!q;
+if (cur_chr > 0) /*\.{\\indent}*/
+ {@+p=new_null_box();width(p)=par_indent;
+ if (abs(mode)==hmode) space_factor=1000;
+ else{@+q=new_noad();math_type(nucleus(q))=sub_box;
+ info(nucleus(q))=p;p=q;
+ }
+ tail_append(p);
+ }
+}
+
+@ A paragraph ends when a |par_end| command is sensed, or when we are in
+horizontal mode when reaching the right brace of vertical-mode routines
+like \.{\\vbox}, \.{\\insert}, or \.{\\output}.
+
+@<Cases of |main_control| that build...@>=
+case vmode+par_end: {@+normal_paragraph();
+ if (mode > 0) build_page();
+ } @+break;
+case hmode+par_end: {@+if (align_state < 0) off_save(); /*this tries to
+ recover from an alignment that didn't end properly*/
+ end_graf(); /*this takes us to the enclosing mode, if |mode > 0|*/
+ if (mode==vmode) build_page();
+ } @+break;
+case hmode+stop: case hmode+vskip: case hmode+hrule:
+ case hmode+un_vbox: case hmode+halign: head_for_vmode();@+break;
+
+@ @<Declare act...@>=
+static void head_for_vmode(void)
+{@+if (mode < 0)
+ if (cur_cmd!=hrule) off_save();
+ else{@+print_err("You can't use `");
+ print_esc("hrule");print("' here except with leaders");
+@.You can't use \\hrule...@>
+ help2("To put a horizontal rule in an hbox or an alignment,",@/
+ "you should use \\leaders or \\hrulefill (see The TeXbook).");
+ error();
+ }
+else{@+back_input();cur_tok=par_token;back_input();token_type=inserted;
+ }
+}
+
+@ @<Declare act...@>=
+static void end_graf(void)
+{@+if (mode==hmode)
+ {@+if (head==tail) pop_nest(); /*null paragraphs are ignored*/
+ else line_break(widow_penalty);
+ normal_paragraph();
+ error_count=0;
+ }
+}
+
+@ Insertion and adjustment and mark nodes are constructed by the following
+pieces of the program.
+
+@<Cases of |main_control| that build...@>=
+any_mode(insert): case hmode+vadjust:
+ case mmode+vadjust: begin_insert_or_adjust();@+break;
+any_mode(mark): make_mark();@+break;
+
+@ @<Forbidden...@>=
+case vmode+vadjust:
+
+@ @<Declare act...@>=
+static void begin_insert_or_adjust(void)
+{@+if (cur_cmd==vadjust) cur_val=255;
+else{@+scan_eight_bit_int();
+ if (cur_val==255)
+ {@+print_err("You can't ");print_esc("insert");print_int(255);
+@.You can't \\insert255@>
+ help1("I'm changing to \\insert0; box 255 is special.");
+ error();cur_val=0;
+ }
+ }
+saved(0)=cur_val;incr(save_ptr);
+new_save_level(insert_group);scan_left_brace();normal_paragraph();
+push_nest();mode=-vmode;prev_depth=ignore_depth;
+}
+
+@ @<Cases of |handle...@>=
+case insert_group: {@+end_graf();q=split_top_skip;add_glue_ref(q);
+ d=split_max_depth;f=floating_penalty;unsave();decr(save_ptr);
+ /*now |saved(0)| is the insertion number, or 255 for |vadjust|*/
+ p=vpack(link(head), natural);pop_nest();
+ if (saved(0) < 255)
+ {@+tail_append(get_node(ins_node_size));
+ type(tail)=ins_node;subtype(tail)=qi(saved(0));
+ height(tail)=height(p)+depth(p);ins_ptr(tail)=list_ptr(p);
+ split_top_ptr(tail)=q;depth(tail)=d;float_cost(tail)=f;
+ }
+ else{@+tail_append(get_node(small_node_size));
+ type(tail)=adjust_node;@/
+ subtype(tail)=0; /*the |subtype| is not used*/
+ adjust_ptr(tail)=list_ptr(p);delete_glue_ref(q);
+ }
+ free_node(p, box_node_size);
+ if (nest_ptr==0) build_page();
+ } @+break;
+case output_group: @<Resume the page builder...@>@;@+break;
+
+@ @<Declare act...@>=
+static void make_mark(void)
+{@+pointer p; /*new node*/
+halfword @!c; /*the mark class*/
+if (cur_chr==0) c=0;
+else{@+scan_register_num();c=cur_val;
+ }
+p=scan_toks(false, true);p=get_node(small_node_size);
+mark_class(p)=c;
+type(p)=mark_node;subtype(p)=0; /*the |subtype| is not used*/
+mark_ptr(p)=def_ref;link(tail)=p;tail=p;
+}
+
+@ Penalty nodes get into a list via the |break_penalty| command.
+@^penalties@>
+
+@<Cases of |main_control| that build...@>=
+any_mode(break_penalty): append_penalty();@+break;
+
+@ @<Declare action...@>=
+static void append_penalty(void)
+{@+scan_int();tail_append(new_penalty(cur_val));
+if (mode==vmode) build_page();
+}
+
+@ The |remove_item| command removes a penalty, kern, or glue node if it
+appears at the tail of the current list, using a brute-force linear scan.
+Like \.{\\lastbox}, this command is not allowed in vertical mode (except
+internal vertical mode), since the current list in vertical mode is sent
+to the page builder. But if we happen to be able to implement it in
+vertical mode, we do.
+
+@<Cases of |main_control| that build...@>=
+any_mode(remove_item): delete_last();@+break;
+
+@ When |delete_last| is called, |cur_chr| is the |type| of node that
+will be deleted, if present.
+
+@<Declare action...@>=
+static void delete_last(void)
+{@+
+pointer @!p, @!q; /*run through the current list*/
+int @!m; /*the length of a replacement list*/
+if ((mode==vmode)&&(tail==head))
+ @<Apologize for inability to do the operation now, unless \.{\\unskip} follows
+non-glue@>@;
+else{@+if (!is_char_node(tail)) if (type(tail)==cur_chr)
+ {@+q=head;
+ @/do@+{p=q;
+ if (!is_char_node(q)) if (type(q)==disc_node)
+ {@+for (m=1; m<=replace_count(q); m++) p=link(p);
+ if (p==tail) return;
+ }
+ q=link(p);
+ }@+ while (!(q==tail));
+ link(p)=null;flush_node_list(tail);tail=p;
+ }
+ }
+}
+
+@ @<Apologize for inability to do the operation...@>=
+{@+if ((cur_chr!=glue_node)||(last_glue!=max_halfword))
+ {@+you_cant();
+ help2("Sorry...I usually can't take things from the current page.",@/
+ "Try `I\\vskip-\\lastskip' instead.");
+ if (cur_chr==kern_node) help_line[0]=
+ ("Try `I\\kern-\\lastkern' instead.");
+ else if (cur_chr!=glue_node) help_line[0]=@|
+ ("Perhaps you can make the output routine do it.");
+ error();
+ }
+}
+
+@ @<Put each...@>=
+primitive("unpenalty", remove_item, penalty_node);@/
+@!@:un\_penalty\_}{\.{\\unpenalty} primitive@>
+primitive("unkern", remove_item, kern_node);@/
+@!@:un\_kern\_}{\.{\\unkern} primitive@>
+primitive("unskip", remove_item, glue_node);@/
+@!@:un\_skip\_}{\.{\\unskip} primitive@>
+primitive("unhbox", un_hbox, box_code);@/
+@!@:un\_hbox\_}{\.{\\unhbox} primitive@>
+primitive("unhcopy", un_hbox, copy_code);@/
+@!@:un\_hcopy\_}{\.{\\unhcopy} primitive@>
+primitive("unvbox", un_vbox, box_code);@/
+@!@:un\_vbox\_}{\.{\\unvbox} primitive@>
+primitive("unvcopy", un_vbox, copy_code);@/
+@!@:un\_vcopy\_}{\.{\\unvcopy} primitive@>
+
+@ @<Cases of |print_cmd_chr|...@>=
+case remove_item: if (chr_code==glue_node) print_esc("unskip");
+ else if (chr_code==kern_node) print_esc("unkern");
+ else print_esc("unpenalty");@+break;
+case un_hbox: if (chr_code==copy_code) print_esc("unhcopy");
+ else print_esc("unhbox");@+break;
+case un_vbox: if (chr_code==copy_code) print_esc("unvcopy")
+ @<Cases of |un_vbox| for |print_cmd_chr|@>;@/
+ else print_esc("unvbox");@+break;
+
+@ The |un_hbox| and |un_vbox| commands unwrap one of the 256 current boxes.
+
+@<Cases of |main_control| that build...@>=
+case vmode+un_vbox: case hmode+un_hbox:
+ case mmode+un_hbox: unpackage();@+break;
+
+@ @<Declare act...@>=
+static void unpackage(void)
+{@+
+pointer p; /*the box*/
+int @!c; /*should we copy?*/
+if (cur_chr > copy_code) @<Handle saved items and |goto done|@>;
+c=cur_chr;scan_register_num();fetch_box(p);
+if (p==null) return;
+if ((abs(mode)==mmode)||((abs(mode)==vmode)&&(type(p)!=vlist_node))||@|
+ ((abs(mode)==hmode)&&(type(p)!=hlist_node)))
+ {@+print_err("Incompatible list can't be unboxed");
+@.Incompatible list...@>
+ help3("Sorry, Pandora. (You sneaky devil.)",@/
+ "I refuse to unbox an \\hbox in vertical mode or vice versa.",@/
+ "And I can't open any boxes in math mode.");@/
+ error();return;
+ }
+if (c==copy_code) link(tail)=copy_node_list(list_ptr(p));
+else{@+link(tail)=list_ptr(p);change_box(null);
+ free_node(p, box_node_size);
+ }
+done:
+while (link(tail)!=null) tail=link(tail);
+}
+
+@ @<Forbidden...@>=case vmode+ital_corr:
+
+@ Italic corrections are converted to kern nodes when the |ital_corr| command
+follows a character. In math mode the same effect is achieved by appending
+a kern of zero here, since italic corrections are supplied later.
+
+@<Cases of |main_control| that build...@>=
+case hmode+ital_corr: append_italic_correction();@+break;
+case mmode+ital_corr: tail_append(new_kern(0))@;@+break;
+
+@ @<Declare act...@>=
+static void append_italic_correction(void)
+{@+
+pointer p; /*|char_node| at the tail of the current list*/
+internal_font_number @!f; /*the font in the |char_node|*/
+if (tail!=head)
+ {@+if (is_char_node(tail)) p=tail;
+ else if (type(tail)==ligature_node) p=lig_char(tail);
+ else return;
+ f=font(p);
+ tail_append(new_kern(char_italic(f, char_info(f, character(p)))));
+ subtype(tail)=explicit;
+ }
+}
+
+@ Discretionary nodes are easy in the common case `\.{\\-}', but in the
+general case we must process three braces full of items.
+
+@<Put each...@>=
+primitive("-", discretionary, 1);
+@!@:Single-character primitives -}{\quad\.{\\-}@>
+primitive("discretionary", discretionary, 0);
+@!@:discretionary\_}{\.{\\discretionary} primitive@>
+
+@ @<Cases of |print_cmd_chr|...@>=
+case discretionary: if (chr_code==1)
+ print_esc("-");@+else print_esc("discretionary");@+break;
+
+@ @<Cases of |main_control| that build...@>=
+case hmode+discretionary: case mmode+discretionary: append_discretionary();@+break;
+
+@ The space factor does not change when we append a discretionary node,
+but it starts out as 1000 in the subsidiary lists.
+
+@<Declare act...@>=
+static void append_discretionary(void)
+{@+int c; /*hyphen character*/
+tail_append(new_disc());
+if (cur_chr==1)
+ {@+c=hyphen_char[cur_font];
+ if (c >= 0) if (c < 256) pre_break(tail)=new_character(cur_font, c);
+ }
+else{@+incr(save_ptr);saved(-1)=0;new_save_level(disc_group);
+ scan_left_brace();push_nest();mode=-hmode;space_factor=1000;
+ }
+}
+
+@ The three discretionary lists are constructed somewhat as if they were
+hboxes. A~subroutine called |build_discretionary| handles the transitions.
+(This is sort of fun.)
+
+@<Cases of |handle...@>=
+case disc_group: build_discretionary();@+break;
+
+@ @<Declare act...@>=
+static void build_discretionary(void)
+{@+
+pointer p, @!q; /*for link manipulation*/
+int @!n; /*length of discretionary list*/
+unsave();
+@<Prune the current list, if necessary, until it contains only |char_node|,
+|kern_node|, |hlist_node|, |vlist_node|, |rule_node|, and |ligature_node|
+items; set |n| to the length of the list, and set |q| to the list's tail@>;
+p=link(head);pop_nest();
+switch (saved(-1)) {
+case 0: pre_break(tail)=p;@+break;
+case 1: post_break(tail)=p;@+break;
+case 2: @<Attach list |p| to the current list, and record its length; then
+finish up and |return|@>;
+} /*there are no other cases*/
+incr(saved(-1));new_save_level(disc_group);scan_left_brace();
+push_nest();mode=-hmode;space_factor=1000;
+}
+
+@ @<Attach list |p| to the current...@>=
+{@+if ((n > 0)&&(abs(mode)==mmode))
+ {@+print_err("Illegal math ");print_esc("discretionary");
+@.Illegal math \\disc...@>
+ help2("Sorry: The third part of a discretionary break must be",@/
+ "empty, in math formulas. I had to delete your third part.");
+ flush_node_list(p);n=0;error();
+ }
+else link(tail)=p;
+if (n <= max_quarterword) replace_count(tail)=n;
+else{@+print_err("Discretionary list is too long");
+@.Discretionary list is too long@>
+ help2("Wow---I never thought anybody would tweak me here.",@/
+ "You can't seriously need such a huge discretionary list?");
+ error();
+ }
+if (n > 0) tail=q;
+decr(save_ptr);return;
+}
+
+@ During this loop, |p==link(q)| and there are |n| items preceding |p|.
+
+@<Prune the current list, if necessary...@>=
+q=head;p=link(q);n=0;
+while (p!=null)
+ {@+if (!is_char_node(p)) if (type(p) > rule_node)
+ if (type(p)!=kern_node) if (type(p)!=ligature_node)
+ {@+print_err("Improper discretionary list");
+@.Improper discretionary list@>
+ help1("Discretionary lists must contain only boxes and kerns.");@/
+ error();
+ begin_diagnostic();
+ print_nl("The following discretionary sublist has been deleted:");
+@.The following...deleted@>
+ show_box(p);
+ end_diagnostic(true);
+ flush_node_list(p);link(q)=null;goto done;
+ }
+ q=p;p=link(q);incr(n);
+ }
+done:
+
+@ We need only one more thing to complete the horizontal mode routines, namely
+the \.{\\accent} primitive.
+
+@<Cases of |main_control| that build...@>=
+case hmode+accent: make_accent();@+break;
+
+@ The positioning of accents is straightforward but tedious. Given an accent
+of width |a|, designed for characters of height |x| and slant |s|;
+and given a character of width |w|, height |h|, and slant |t|: We will shift
+the accent down by |x-h|, and we will insert kern nodes that have the effect of
+centering the accent over the character and shifting the accent to the
+right by $\delta={1\over2}(w-a)+h\cdot t-x\cdot s$. If either character is
+absent from the font, we will simply use the other, without shifting.
+
+@<Declare act...@>=
+static void make_accent(void)
+{@+double s, @!t; /*amount of slant*/
+pointer @!p, @!q, @!r; /*character, box, and kern nodes*/
+internal_font_number @!f; /*relevant font*/
+scaled @!a, @!h, @!x, @!w, @!delta; /*heights and widths, as explained above*/
+four_quarters @!i; /*character information*/
+scan_char_num();f=cur_font;p=new_character(f, cur_val);
+if (p!=null)
+ {@+x=x_height(f);s=slant(f)/float_constant(65536);
+@^real division@>
+ a=char_width(f, char_info(f, character(p)));@/
+ do_assignments();@/
+ @<Create a character node |q| for the next character, but set |q:=null|
+if problems arise@>;
+ if (q!=null) @<Append the accent with appropriate kerns, then set |p:=q|@>;
+ link(tail)=p;tail=p;space_factor=1000;
+ }
+}
+
+@ @<Create a character node |q| for the next...@>=
+q=null;f=cur_font;
+if ((cur_cmd==letter)||(cur_cmd==other_char)||(cur_cmd==char_given))
+ q=new_character(f, cur_chr);
+else if (cur_cmd==char_num)
+ {@+scan_char_num();q=new_character(f, cur_val);
+ }
+else back_input()
+
+@ The kern nodes appended here must be distinguished from other kerns, lest
+they be wiped away by the hyphenation algorithm or by a previous line break.
+
+The two kerns are computed with (machine-dependent) |double| arithmetic, but
+their sum is machine-independent; the net effect is machine-independent,
+because the user cannot remove these nodes nor access them via \.{\\lastkern}.
+
+@<Append the accent with appropriate kerns...@>=
+{@+t=slant(f)/float_constant(65536);
+@^real division@>
+i=char_info(f, character(q));
+w=char_width(f, i);h=char_height(f, height_depth(i));
+if (h!=x) /*the accent must be shifted up or down*/
+ {@+p=hpack(p, natural);shift_amount(p)=x-h;
+ }
+delta=round((w-a)/float_constant(2)+h*t-x*s);
+@^real multiplication@>
+@^real addition@>
+r=new_kern(delta);subtype(r)=acc_kern;link(tail)=r;link(r)=p;
+tail=new_kern(-a-delta);subtype(tail)=acc_kern;link(p)=tail;p=q;
+}
+
+@ When `\.{\\cr}' or `\.{\\span}' or a tab mark comes through the scanner
+into |main_control|, it might be that the user has foolishly inserted
+one of them into something that has nothing to do with alignment. But it is
+far more likely that a left brace or right brace has been omitted, since
+|get_next| takes actions appropriate to alignment only when `\.{\\cr}'
+or `\.{\\span}' or tab marks occur with |align_state==0|. The following
+program attempts to make an appropriate recovery.
+
+@<Cases of |main_control| that build...@>=
+any_mode(car_ret): any_mode(tab_mark): align_error();@+break;
+any_mode(no_align): no_align_error();@+break;
+any_mode(omit): omit_error();@+break;
+
+@ @<Declare act...@>=
+static void align_error(void)
+{@+if (abs(align_state) > 2)
+ @<Express consternation over the fact that no alignment is in progress@>@;
+else{@+back_input();
+ if (align_state < 0)
+ {@+print_err("Missing { inserted");
+@.Missing \{ inserted@>
+ incr(align_state);cur_tok=left_brace_token+'{';
+ }
+ else{@+print_err("Missing } inserted");
+@.Missing \} inserted@>
+ decr(align_state);cur_tok=right_brace_token+'}';
+ }
+ help3("I've put in what seems to be necessary to fix",@/
+ "the current column of the current alignment.",@/
+ "Try to go on, since this might almost work.");ins_error();
+ }
+}
+
+@ @<Express consternation...@>=
+{@+print_err("Misplaced ");print_cmd_chr(cur_cmd, cur_chr);
+@.Misplaced \&@>
+@.Misplaced \\span@>
+@.Misplaced \\cr@>
+if (cur_tok==tab_token+'&')
+ {@+help6("I can't figure out why you would want to use a tab mark",@/
+ "here. If you just want an ampersand, the remedy is",@/
+ "simple: Just type `I\\&' now. But if some right brace",@/
+ "up above has ended a previous alignment prematurely,",@/
+ "you're probably due for more error messages, and you",@/
+ "might try typing `S' now just to see what is salvageable.");
+ }
+else{@+help5("I can't figure out why you would want to use a tab mark",@/
+ "or \\cr or \\span just now. If something like a right brace",@/
+ "up above has ended a previous alignment prematurely,",@/
+ "you're probably due for more error messages, and you",@/
+ "might try typing `S' now just to see what is salvageable.");
+ }
+error();
+}
+
+@ The help messages here contain a little white lie, since \.{\\noalign}
+and \.{\\omit} are allowed also after `\.{\\noalign\{...\}}'.
+
+@<Declare act...@>=
+static void no_align_error(void)
+{@+print_err("Misplaced ");print_esc("noalign");
+@.Misplaced \\noalign@>
+help2("I expect to see \\noalign only after the \\cr of",@/
+ "an alignment. Proceed, and I'll ignore this case.");error();
+}
+static void omit_error(void)
+{@+print_err("Misplaced ");print_esc("omit");
+@.Misplaced \\omit@>
+help2("I expect to see \\omit only after tab marks or the \\cr of",@/
+ "an alignment. Proceed, and I'll ignore this case.");error();
+}
+
+@ We've now covered most of the abuses of \.{\\halign} and \.{\\valign}.
+Let's take a look at what happens when they are used correctly.
+
+@<Cases of |main_control| that build...@>=
+case vmode+halign: case hmode+valign: init_align();@+break;
+case mmode+halign: if (privileged())
+ if (cur_group==math_shift_group) init_align();
+ else off_save();@+break;
+case vmode+endv: case hmode+endv: do_endv();@+break;
+
+@ An |align_group| code is supposed to remain on the |save_stack|
+during an entire alignment, until |fin_align| removes it.
+
+A devious user might force an |endv| command to occur just about anywhere;
+we must defeat such hacks.
+
+@<Declare act...@>=
+static void do_endv(void)
+{@+base_ptr=input_ptr;input_stack[base_ptr]=cur_input;
+while ((input_stack[base_ptr].index_field!=v_template)&&
+ (input_stack[base_ptr].loc_field==null)&&
+ (input_stack[base_ptr].state_field==token_list)) decr(base_ptr);
+if ((input_stack[base_ptr].index_field!=v_template)||
+ (input_stack[base_ptr].loc_field!=null)||
+ (input_stack[base_ptr].state_field!=token_list))
+ fatal_error("(interwoven alignment preambles are not allowed)");
+@.interwoven alignment preambles...@>
+ if (cur_group==align_group)
+ {@+end_graf();
+ if (fin_col()) fin_row();
+ }
+else off_save();
+}
+
+@ @<Cases of |handle_right_brace|...@>=
+case align_group: {@+back_input();cur_tok=cs_token_flag+frozen_cr;
+ print_err("Missing ");print_esc("cr");print(" inserted");
+@.Missing \\cr inserted@>
+ help1("I'm guessing that you meant to end an alignment here.");
+ ins_error();
+ } @+break;
+
+@ @<Cases of |handle_right_brace|...@>=
+case no_align_group: {@+end_graf();unsave();align_peek();
+ } @+break;
+
+@ Finally, \.{\\endcsname} is not supposed to get through to |main_control|.
+
+@<Cases of |main_control| that build...@>=
+any_mode(end_cs_name): cs_error();@+break;
+
+@ @<Declare act...@>=
+static void cs_error(void)
+{@+print_err("Extra ");print_esc("endcsname");
+@.Extra \\endcsname@>
+help1("I'm ignoring this, since I wasn't doing a \\csname.");
+error();
+}
+
+@* Building math lists.
+The routines that \TeX\ uses to create mlists are similar to those we have
+just seen for the generation of hlists and vlists. But it is necessary to
+make ``noads'' as well as nodes, so the reader should review the
+discussion of math mode data structures before trying to make sense out of
+the following program.
+
+Here is a little routine that needs to be done whenever a subformula
+is about to be processed. The parameter is a code like |math_group|.
+
+@<Declare act...@>=
+static void push_math(group_code @!c)
+{@+push_nest();mode=-mmode;incompleat_noad=null;new_save_level(c);
+}
+
+@ We get into math mode from horizontal mode when a `\.\$' (i.e., a
+|math_shift| character) is scanned. We must check to see whether this
+`\.\$' is immediately followed by another, in case display math mode is
+called for.
+
+@<Cases of |main_control| that build...@>=
+case hmode+math_shift: init_math();@+break;
+
+@ @<Declare act...@>=
+static void init_math(void)
+{@+
+scaled w; /*new or partial |pre_display_size|*/
+scaled @!l; /*new |display_width|*/
+scaled @!s; /*new |display_indent|*/
+pointer @!p; /*current node when calculating |pre_display_size|*/
+pointer @!q; /*glue specification when calculating |pre_display_size|*/
+internal_font_number @!f; /*font in current |char_node|*/
+int @!n; /*scope of paragraph shape specification*/
+scaled @!v; /*|w| plus possible glue amount*/
+scaled @!d; /*increment to |v|*/
+get_token(); /*|get_x_token| would fail on \.{\\ifmmode}\thinspace!*/
+if ((cur_cmd==math_shift)&&(mode > 0)) @<Go into display math mode@>@;
+else{@+back_input();@<Go into ordinary math mode@>;
+ }
+}
+
+@ @<Go into ordinary math mode@>=
+{@+push_math(math_shift_group);eq_word_define(int_base+cur_fam_code,-1);
+if (every_math!=null) begin_token_list(every_math, every_math_text);
+}
+
+@ We get into ordinary math mode from display math mode when `\.{\\eqno}' or
+`\.{\\leqno}' appears. In such cases |cur_chr| will be 0 or~1, respectively;
+the value of |cur_chr| is placed onto |save_stack| for safe keeping.
+
+@<Cases of |main_control| that build...@>=
+case mmode+eq_no: if (privileged())
+ if (cur_group==math_shift_group) start_eq_no();
+ else off_save();@+break;
+
+@ @<Put each...@>=
+primitive("eqno", eq_no, 0);
+@!@:eq\_no\_}{\.{\\eqno} primitive@>
+primitive("leqno", eq_no, 1);
+@!@:leq\_no\_}{\.{\\leqno} primitive@>
+
+@ When \TeX\ is in display math mode, |cur_group==math_shift_group|,
+so it is not necessary for the |start_eq_no| procedure to test for
+this condition.
+
+@<Declare act...@>=
+static void start_eq_no(void)
+{@+saved(0)=cur_chr;incr(save_ptr);
+@<Go into ordinary math mode@>;
+}
+
+@ @<Cases of |print_cmd_chr|...@>=
+case eq_no: if (chr_code==1) print_esc("leqno");@+else print_esc("eqno");@+break;
+
+@ @<Forbidden...@>=non_math(eq_no):
+
+@ When we enter display math mode, we need to call |line_break| to
+process the partial paragraph that has just been interrupted by the
+display. Then we can set the proper values of |display_width| and
+|display_indent| and |pre_display_size|.
+
+@<Go into display math mode@>=
+{@+if (head==tail) /*`\.{\\noindent\$\$}' or `\.{\$\${ }\$\$}'*/
+ {@+pop_nest();w=-max_dimen;
+ }
+else{@+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 on that line is affected by stretching or shrinking@>;
+ }
+ /*now we are in vertical mode, working on the list that will contain the display*/
+@<Calculate the length, |l|, and the shift amount, |s|, of the display lines@>;
+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);
+eq_word_define(dimen_base+display_width_code, l);
+eq_word_define(dimen_base+display_indent_code, s);
+if (every_display!=null) begin_token_list(every_display, every_display_text);
+if (nest_ptr==1) build_page();
+}
+
+@ @<Calculate the natural width, |w|, by which...@>=
+v=shift_amount(just_box)+2*quad(cur_font);w=-max_dimen;
+p=list_ptr(just_box);
+while (p!=null)
+ {@+@<Let |d| be the natural width of node |p|; if the node is ``visible,''
+|goto found|; if the node is glue that stretches or shrinks, set |v:=max_dimen|@>;
+ if (v < max_dimen) v=v+d;
+ goto not_found;
+ found: if (v < max_dimen)
+ {@+v=v+d;w=v;
+ }
+ else{@+w=max_dimen;goto done;
+ }
+ not_found: p=link(p);
+ }
+done:
+
+@ @<Let |d| be the natural width of node |p|...@>=
+reswitch: if (is_char_node(p))
+ {@+f=font(p);d=char_width(f, char_info(f, character(p)));
+ goto found;
+ }
+switch (type(p)) {
+case hlist_node: case vlist_node: case rule_node: {@+d=width(p);goto found;
+ }
+case ligature_node: @<Make node |p| look like a |char_node|...@>@;
+case kern_node: case math_node: d=width(p);@+break;
+case 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@>@;@+break;
+case whatsit_node: @<Let |d| be the width of the whatsit |p|@>;@+break;
+default:d=0;
+}
+
+@ We need to be careful that |w|, |v|, and |d| do not depend on any |glue_set|
+values, since such values are subject to system-dependent rounding.
+System-dependent numbers are not allowed to infiltrate parameters like
+|pre_display_size|, since \TeX82 is supposed to make the same decisions on all
+machines.
+
+@<Let |d| be the natural width of this glue...@>=
+{@+q=glue_ptr(p);d=width(q);
+if (glue_sign(just_box)==stretching)
+ {@+if ((glue_order(just_box)==stretch_order(q))&&@|
+ (stretch(q)!=0))
+ v=max_dimen;
+ }
+else if (glue_sign(just_box)==shrinking)
+ {@+if ((glue_order(just_box)==shrink_order(q))&&@|
+ (shrink(q)!=0))
+ v=max_dimen;
+ }
+if (subtype(p) >= a_leaders) goto found;
+}
+
+@ A displayed equation is considered to be three lines long, so we
+calculate the length and offset of line number |prev_graf+2|.
+
+@<Calculate the length, |l|,...@>=
+if (par_shape_ptr==null)
+ if ((hang_indent!=0)&&@|
+ (((hang_after >= 0)&&(prev_graf+2 > hang_after))||@|
+ (prev_graf+1 < -hang_after)))
+ {@+l=hsize-abs(hang_indent);
+ if (hang_indent > 0) s=hang_indent;@+else s=0;
+ }
+ else{@+l=hsize;s=0;
+ }
+else{@+n=info(par_shape_ptr);
+ if (prev_graf+2 >= n) p=par_shape_ptr+2*n;
+ else p=par_shape_ptr+2*(prev_graf+2);
+ s=mem[p-1].sc;l=mem[p].sc;
+ }
+
+@ Subformulas of math formulas cause a new level of math mode to be entered,
+on the semantic nest as well as the save stack. These subformulas arise in
+several ways: (1)~A left brace by itself indicates the beginning of a
+subformula that will be put into a box, thereby freezing its glue and
+preventing line breaks. (2)~A subscript or superscript is treated as a
+subformula if it is not a single character; the same applies to
+the nucleus of things like \.{\\underline}. (3)~The \.{\\left} primitive
+initiates a subformula that will be terminated by a matching \.{\\right}.
+The group codes placed on |save_stack| in these three cases are
+|math_group|, |math_group|, and |math_left_group|, respectively.
+
+Here is the code that handles case (1); the other cases are not quite as
+trivial, so we shall consider them later.
+
+@<Cases of |main_control| that build...@>=
+case mmode+left_brace: {@+tail_append(new_noad());
+ back_input();scan_math(nucleus(tail));
+ } @+break;
+
+@ Recall that the |nucleus|, |subscr|, and |supscr| fields in a noad are
+broken down into subfields called |math_type| and either |info| or
+|(fam, character)|. The job of |scan_math| is to figure out what to place
+in one of these principal fields; it looks at the subformula that
+comes next in the input, and places an encoding of that subformula
+into a given word of |mem|.
+
+@d fam_in_range ((cur_fam >= 0)&&(cur_fam < 16))
+
+@<Declare act...@>=
+static void scan_math(pointer @!p)
+{@+
+int c; /*math character code*/
+restart: @<Get the next non-blank non-relax...@>;
+reswitch: switch (cur_cmd) {
+case letter: case other_char: case char_given: {@+c=ho(math_code(cur_chr));
+ if (c==0100000)
+ {@+@<Treat |cur_chr| as an active character@>;
+ goto restart;
+ }
+ } @+break;
+case char_num: {@+scan_char_num();cur_chr=cur_val;cur_cmd=char_given;
+ goto reswitch;
+ }
+case math_char_num: {@+scan_fifteen_bit_int();c=cur_val;
+ } @+break;
+case math_given: c=cur_chr;@+break;
+case delim_num: {@+scan_twenty_seven_bit_int();c=cur_val/010000;
+ } @+break;
+default:@<Scan a subformula enclosed in braces and |return|@>@;
+} @/
+math_type(p)=math_char;character(p)=qi(c%256);
+if ((c >= var_code)&&fam_in_range) fam(p)=cur_fam;
+else fam(p)=(c/256)%16;
+}
+
+@ An active character that is an |outer_call| is allowed here.
+
+@<Treat |cur_chr|...@>=
+{@+cur_cs=cur_chr+active_base;
+cur_cmd=eq_type(cur_cs);cur_chr=equiv(cur_cs);
+x_token();back_input();
+}
+
+@ The pointer |p| is placed on |save_stack| while a complex subformula
+is being scanned.
+
+@<Scan a subformula...@>=
+{@+back_input();scan_left_brace();@/
+saved(0)=p;incr(save_ptr);push_math(math_group);return;
+}
+
+@ The simplest math formula is, of course, `\.{\${ }\$}', when no noads are
+generated. The next simplest cases involve a single character, e.g.,
+`\.{\$x\$}'. Even though such cases may not seem to be very interesting,
+the reader can perhaps understand how happy the author was when `\.{\$x\$}'
+was first properly typeset by \TeX. The code in this section was used.
+@^Knuth, Donald Ervin@>
+
+@<Cases of |main_control| that build...@>=
+case mmode+letter: case mmode+other_char:
+ case mmode+char_given:
+ set_math_char(ho(math_code(cur_chr)));@+break;
+case mmode+char_num: {@+scan_char_num();cur_chr=cur_val;
+ set_math_char(ho(math_code(cur_chr)));
+ } @+break;
+case mmode+math_char_num: {@+scan_fifteen_bit_int();set_math_char(cur_val);
+ } @+break;
+case mmode+math_given: set_math_char(cur_chr);@+break;
+case mmode+delim_num: {@+scan_twenty_seven_bit_int();
+ set_math_char(cur_val/010000);
+ } @+break;
+
+@ The |set_math_char| procedure creates a new noad appropriate to a given
+math code, and appends it to the current mlist. However, if the math code
+is sufficiently large, the |cur_chr| is treated as an active character and
+nothing is appended.
+
+@<Declare act...@>=
+static void set_math_char(int @!c)
+{@+pointer p; /*the new noad*/
+if (c >= 0100000)
+ @<Treat |cur_chr|...@>@;
+else{@+p=new_noad();math_type(nucleus(p))=math_char;
+ character(nucleus(p))=qi(c%256);
+ fam(nucleus(p))=(c/256)%16;
+ if (c >= var_code)
+ {@+if (fam_in_range) fam(nucleus(p))=cur_fam;
+ type(p)=ord_noad;
+ }
+ else type(p)=ord_noad+(c/010000);
+ link(tail)=p;tail=p;
+ }
+}
+
+@ Primitive math operators like \.{\\mathop} and \.{\\underline} are given
+the command code |math_comp|, supplemented by the noad type that they
+generate.
+
+@<Put each...@>=
+primitive("mathord", math_comp, ord_noad);
+@!@:math\_ord\_}{\.{\\mathord} primitive@>
+primitive("mathop", math_comp, op_noad);
+@!@:math\_op\_}{\.{\\mathop} primitive@>
+primitive("mathbin", math_comp, bin_noad);
+@!@:math\_bin\_}{\.{\\mathbin} primitive@>
+primitive("mathrel", math_comp, rel_noad);
+@!@:math\_rel\_}{\.{\\mathrel} primitive@>
+primitive("mathopen", math_comp, open_noad);
+@!@:math\_open\_}{\.{\\mathopen} primitive@>
+primitive("mathclose", math_comp, close_noad);
+@!@:math\_close\_}{\.{\\mathclose} primitive@>
+primitive("mathpunct", math_comp, punct_noad);
+@!@:math\_punct\_}{\.{\\mathpunct} primitive@>
+primitive("mathinner", math_comp, inner_noad);
+@!@:math\_inner\_}{\.{\\mathinner} primitive@>
+primitive("underline", math_comp, under_noad);
+@!@:underline\_}{\.{\\underline} primitive@>
+primitive("overline", math_comp, over_noad);@/
+@!@:overline\_}{\.{\\overline} primitive@>
+primitive("displaylimits", limit_switch, normal);
+@!@:display\_limits\_}{\.{\\displaylimits} primitive@>
+primitive("limits", limit_switch, limits);
+@!@:limits\_}{\.{\\limits} primitive@>
+primitive("nolimits", limit_switch, no_limits);
+@!@:no\_limits\_}{\.{\\nolimits} primitive@>
+
+@ @<Cases of |print_cmd_chr|...@>=
+case math_comp: switch (chr_code) {
+ case ord_noad: print_esc("mathord");@+break;
+ case op_noad: print_esc("mathop");@+break;
+ case bin_noad: print_esc("mathbin");@+break;
+ case rel_noad: print_esc("mathrel");@+break;
+ case open_noad: print_esc("mathopen");@+break;
+ case close_noad: print_esc("mathclose");@+break;
+ case punct_noad: print_esc("mathpunct");@+break;
+ case inner_noad: print_esc("mathinner");@+break;
+ case under_noad: print_esc("underline");@+break;
+ default:print_esc("overline");
+ } @+break;
+case limit_switch: if (chr_code==limits) print_esc("limits");
+ else if (chr_code==no_limits) print_esc("nolimits");
+ else print_esc("displaylimits");@+break;
+
+@ @<Cases of |main_control| that build...@>=
+case mmode+math_comp: {@+tail_append(new_noad());
+ type(tail)=cur_chr;scan_math(nucleus(tail));
+ } @+break;
+case mmode+limit_switch: math_limit_switch();@+break;
+
+@ @<Declare act...@>=
+static void math_limit_switch(void)
+{@+
+if (head!=tail) if (type(tail)==op_noad)
+ {@+subtype(tail)=cur_chr;return;
+ }
+print_err("Limit controls must follow a math operator");
+@.Limit controls must follow...@>
+help1("I'm ignoring this misplaced \\limits or \\nolimits command.");error();
+}
+
+@ Delimiter fields of noads are filled in by the |scan_delimiter| routine.
+The first parameter of this procedure is the |mem| address where the
+delimiter is to be placed; the second tells if this delimiter follows
+\.{\\radical} or not.
+
+@<Declare act...@>=
+static void scan_delimiter(pointer @!p, bool @!r)
+{@+if (r) scan_twenty_seven_bit_int();
+else{@+@<Get the next non-blank non-relax...@>;
+ switch (cur_cmd) {
+ case letter: case other_char: cur_val=del_code(cur_chr);@+break;
+ case delim_num: scan_twenty_seven_bit_int();@+break;
+ default:cur_val=-1;
+ }
+ }
+if (cur_val < 0) @<Report that an invalid delimiter code is being changed
+to null; set~|cur_val:=0|@>;
+small_fam(p)=(cur_val/04000000)%16;
+small_char(p)=qi((cur_val/010000)%256);
+large_fam(p)=(cur_val/256)%16;
+large_char(p)=qi(cur_val%256);
+}
+
+@ @<Report that an invalid delimiter...@>=
+{@+print_err("Missing delimiter (. inserted)");
+@.Missing delimiter...@>
+help6("I was expecting to see something like `(' or `\\{' or",@/
+ "`\\}' here. If you typed, e.g., `{' instead of `\\{', you",@/
+ "should probably delete the `{' by typing `1' now, so that",@/
+ "braces don't get unbalanced. Otherwise just proceed.",@/
+ "Acceptable delimiters are characters whose \\delcode is",@/
+ "nonnegative, or you can use `\\delimiter <delimiter code>'.");
+back_error();cur_val=0;
+}
+
+@ @<Cases of |main_control| that build...@>=
+case mmode+radical: math_radical();@+break;
+
+@ @<Declare act...@>=
+static void math_radical(void)
+{@+tail_append(get_node(radical_noad_size));
+type(tail)=radical_noad;subtype(tail)=normal;
+mem[nucleus(tail)].hh=empty_field;
+mem[subscr(tail)].hh=empty_field;
+mem[supscr(tail)].hh=empty_field;
+scan_delimiter(left_delimiter(tail), true);scan_math(nucleus(tail));
+}
+
+@ @<Cases of |main_control| that build...@>=
+case mmode+accent: case mmode+math_accent: math_ac();@+break;
+
+@ @<Declare act...@>=
+static void math_ac(void)
+{@+if (cur_cmd==accent)
+ @<Complain that the user should have said \.{\\mathaccent}@>;
+tail_append(get_node(accent_noad_size));
+type(tail)=accent_noad;subtype(tail)=normal;
+mem[nucleus(tail)].hh=empty_field;
+mem[subscr(tail)].hh=empty_field;
+mem[supscr(tail)].hh=empty_field;
+math_type(accent_chr(tail))=math_char;
+scan_fifteen_bit_int();
+character(accent_chr(tail))=qi(cur_val%256);
+if ((cur_val >= var_code)&&fam_in_range) fam(accent_chr(tail))=cur_fam;
+else fam(accent_chr(tail))=(cur_val/256)%16;
+scan_math(nucleus(tail));
+}
+
+@ @<Complain that the user should have said \.{\\mathaccent}@>=
+{@+print_err("Please use ");print_esc("mathaccent");
+print(" for accents in math mode");
+@.Please use \\mathaccent...@>
+help2("I'm changing \\accent to \\mathaccent here; wish me luck.",@/
+ "(Accents are not the same in formulas as they are in text.)");
+error();
+}
+
+@ @<Cases of |main_control| that build...@>=
+case mmode+vcenter: {@+scan_spec(vcenter_group, false);normal_paragraph();
+ push_nest();mode=-vmode;prev_depth=ignore_depth;
+ if (every_vbox!=null) begin_token_list(every_vbox, every_vbox_text);
+ } @+break;
+
+@ @<Cases of |handle...@>=
+case vcenter_group: {@+end_graf();unsave();save_ptr=save_ptr-2;
+ p=vpack(link(head), saved(1), saved(0));pop_nest();
+ tail_append(new_noad());type(tail)=vcenter_noad;
+ math_type(nucleus(tail))=sub_box;info(nucleus(tail))=p;
+ } @+break;
+
+@ The routine that inserts a |style_node| holds no surprises.
+
+@<Put each...@>=
+primitive("displaystyle", math_style, display_style);
+@!@:display\_style\_}{\.{\\displaystyle} primitive@>
+primitive("textstyle", math_style, text_style);
+@!@:text\_style\_}{\.{\\textstyle} primitive@>
+primitive("scriptstyle", math_style, script_style);
+@!@:script\_style\_}{\.{\\scriptstyle} primitive@>
+primitive("scriptscriptstyle", math_style, script_script_style);
+@!@:script\_script\_style\_}{\.{\\scriptscriptstyle} primitive@>
+
+@ @<Cases of |print_cmd_chr|...@>=
+case math_style: print_style(chr_code);@+break;
+
+@ @<Cases of |main_control| that build...@>=
+case mmode+math_style: tail_append(new_style(cur_chr))@;@+break;
+case mmode+non_script: {@+tail_append(new_glue(zero_glue));
+ subtype(tail)=cond_math_glue;
+ } @+break;
+case mmode+math_choice: append_choices();@+break;
+
+@ The routine that scans the four mlists of a \.{\\mathchoice} is very
+much like the routine that builds discretionary nodes.
+
+@<Declare act...@>=
+static void append_choices(void)
+{@+tail_append(new_choice());incr(save_ptr);saved(-1)=0;
+push_math(math_choice_group);scan_left_brace();
+}
+
+@ @<Cases of |handle_right_brace|...@>=
+case math_choice_group: build_choices();@+break;
+
+@ @<Declare act...@>=
+@t\4@>@<Declare the function called |fin_mlist|@>@t@>@;@/
+static void build_choices(void)
+{@+
+pointer p; /*the current mlist*/
+unsave();p=fin_mlist(null);
+switch (saved(-1)) {
+case 0: display_mlist(tail)=p;@+break;
+case 1: text_mlist(tail)=p;@+break;
+case 2: script_mlist(tail)=p;@+break;
+case 3: {@+script_script_mlist(tail)=p;decr(save_ptr);return;
+ }
+} /*there are no other cases*/
+incr(saved(-1));push_math(math_choice_group);scan_left_brace();
+}
+
+@ Subscripts and superscripts are attached to the previous nucleus by the
+@^superscripts@>@^subscripts@>
+action procedure called |sub_sup|. We use the facts that |sub_mark==sup_mark+1|
+and |subscr(p)==supscr(p)+1|.
+
+@<Cases of |main_control| that build...@>=
+case mmode+sub_mark: case mmode+sup_mark: sub_sup();@+break;
+
+@ @<Declare act...@>=
+static void sub_sup(void)
+{@+small_number t; /*type of previous sub/superscript*/
+pointer @!p; /*field to be filled by |scan_math|*/
+t=empty;p=null;
+if (tail!=head) if (scripts_allowed(tail))
+ {@+p=supscr(tail)+cur_cmd-sup_mark; /*|supscr| or |subscr|*/
+ t=math_type(p);
+ }
+if ((p==null)||(t!=empty)) @<Insert a dummy noad to be sub/superscripted@>;
+scan_math(p);
+}
+
+@ @<Insert a dummy...@>=
+{@+tail_append(new_noad());
+p=supscr(tail)+cur_cmd-sup_mark; /*|supscr| or |subscr|*/
+if (t!=empty)
+ {@+if (cur_cmd==sup_mark)
+ {@+print_err("Double superscript");
+@.Double superscript@>
+ help1("I treat `x^1^2' essentially like `x^1{}^2'.");
+ }
+ else{@+print_err("Double subscript");
+@.Double subscript@>
+ help1("I treat `x_1_2' essentially like `x_1{}_2'.");
+ }
+ error();
+ }
+}
+
+@ An operation like `\.{\\over}' causes the current mlist to go into a
+state of suspended animation: |incompleat_noad| points to a |fraction_noad|
+that contains the mlist-so-far as its numerator, while the denominator
+is yet to come. Finally when the mlist is finished, the denominator will
+go into the incompleat fraction noad, and that noad will become the
+whole formula, unless it is surrounded by `\.{\\left}' and `\.{\\right}'
+delimiters.
+
+@d above_code 0 /* `\.{\\above}' */
+@d over_code 1 /* `\.{\\over}' */
+@d atop_code 2 /* `\.{\\atop}' */
+@d delimited_code 3 /* `\.{\\abovewithdelims}', etc.*/
+
+@<Put each...@>=
+primitive("above", above, above_code);@/
+@!@:above\_}{\.{\\above} primitive@>
+primitive("over", above, over_code);@/
+@!@:over\_}{\.{\\over} primitive@>
+primitive("atop", above, atop_code);@/
+@!@:atop\_}{\.{\\atop} primitive@>
+primitive("abovewithdelims", above, delimited_code+above_code);@/
+@!@:above\_with\_delims\_}{\.{\\abovewithdelims} primitive@>
+primitive("overwithdelims", above, delimited_code+over_code);@/
+@!@:over\_with\_delims\_}{\.{\\overwithdelims} primitive@>
+primitive("atopwithdelims", above, delimited_code+atop_code);
+@!@:atop\_with\_delims\_}{\.{\\atopwithdelims} primitive@>
+
+@ @<Cases of |print_cmd_chr|...@>=
+case above: switch (chr_code) {
+ case over_code: print_esc("over");@+break;
+ case atop_code: print_esc("atop");@+break;
+ case delimited_code+above_code: print_esc("abovewithdelims");@+break;
+ case delimited_code+over_code: print_esc("overwithdelims");@+break;
+ case delimited_code+atop_code: print_esc("atopwithdelims");@+break;
+ default:print_esc("above");
+ } @+break;
+
+@ @<Cases of |main_control| that build...@>=
+case mmode+above: math_fraction();@+break;
+
+@ @<Declare act...@>=
+static void math_fraction(void)
+{@+small_number c; /*the type of generalized fraction we are scanning*/
+c=cur_chr;
+if (incompleat_noad!=null)
+ @<Ignore the fraction operation and complain about this ambiguous case@>@;
+else{@+incompleat_noad=get_node(fraction_noad_size);
+ type(incompleat_noad)=fraction_noad;
+ subtype(incompleat_noad)=normal;
+ math_type(numerator(incompleat_noad))=sub_mlist;
+ info(numerator(incompleat_noad))=link(head);
+ mem[denominator(incompleat_noad)].hh=empty_field;
+ mem[left_delimiter(incompleat_noad)].qqqq=null_delimiter;
+ mem[right_delimiter(incompleat_noad)].qqqq=null_delimiter;@/
+ link(head)=null;tail=head;
+ @<Use code |c| to distinguish between generalized fractions@>;
+ }
+}
+
+@ @<Use code |c|...@>=
+if (c >= delimited_code)
+ {@+scan_delimiter(left_delimiter(incompleat_noad), false);
+ scan_delimiter(right_delimiter(incompleat_noad), false);
+ }
+switch (c%delimited_code) {
+case above_code: {@+scan_normal_dimen;
+ thickness(incompleat_noad)=cur_val;
+ } @+break;
+case over_code: thickness(incompleat_noad)=default_code;@+break;
+case atop_code: thickness(incompleat_noad)=0;
+} /*there are no other cases*/
+
+@ @<Ignore the fraction...@>=
+{@+if (c >= delimited_code)
+ {@+scan_delimiter(garbage, false);scan_delimiter(garbage, false);
+ }
+if (c%delimited_code==above_code) scan_normal_dimen;
+print_err("Ambiguous; you need another { and }");
+@.Ambiguous...@>
+help3("I'm ignoring this fraction specification, since I don't",@/
+ "know whether a construction like `x \\over y \\over z'",@/
+ "means `{x \\over y} \\over z' or `x \\over {y \\over z}'.");
+error();
+}
+
+@ At the end of a math formula or subformula, the |fin_mlist| routine is
+called upon to return a pointer to the newly completed mlist, and to
+pop the nest back to the enclosing semantic level. The parameter to
+|fin_mlist|, if not null, points to a |right_noad| that ends the
+current mlist; this |right_noad| has not yet been appended.
+
+@<Declare the function called |fin_mlist|@>=
+static pointer fin_mlist(pointer @!p)
+{@+pointer q; /*the mlist to return*/
+if (incompleat_noad!=null) @<Compleat the incompleat noad@>@;
+else{@+link(tail)=p;q=link(head);
+ }
+pop_nest();return q;
+}
+
+@ @<Compleat...@>=
+{@+math_type(denominator(incompleat_noad))=sub_mlist;
+info(denominator(incompleat_noad))=link(head);
+if (p==null) q=incompleat_noad;
+else{@+q=info(numerator(incompleat_noad));
+ if ((type(q)!=left_noad)||(delim_ptr==null)) confusion("right");
+@:this can't happen right}{\quad right@>
+ info(numerator(incompleat_noad))=link(delim_ptr);
+ link(delim_ptr)=incompleat_noad;link(incompleat_noad)=p;
+ }
+}
+
+@ Now at last we're ready to see what happens when a right brace occurs
+in a math formula. Two special cases are simplified here: Braces are effectively
+removed when they surround a single Ord without sub/superscripts, or when they
+surround an accent that is the nucleus of an Ord atom.
+
+@<Cases of |handle...@>=
+case math_group: {@+unsave();decr(save_ptr);@/
+ math_type(saved(0))=sub_mlist;p=fin_mlist(null);info(saved(0))=p;
+ if (p!=null) if (link(p)==null)
+ if (type(p)==ord_noad)
+ {@+if (math_type(subscr(p))==empty)
+ if (math_type(supscr(p))==empty)
+ {@+mem[saved(0)].hh=mem[nucleus(p)].hh;
+ free_node(p, noad_size);
+ }
+ }
+ else if (type(p)==accent_noad) if (saved(0)==nucleus(tail))
+ if (type(tail)==ord_noad) @<Replace the tail of the list by |p|@>;
+ } @+break;
+
+@ @<Replace the tail...@>=
+{@+q=head;while (link(q)!=tail) q=link(q);
+link(q)=p;free_node(tail, noad_size);tail=p;
+}
+
+@ We have dealt with all constructions of math mode except `\.{\\left}' and
+`\.{\\right}', so the picture is completed by the following sections of
+the program.
+
+@<Put each...@>=
+primitive("left", left_right, left_noad);
+@!@:left\_}{\.{\\left} primitive@>
+primitive("right", left_right, right_noad);
+@!@:right\_}{\.{\\right} primitive@>
+text(frozen_right)=text(cur_val);eqtb[frozen_right]=eqtb[cur_val];
+
+@ @<Cases of |print_cmd_chr|...@>=
+case left_right: if (chr_code==left_noad) print_esc("left")
+@/@<Cases of |left_right| for |print_cmd_chr|@>;@/
+else print_esc("right");@+break;
+
+@ @<Cases of |main_control| that build...@>=
+case mmode+left_right: math_left_right();@+break;
+
+@ @<Declare act...@>=
+static void math_left_right(void)
+{@+small_number t; /*|left_noad| or |right_noad|*/
+pointer @!p; /*new noad*/
+pointer @!q; /*resulting mlist*/
+t=cur_chr;
+if ((t!=left_noad)&&(cur_group!=math_left_group))
+ @<Try to recover from mismatched \.{\\right}@>@;
+else{@+p=new_noad();type(p)=t;
+ scan_delimiter(delimiter(p), false);
+ if (t==middle_noad)
+ {@+type(p)=right_noad;subtype(p)=middle_noad;
+ }
+ if (t==left_noad) q=p;
+ else{@+q=fin_mlist(p);unsave(); /*end of |math_left_group|*/
+ }
+ if (t!=right_noad)
+ {@+push_math(math_left_group);link(head)=q;tail=p;
+ delim_ptr=p;
+ }
+ else{@+
+ tail_append(new_noad());type(tail)=inner_noad;
+ math_type(nucleus(tail))=sub_mlist;
+ info(nucleus(tail))=q;
+ }
+ }
+}
+
+@ @<Try to recover from mismatch...@>=
+{@+if (cur_group==math_shift_group)
+ {@+scan_delimiter(garbage, false);
+ print_err("Extra ");
+ if (t==middle_noad)
+ {@+print_esc("middle");
+@.Extra \\middle.@>
+ help1("I'm ignoring a \\middle that had no matching \\left.");
+ }
+ else{@+print_esc("right");
+@.Extra \\right.@>
+ help1("I'm ignoring a \\right that had no matching \\left.");
+ }
+ error();
+ }
+else off_save();
+}
+
+@ Here is the only way out of math mode.
+
+@<Cases of |main_control| that build...@>=
+case mmode+math_shift: if (cur_group==math_shift_group) after_math();
+ else off_save();@+break;
+
+@ @<Declare act...@>=
+static void after_math(void)
+{@+bool l; /*`\.{\\leqno}' instead of `\.{\\eqno}'*/
+bool @!danger; /*not enough symbol fonts are present*/
+int @!m; /*|mmode| or |-mmode|*/
+pointer @!p; /*the formula*/
+pointer @!a; /*box containing equation number*/
+@<Local variables for finishing a displayed formula@>@;
+danger=false;
+@<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*/
+if (mode==-m) /*end of equation number*/
+ {@+@<Check that another \.\$ follows@>;
+ cur_mlist=p;cur_style=text_style;mlist_penalties=false;
+ mlist_to_hlist();a=hpack(link(temp_head), natural);
+ unsave();decr(save_ptr); /*now |cur_group==math_shift_group|*/
+ if (saved(0)==1) l=true;
+ danger=false;
+ @<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);
+ }
+else a=null;
+if (m < 0) @<Finish math in text@>@;
+else{@+if (a==null) @<Check that another \.\$ follows@>;
+ @<Finish displayed math@>;
+ }
+}
+
+@ @<Check that the necessary fonts...@>=
+if ((font_params[fam_fnt(2+text_size)] < total_mathsy_params)||@|
+ (font_params[fam_fnt(2+script_size)] < total_mathsy_params)||@|
+ (font_params[fam_fnt(2+script_script_size)] < total_mathsy_params))
+ {@+print_err("Math formula deleted: Insufficient symbol fonts");@/
+@.Math formula deleted...@>
+ help3("Sorry, but I can't typeset math unless \\textfont 2",@/
+ "and \\scriptfont 2 and \\scriptscriptfont 2 have all",@/
+ "the \\fontdimen values needed in math symbol fonts.");
+ error();flush_math();danger=true;
+ }
+else if ((font_params[fam_fnt(3+text_size)] < total_mathex_params)||@|
+ (font_params[fam_fnt(3+script_size)] < total_mathex_params)||@|
+ (font_params[fam_fnt(3+script_script_size)] < total_mathex_params))
+ {@+print_err("Math formula deleted: Insufficient extension fonts");@/
+ help3("Sorry, but I can't typeset math unless \\textfont 3",@/
+ "and \\scriptfont 3 and \\scriptscriptfont 3 have all",@/
+ "the \\fontdimen values needed in math extension fonts.");
+ error();flush_math();danger=true;
+ }
+
+@ The |unsave| is done after everything else here; hence an appearance of
+`\.{\\mathsurround}' inside of `\.{\$...\$}' affects the spacing at these
+particular \.\$'s. This is consistent with the conventions of
+`\.{\$\$...\$\$}', since `\.{\\abovedisplayskip}' inside a display affects the
+space above that display.
+
+@<Finish math in text@>=
+{@+tail_append(new_math(math_surround, before));
+cur_mlist=p;cur_style=text_style;mlist_penalties=(mode > 0);mlist_to_hlist();
+link(tail)=link(temp_head);
+while (link(tail)!=null) tail=link(tail);
+tail_append(new_math(math_surround, after));
+space_factor=1000;unsave();
+}
+
+@ \TeX\ gets to the following part of the program when the first `\.\$' ending
+a display has been scanned.
+
+@<Check that another \.\$ follows@>=
+{@+get_x_token();
+if (cur_cmd!=math_shift)
+ {@+print_err("Display math should end with $$");
+@.Display math...with \$\$@>
+ help2("The `$' that I just saw supposedly matches a previous `$$'.",@/
+ "So I shall assume that you typed `$$' both times.");
+ back_error();
+ }
+}
+
+@ We have saved the worst for last: The fussiest part of math mode processing
+occurs when a displayed formula is being centered and placed with an optional
+equation number.
+
+@<Local variables for finishing...@>=
+pointer @!b; /*box containing the equation*/
+scaled @!w; /*width of the equation*/
+scaled @!z; /*width of the line*/
+scaled @!e; /*width of equation number*/
+scaled @!q; /*width of equation number plus space to separate from equation*/
+scaled @!d; /*displacement of equation in the line*/
+scaled @!s; /*move the line right this much*/
+small_number @!g1, @!g2; /*glue parameter codes for before and after*/
+pointer @!r; /*kern node used to position the display*/
+pointer @!t; /*tail of adjustment list*/
+
+@ At this time |p| points to the mlist for the formula; |a| is either
+|null| or it points to a box containing the equation number; and we are in
+vertical mode (or internal vertical mode).
+
+@<Finish displayed math@>=
+cur_mlist=p;cur_style=display_style;mlist_penalties=false;
+mlist_to_hlist();p=link(temp_head);@/
+adjust_tail=adjust_head;b=hpack(p, natural);p=list_ptr(b);
+t=adjust_tail;adjust_tail=null;@/
+w=width(b);z=display_width;s=display_indent;
+if ((a==null)||danger)
+ {@+e=0;q=0;
+ }
+else{@+e=width(a);q=e+math_quad(text_size);
+ }
+if (w+q > z)
+ @<Squeeze the equation as much as possible; if there is an equation number
+that should go on a separate line by itself, set~|e:=0|@>;
+@<Determine the displacement, |d|, of the left edge of the equation, with
+respect to the line size |z|, assuming that |l=false|@>;
+@<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@>;
+resume_after_display()
+
+@ @<Declare act...@>=
+static void resume_after_display(void)
+{@+if (cur_group!=math_shift_group) confusion("display");
+@:this can't happen display}{\quad display@>
+unsave();prev_graf=prev_graf+3;
+push_nest();mode=hmode;space_factor=1000;set_cur_lang;clang=cur_lang;
+prev_graf=(norm_min(left_hyphen_min)*0100+norm_min(right_hyphen_min))
+ *0200000+cur_lang;
+@<Scan an optional space@>;
+if (nest_ptr==1) build_page();
+}
+
+@ The user can force the equation number to go on a separate line
+by causing its width to be zero.
+
+@<Squeeze the equation as much as possible...@>=
+{@+if ((e!=0)&&((w-total_shrink[normal]+q <= z)||@|
+ (total_shrink[fil]!=0)||(total_shrink[fill]!=0)||
+ (total_shrink[filll]!=0)))
+ {@+free_node(b, box_node_size);
+ b=hpack(p, z-q, exactly);
+ }
+else{@+e=0;
+ if (w > z)
+ {@+free_node(b, box_node_size);
+ b=hpack(p, z, exactly);
+ }
+ }
+w=width(b);
+}
+
+@ We try first to center the display without regard to the existence of
+the equation number. If that would make it too close (where ``too close''
+means that the space between display and equation number is less than the
+width of the equation number), we either center it in the remaining space
+or move it as far from the equation number as possible. The latter alternative
+is taken only if the display begins with glue, since we assume that the
+user put glue there to control the spacing precisely.
+
+@<Determine the displacement, |d|, of the left edge of the equation...@>=
+d=half(z-w);
+if ((e > 0)&&(d < 2*e)) /*too close*/
+ {@+d=half(z-w-e);
+ if (p!=null) if (!is_char_node(p)) if (type(p)==glue_node) d=0;
+ }
+
+@ If the equation number is set on a line by itself, either before or
+after the formula, we append an infinite penalty so that no page break will
+separate the display from its number; and we use the same size and
+displacement for all three potential lines of the display, even though
+`\.{\\parshape}' may specify them differently.
+
+@<Append the glue or equation number preceding the display@>=
+tail_append(new_penalty(pre_display_penalty));@/
+if ((d+s <= pre_display_size)||l) /*not enough clearance*/
+ {@+g1=above_display_skip_code;g2=below_display_skip_code;
+ }
+else{@+g1=above_display_short_skip_code;
+ g2=below_display_short_skip_code;
+ }
+if (l&&(e==0)) /*it follows that |type(a)==hlist_node|*/
+ {@+shift_amount(a)=s;append_to_vlist(a);
+ tail_append(new_penalty(inf_penalty));
+ }
+else tail_append(new_param_glue(g1))
+
+@ @<Append the display and perhaps also the equation number@>=
+if (e!=0)
+ {@+r=new_kern(z-w-e-d);
+ if (l)
+ {@+link(a)=r;link(r)=b;b=a;d=0;
+ }
+ else{@+link(b)=r;link(r)=a;
+ }
+ b=hpack(b, natural);
+ }
+shift_amount(b)=s+d;append_to_vlist(b)
+
+@ @<Append the glue or equation number following the display@>=
+if ((a!=null)&&(e==0)&&!l)
+ {@+tail_append(new_penalty(inf_penalty));
+ shift_amount(a)=s+z-width(a);
+ append_to_vlist(a);
+ g2=0;
+ }
+if (t!=adjust_head) /*migrating material comes after equation number*/
+ {@+link(tail)=link(adjust_head);tail=t;
+ }
+tail_append(new_penalty(post_display_penalty));
+if (g2 > 0) tail_append(new_param_glue(g2))
+
+@ When \.{\\halign} appears in a display, the alignment routines operate
+essentially as they do in vertical mode. Then the following program is
+activated, with |p| and |q| pointing to the beginning and end of the
+resulting list, and with |aux_save| holding the |prev_depth| value.
+
+@<Finish an alignment in a display@>=
+{@+do_assignments();
+if (cur_cmd!=math_shift) @<Pontificate about improper alignment in display@>@;
+else@<Check that another \.\$ follows@>;
+pop_nest();
+tail_append(new_penalty(pre_display_penalty));
+tail_append(new_param_glue(above_display_skip_code));
+link(tail)=p;
+if (p!=null) tail=q;
+tail_append(new_penalty(post_display_penalty));
+tail_append(new_param_glue(below_display_skip_code));
+prev_depth=aux_save.sc;resume_after_display();
+}
+
+@ @<Pontificate...@>=
+{@+print_err("Missing $$ inserted");
+@.Missing {\$\$} inserted@>
+help2("Displays can use special alignments (like \\eqalignno)",@/
+ "only if nothing but the alignment itself is between $$'s.");
+back_error();
+}
+
+@* Mode-independent processing.
+The long |main_control| procedure has now been fully specified, except for
+certain activities that are independent of the current mode. These activities
+do not change the current vlist or hlist or mlist; if they change anything,
+it is the value of a parameter or the meaning of a control sequence.
+
+Assignments to values in |eqtb| can be global or local. Furthermore, a
+control sequence can be defined to be `\.{\\long}', `\.{\\protected}',
+or `\.{\\outer}', and it might or might not be expanded. The prefixes
+`\.{\\global}', `\.{\\long}', `\.{\\protected}',
+and `\.{\\outer}' can occur in any order. Therefore we assign binary numeric
+codes, making it possible to accumulate the union of all specified prefixes
+by adding the corresponding codes. (\PASCAL's |set| operations could also
+have been used.)
+
+@<Put each...@>=
+primitive("long", prefix, 1);
+@!@:long\_}{\.{\\long} primitive@>
+primitive("outer", prefix, 2);
+@!@:outer\_}{\.{\\outer} primitive@>
+primitive("global", prefix, 4);
+@!@:global\_}{\.{\\global} primitive@>
+primitive("def", def, 0);
+@!@:def\_}{\.{\\def} primitive@>
+primitive("gdef", def, 1);
+@!@:gdef\_}{\.{\\gdef} primitive@>
+primitive("edef", def, 2);
+@!@:edef\_}{\.{\\edef} primitive@>
+primitive("xdef", def, 3);
+@!@:xdef\_}{\.{\\xdef} primitive@>
+
+@ @<Cases of |print_cmd_chr|...@>=
+case prefix: if (chr_code==1) print_esc("long");
+ else if (chr_code==2) print_esc("outer")
+ @/@<Cases of |prefix| for |print_cmd_chr|@>;@/
+ else print_esc("global");@+break;
+case def: if (chr_code==0) print_esc("def");
+ else if (chr_code==1) print_esc("gdef");
+ else if (chr_code==2) print_esc("edef");
+ else print_esc("xdef");@+break;
+
+@ Every prefix, and every command code that might or might not be prefixed,
+calls the action procedure |prefixed_command|. This routine accumulates
+a sequence of prefixes until coming to a non-prefix, then it carries out
+the command.
+
+@<Cases of |main_control| that don't...@>=
+any_mode(toks_register):
+any_mode(assign_toks):
+any_mode(assign_int):
+any_mode(assign_dimen):
+any_mode(assign_glue):
+any_mode(assign_mu_glue):
+any_mode(assign_font_dimen):
+any_mode(assign_font_int):
+any_mode(set_aux):
+any_mode(set_prev_graf):
+any_mode(set_page_dimen):
+any_mode(set_page_int):
+any_mode(set_box_dimen):
+any_mode(set_shape):
+any_mode(def_code):
+any_mode(def_family):
+any_mode(set_font):
+any_mode(def_font):
+any_mode(internal_register):
+any_mode(advance):
+any_mode(multiply):
+any_mode(divide):
+any_mode(prefix):
+any_mode(let):
+any_mode(shorthand_def):
+any_mode(read_to_cs):
+any_mode(def):
+any_mode(set_box):
+any_mode(hyph_data):
+any_mode(set_interaction): prefixed_command();@+break;
+
+@ If the user says, e.g., `\.{\\global\\global}', the redundancy is
+silently accepted.
+
+@<Declare act...@>=
+@t\4@>@<Declare subprocedures for |prefixed_command|@>@t@>@;@/
+static void prefixed_command(void)
+{@+
+small_number a; /*accumulated prefix codes so far*/
+internal_font_number @!f; /*identifies a font*/
+int @!j; /*index into a \.{\\parshape} specification*/
+font_index @!k; /*index into |font_info|*/
+pointer @!p, @!q; /*for temporary short-term use*/
+int @!n; /*ditto*/
+bool @!e; /*should a definition be expanded? or was \.{\\let} not done?*/
+a=0;
+while (cur_cmd==prefix)
+ {@+if (!odd(a/cur_chr)) a=a+cur_chr;
+ @<Get the next non-blank non-relax...@>;
+ if (cur_cmd <= max_non_prefixed_command)
+ @<Discard erroneous prefixes and |return|@>;
+ if (tracing_commands > 2) if (eTeX_ex) show_cur_cmd_chr();
+ }
+@<Discard the prefixes \.{\\long} and \.{\\outer} if they are irrelevant@>;
+@<Adjust \(f)for the setting of \.{\\globaldefs}@>;
+switch (cur_cmd) {
+@t\4@>@<Assignments@>@;
+default:confusion("prefix");
+@:this can't happen prefix}{\quad prefix@>
+}
+done: @<Insert a token saved by \.{\\afterassignment}, if any@>;
+}
+
+@ @<Discard erroneous...@>=
+{@+print_err("You can't use a prefix with `");
+@.You can't use a prefix with x@>
+print_cmd_chr(cur_cmd, cur_chr);print_char('\'');
+help1("I'll pretend you didn't say \\long or \\outer or \\global.");
+if (eTeX_ex) help_line[0]=@|
+ "I'll pretend you didn't say \\long or \\outer or \\global or \\protected.";
+back_error();return;
+}
+
+@ @<Discard the prefixes...@>=
+if (a >= 8)
+ {@+j=protected_token;a=a-8;
+ }
+else j=0;
+if ((cur_cmd!=def)&&((a%4!=0)||(j!=0)))
+ {@+print_err("You can't use `");print_esc("long");print("' or `");
+ print_esc("outer");
+ help1("I'll pretend you didn't say \\long or \\outer here.");
+ if (eTeX_ex)
+ {@+help_line[0]=@|
+ "I'll pretend you didn't say \\long or \\outer or \\protected here.";
+ print("' or `");print_esc("protected");
+ }
+ print("' with `");
+@.You can't use \\long...@>
+ print_cmd_chr(cur_cmd, cur_chr);print_char('\'');
+ error();
+ }
+
+@ The previous routine does not have to adjust |a| so that |a%4==0|,
+since the following routines test for the \.{\\global} prefix as follows.
+
+@d global (a >= 4)
+@d define(A, B, C) if (global) geq_define(A, B, C);@+else eq_define(A, B, C)
+@d word_define(A, B) if (global) geq_word_define(A, B);@+else eq_word_define(A, B)
+
+@<Adjust \(f)for the setting of \.{\\globaldefs}@>=
+if (global_defs!=0)
+ if (global_defs < 0)
+ {@+if (global) a=a-4;
+ }
+ else{@+if (!global) a=a+4;
+ }
+
+@ When a control sequence is to be defined, by \.{\\def} or \.{\\let} or
+something similar, the |get_r_token| routine will substitute a special
+control sequence for a token that is not redefinable.
+
+@<Declare subprocedures for |prefixed_command|@>=
+static void get_r_token(void)
+{@+
+restart: @/do@+{get_token();
+}@+ while (!(cur_tok!=space_token));
+if ((cur_cs==0)||(cur_cs > frozen_control_sequence))
+ {@+print_err("Missing control sequence inserted");
+@.Missing control...@>
+ help5("Please don't say `\\def cs{...}', say `\\def\\cs{...}'.",@/
+ "I've inserted an inaccessible control sequence so that your",@/
+ "definition will be completed without mixing me up too badly.",@/
+ "You can recover graciously from this error, if you're",@/
+ "careful; see exercise 27.2 in The TeXbook.");
+@:TeXbook}{\sl The \TeX book@>
+ if (cur_cs==0) back_input();
+ cur_tok=cs_token_flag+frozen_protection;ins_error();goto restart;
+ }
+}
+
+@ @<Initialize table entries...@>=
+text(frozen_protection)=s_no("inaccessible");
+@.inaccessible@>
+
+@ Here's an example of the way many of the following routines operate.
+(Unfortunately, they aren't all as simple as this.)
+
+@<Assignments@>=
+case set_font: define(cur_font_loc, data, cur_chr);@+break;
+
+@ When a |def| command has been scanned,
+|cur_chr| is odd if the definition is supposed to be global, and
+|cur_chr >= 2| if the definition is supposed to be expanded.
+
+@<Assignments@>=
+case def: {@+uint32_t def_fl;
+ if (odd(cur_chr)&&!global&&(global_defs >= 0)) a=a+4;
+ e=(cur_chr >= 2);get_r_token();p=cur_cs; def_fl=cur_file_line;
+ q=scan_toks(true, e);
+ if (j!=0)
+ {@+q=get_avail();info(q)=j;link(q)=link(def_ref);
+ link(def_ref)=q;
+ }
+ define(p, call+(a%4), def_ref);
+ fl_mem[def_ref]=def_fl;
+ } @+break;
+
+@ Both \.{\\let} and \.{\\futurelet} share the command code |let|.
+
+@<Put each...@>=
+primitive("let", let, normal);@/
+@!@:let\_}{\.{\\let} primitive@>
+primitive("futurelet", let, normal+1);@/
+@!@:future\_let\_}{\.{\\futurelet} primitive@>
+
+@ @<Cases of |print_cmd_chr|...@>=
+case let: if (chr_code!=normal) print_esc("futurelet");@+else print_esc("let");@+break;
+
+@ @<Assignments@>=
+case let: {@+n=cur_chr;
+ get_r_token();p=cur_cs;
+ if (n==normal)
+ {@+@/do@+{get_token();
+ }@+ while (!(cur_cmd!=spacer));
+ if (cur_tok==other_token+'=')
+ {@+get_token();
+ if (cur_cmd==spacer) get_token();
+ }
+ }
+ else{@+get_token();q=cur_tok;get_token();back_input();
+ cur_tok=q;back_input(); /*look ahead, then back up*/
+ } /*note that |back_input| doesn't affect |cur_cmd|, |cur_chr|*/
+ if (cur_cmd >= call) add_token_ref(cur_chr);
+ else if ((cur_cmd==internal_register)||(cur_cmd==toks_register))
+ if ((cur_chr < mem_bot)||(cur_chr > lo_mem_stat_max))
+ add_sa_ref(cur_chr);
+ define(p, cur_cmd, cur_chr);
+ } @+break;
+
+@ A \.{\\chardef} creates a control sequence whose |cmd| is |char_given|;
+a \.{\\mathchardef} creates a control sequence whose |cmd| is |math_given|;
+and the corresponding |chr| is the character code or math code. A \.{\\countdef}
+or \.{\\dimendef} or \.{\\skipdef} or \.{\\muskipdef} creates a control
+sequence whose |cmd| is |assign_int| or \dots\ or |assign_mu_glue|, and the
+corresponding |chr| is the |eqtb| location of the internal register in question.
+
+@d char_def_code 0 /*|shorthand_def| for \.{\\chardef}*/
+@d math_char_def_code 1 /*|shorthand_def| for \.{\\mathchardef}*/
+@d count_def_code 2 /*|shorthand_def| for \.{\\countdef}*/
+@d dimen_def_code 3 /*|shorthand_def| for \.{\\dimendef}*/
+@d skip_def_code 4 /*|shorthand_def| for \.{\\skipdef}*/
+@d mu_skip_def_code 5 /*|shorthand_def| for \.{\\muskipdef}*/
+@d toks_def_code 6 /*|shorthand_def| for \.{\\toksdef}*/
+
+@<Put each...@>=
+primitive("chardef", shorthand_def, char_def_code);@/
+@!@:char\_def\_}{\.{\\chardef} primitive@>
+primitive("mathchardef", shorthand_def, math_char_def_code);@/
+@!@:math\_char\_def\_}{\.{\\mathchardef} primitive@>
+primitive("countdef", shorthand_def, count_def_code);@/
+@!@:count\_def\_}{\.{\\countdef} primitive@>
+primitive("dimendef", shorthand_def, dimen_def_code);@/
+@!@:dimen\_def\_}{\.{\\dimendef} primitive@>
+primitive("skipdef", shorthand_def, skip_def_code);@/
+@!@:skip\_def\_}{\.{\\skipdef} primitive@>
+primitive("muskipdef", shorthand_def, mu_skip_def_code);@/
+@!@:mu\_skip\_def\_}{\.{\\muskipdef} primitive@>
+primitive("toksdef", shorthand_def, toks_def_code);@/
+@!@:toks\_def\_}{\.{\\toksdef} primitive@>
+
+@ @<Cases of |print_cmd_chr|...@>=
+case shorthand_def: switch (chr_code) {
+ case char_def_code: print_esc("chardef");@+break;
+ case math_char_def_code: print_esc("mathchardef");@+break;
+ case count_def_code: print_esc("countdef");@+break;
+ case dimen_def_code: print_esc("dimendef");@+break;
+ case skip_def_code: print_esc("skipdef");@+break;
+ case mu_skip_def_code: print_esc("muskipdef");@+break;
+ default:print_esc("toksdef");
+ } @+break;
+case char_given: {@+print_esc("char");print_hex(chr_code);
+ } @+break;
+case math_given: {@+print_esc("mathchar");print_hex(chr_code);
+ } @+break;
+
+@ We temporarily define |p| to be |relax|, so that an occurrence of |p|
+while scanning the definition will simply stop the scanning instead of
+producing an ``undefined control sequence'' error or expanding the
+previous meaning. This allows, for instance, `\.{\\chardef\\foo=123\\foo}'.
+
+@<Assignments@>=
+case shorthand_def: {@+n=cur_chr;get_r_token();p=cur_cs;define(p, relax, 256);
+ scan_optional_equals();
+ switch (n) {
+ case char_def_code: {@+scan_char_num();define(p, char_given, cur_val);
+ } @+break;
+ case math_char_def_code: {@+scan_fifteen_bit_int();define(p, math_given, cur_val);
+ } @+break;
+ default:{@+scan_register_num();
+ if (cur_val > 255)
+ {@+j=n-count_def_code; /*|int_val dotdot box_val|*/
+ if (j > mu_val) j=tok_val; /*|int_val dotdot mu_val| or |tok_val|*/
+ find_sa_element(j, cur_val, true);add_sa_ref(cur_ptr);
+ if (j==tok_val) j=toks_register;@+else j=internal_register;
+ define(p, j, cur_ptr);
+ }
+ else
+ switch (n) {
+ case count_def_code: define(p, assign_int, count_base+cur_val);@+break;
+ case dimen_def_code: define(p, assign_dimen, scaled_base+cur_val);@+break;
+ case skip_def_code: define(p, assign_glue, skip_base+cur_val);@+break;
+ case mu_skip_def_code: define(p, assign_mu_glue, mu_skip_base+cur_val);@+break;
+ case toks_def_code: define(p, assign_toks, toks_base+cur_val);
+ } /*there are no other cases*/
+ }
+ }
+ } @+break;
+
+@ @<Assignments@>=
+case read_to_cs: {@+j=cur_chr;scan_int();n=cur_val;
+ if (!scan_keyword("to"))
+@.to@>
+ {@+print_err("Missing `to' inserted");
+@.Missing `to'...@>
+ help2("You should have said `\\read<number> to \\cs'.",@/
+ "I'm going to look for the \\cs now.");error();
+ }
+ get_r_token();
+ p=cur_cs;read_toks(n, p, j);define(p, call, cur_val);
+ } @+break;
+
+@ The token-list parameters, \.{\\output} and \.{\\everypar}, etc., receive
+their values in the following way. (For safety's sake, we place an
+enclosing pair of braces around an \.{\\output} list.)
+
+@<Assignments@>=
+case toks_register: case assign_toks: {@+uint32_t def_fl=cur_file_line; q=cur_cs;
+ e=false; /*just in case, will be set |true| for sparse array elements*/
+ if (cur_cmd==toks_register)
+ if (cur_chr==mem_bot)
+ {@+scan_register_num();
+ if (cur_val > 255)
+ {@+find_sa_element(tok_val, cur_val, true);
+ cur_chr=cur_ptr;e=true;
+ }
+ else cur_chr=toks_base+cur_val;
+ }
+ else e=true;
+ p=cur_chr; /*|p==every_par_loc| or |output_routine_loc| or \dots*/
+ scan_optional_equals();
+ @<Get the next non-blank non-relax non-call token@>;
+ if (cur_cmd!=left_brace) @<If the right-hand side is a token parameter or
+token register, finish the assignment and |goto done|@>;
+ back_input();cur_cs=q;q=scan_toks(false, false);
+ if (link(def_ref)==null) /*empty list: revert to the default*/
+ {@+sa_define(p, null, p, undefined_cs, null);free_avail(def_ref);
+ }
+ else{@+if ((p==output_routine_loc)&&!e) /*enclose in curlies*/
+ {@+link(q)=get_avail();q=link(q);
+ info(q)=right_brace_token+'}';
+ fl_mem[q]=FILE_LINE(system_file, system_insert);
+ q=get_avail();info(q)=left_brace_token+'{';
+ fl_mem[q]=FILE_LINE(system_file, system_insert);
+ link(q)=link(def_ref);link(def_ref)=q;
+ }
+ sa_define(p, def_ref, p, call, def_ref);
+ fl_mem[def_ref]=def_fl;
+ }
+ } @+break;
+
+@ @<If the right-hand side is a token parameter...@>=
+if ((cur_cmd==toks_register)||(cur_cmd==assign_toks))
+ {@+if (cur_cmd==toks_register)
+ if (cur_chr==mem_bot)
+ {@+scan_register_num();
+ if (cur_val < 256) q=equiv(toks_base+cur_val);
+ else{@+find_sa_element(tok_val, cur_val, false);
+ if (cur_ptr==null) q=null;
+ else q=sa_ptr(cur_ptr);
+ }
+ }
+ else q=sa_ptr(cur_chr);
+ else q=equiv(cur_chr);
+ if (q==null) sa_define(p, null, p, undefined_cs, null);
+ else{@+add_token_ref(q);sa_define(p, q, p, call, q);
+ }
+ goto done;
+ }
+
+@ Similar routines are used to assign values to the numeric parameters.
+
+@<Assignments@>=
+case assign_int: {@+p=cur_chr;scan_optional_equals();scan_int();
+ word_define(p, cur_val);
+ } @+break;
+case assign_dimen: {@+p=cur_chr;scan_optional_equals();
+ scan_normal_dimen;word_define(p, cur_val);
+ } @+break;
+case assign_glue: case assign_mu_glue: {@+p=cur_chr;n=cur_cmd;scan_optional_equals();
+ if (n==assign_mu_glue) scan_glue(mu_val);@+else scan_glue(glue_val);
+ trap_zero_glue();
+ define(p, glue_ref, cur_val);
+ } @+break;
+
+@ When a glue register or parameter becomes zero, it will always point to
+|zero_glue| because of the following procedure. (Exception: The tabskip
+glue isn't trapped while preambles are being scanned.)
+
+@<Declare subprocedures for |prefixed_command|@>=
+static void trap_zero_glue(void)
+{@+if ((width(cur_val)==0)&&(stretch(cur_val)==0)&&(shrink(cur_val)==0))
+ {@+add_glue_ref(zero_glue);
+ delete_glue_ref(cur_val);cur_val=zero_glue;
+ }
+}
+
+@ The various character code tables are changed by the |def_code| commands,
+and the font families are declared by |def_family|.
+
+@<Put each...@>=
+primitive("catcode", def_code, cat_code_base);
+@!@:cat\_code\_}{\.{\\catcode} primitive@>
+primitive("mathcode", def_code, math_code_base);
+@!@:math\_code\_}{\.{\\mathcode} primitive@>
+primitive("lccode", def_code, lc_code_base);
+@!@:lc\_code\_}{\.{\\lccode} primitive@>
+primitive("uccode", def_code, uc_code_base);
+@!@:uc\_code\_}{\.{\\uccode} primitive@>
+primitive("sfcode", def_code, sf_code_base);
+@!@:sf\_code\_}{\.{\\sfcode} primitive@>
+primitive("delcode", def_code, del_code_base);
+@!@:del\_code\_}{\.{\\delcode} primitive@>
+primitive("textfont", def_family, math_font_base);
+@!@:text\_font\_}{\.{\\textfont} primitive@>
+primitive("scriptfont", def_family, math_font_base+script_size);
+@!@:script\_font\_}{\.{\\scriptfont} primitive@>
+primitive("scriptscriptfont", def_family, math_font_base+script_script_size);
+@!@:script\_script\_font\_}{\.{\\scriptscriptfont} primitive@>
+
+@ @<Cases of |print_cmd_chr|...@>=
+case def_code: if (chr_code==cat_code_base) print_esc("catcode");
+ else if (chr_code==math_code_base) print_esc("mathcode");
+ else if (chr_code==lc_code_base) print_esc("lccode");
+ else if (chr_code==uc_code_base) print_esc("uccode");
+ else if (chr_code==sf_code_base) print_esc("sfcode");
+ else print_esc("delcode");@+break;
+case def_family: print_size(chr_code-math_font_base);@+break;
+
+@ The different types of code values have different legal ranges; the
+following program is careful to check each case properly.
+
+@<Assignments@>=
+case def_code: {@+@<Let |n| be the largest legal code value, based on |cur_chr|@>;
+ p=cur_chr;scan_char_num();p=p+cur_val;scan_optional_equals();
+ scan_int();
+ if (((cur_val < 0)&&(p < del_code_base))||(cur_val > n))
+ {@+print_err("Invalid code (");print_int(cur_val);
+@.Invalid code@>
+ if (p < del_code_base) print("), should be in the range 0..");
+ else print("), should be at most ");
+ print_int(n);
+ help1("I'm going to use 0 instead of that illegal code value.");@/
+ error();cur_val=0;
+ }
+ if (p < math_code_base) define(p, data, cur_val);
+ else if (p < del_code_base) define(p, data, hi(cur_val));
+ else word_define(p, cur_val);
+ } @+break;
+
+@ @<Let |n| be the largest...@>=
+if (cur_chr==cat_code_base) n=max_char_code;
+else if (cur_chr==math_code_base) n=0100000;
+else if (cur_chr==sf_code_base) n=077777;
+else if (cur_chr==del_code_base) n=077777777;
+else n=255
+
+@ @<Assignments@>=
+case def_family: {@+p=cur_chr;scan_four_bit_int();p=p+cur_val;
+ scan_optional_equals();scan_font_ident();define(p, data, cur_val);
+ } @+break;
+
+@ Next we consider changes to \TeX's numeric registers.
+
+@<Assignments@>=
+case internal_register: case advance:
+ case multiply: case divide: do_register_command(a);@+break;
+
+@ We use the fact that |internal_register < advance < multiply < divide|.
+
+@<Declare subprocedures for |prefixed_command|@>=
+static void do_register_command(small_number @!a)
+{@+
+pointer l, @!q, @!r, @!s; /*for list manipulation*/
+int @!p; /*type of register involved*/
+bool @!e; /*does |l| refer to a sparse array element?*/
+int @!w; /*integer or dimen value of |l|*/
+q=cur_cmd;
+e=false; /*just in case, will be set |true| for sparse array elements*/
+@<Compute the register location |l| and its type |p|; but |return| if invalid@>;
+if (q==internal_register) scan_optional_equals();
+else if (scan_keyword("by")) do_nothing; /*optional `\.{by}'*/
+@.by@>
+arith_error=false;
+if (q < multiply) @<Compute result of |register| or |advance|, put it in |cur_val|@>@;
+else@<Compute result of |multiply| or |divide|, put it in |cur_val|@>;
+if (arith_error)
+ {@+print_err("Arithmetic overflow");
+@.Arithmetic overflow@>
+ help2("I can't carry out that multiplication or division,",@/
+ "since the result is out of range.");
+ if (p >= glue_val) delete_glue_ref(cur_val);
+ error();return;
+ }
+if (p < glue_val) sa_word_define(l, cur_val);
+else{@+trap_zero_glue();sa_define(l, cur_val, l, glue_ref, cur_val);
+ }
+}
+
+@ Here we use the fact that the consecutive codes |int_val dotdot mu_val| and
+|assign_int dotdot assign_mu_glue| correspond to each other nicely.
+
+@<Compute the register location |l| and its type |p|...@>=
+{@+if (q!=internal_register)
+ {@+get_x_token();
+ if ((cur_cmd >= assign_int)&&(cur_cmd <= assign_mu_glue))
+ {@+l=cur_chr;p=cur_cmd-assign_int;goto found;
+ }
+ if (cur_cmd!=internal_register)
+ {@+print_err("You can't use `");print_cmd_chr(cur_cmd, cur_chr);
+@.You can't use x after ...@>
+ print("' after ");print_cmd_chr(q, 0);
+ help1("I'm forgetting what you said and not changing anything.");
+ error();return;
+ }
+ }
+if ((cur_chr < mem_bot)||(cur_chr > lo_mem_stat_max))
+ {@+l=cur_chr;p=sa_type(l);e=true;
+ }
+else{@+p=cur_chr-mem_bot;scan_register_num();
+ if (cur_val > 255)
+ {@+find_sa_element(p, cur_val, true);l=cur_ptr;e=true;
+ }
+ else
+switch (p) {
+case int_val: l=cur_val+count_base;@+break;
+case dimen_val: l=cur_val+scaled_base;@+break;
+case glue_val: l=cur_val+skip_base;@+break;
+case mu_val: l=cur_val+mu_skip_base;
+} /*there are no other cases*/
+ }
+}
+found: if (p < glue_val) @+if (e) w=sa_int(l);@+else w=eqtb[l].i;
+else if (e) s=sa_ptr(l);@+else s=equiv(l)
+
+@ @<Compute result of |register| or |advance|...@>=
+if (p < glue_val)
+ {@+if (p==int_val) scan_int();@+else scan_normal_dimen;
+ if (q==advance) cur_val=cur_val+w;
+ }
+else{@+scan_glue(p);
+ if (q==advance) @<Compute the sum of two glue specs@>;
+ }
+
+@ @<Compute the sum of two glue specs@>=
+{@+q=new_spec(cur_val);r=s;
+delete_glue_ref(cur_val);
+width(q)=width(q)+width(r);
+if (stretch(q)==0) stretch_order(q)=normal;
+if (stretch_order(q)==stretch_order(r)) stretch(q)=stretch(q)+stretch(r);
+else if ((stretch_order(q) < stretch_order(r))&&(stretch(r)!=0))
+ {@+stretch(q)=stretch(r);stretch_order(q)=stretch_order(r);
+ }
+if (shrink(q)==0) shrink_order(q)=normal;
+if (shrink_order(q)==shrink_order(r)) shrink(q)=shrink(q)+shrink(r);
+else if ((shrink_order(q) < shrink_order(r))&&(shrink(r)!=0))
+ {@+shrink(q)=shrink(r);shrink_order(q)=shrink_order(r);
+ }
+cur_val=q;
+}
+
+@ @<Compute result of |multiply| or |divide|...@>=
+{@+scan_int();
+if (p < glue_val)
+ if (q==multiply)
+ if (p==int_val) cur_val=mult_integers(w, cur_val);
+ else cur_val=nx_plus_y(w, cur_val, 0);
+ else cur_val=x_over_n(w, cur_val);
+else{@+r=new_spec(s);
+ if (q==multiply)
+ {@+width(r)=nx_plus_y(width(s), cur_val, 0);
+ stretch(r)=nx_plus_y(stretch(s), cur_val, 0);
+ shrink(r)=nx_plus_y(shrink(s), cur_val, 0);
+ }
+ else{@+width(r)=x_over_n(width(s), cur_val);
+ stretch(r)=x_over_n(stretch(s), cur_val);
+ shrink(r)=x_over_n(shrink(s), cur_val);
+ }
+ cur_val=r;
+ }
+}
+
+@ The processing of boxes is somewhat different, because we may need
+to scan and create an entire box before we actually change the value of the old
+one.
+
+@<Assignments@>=
+case set_box: {@+scan_register_num();
+ if (global) n=global_box_flag+cur_val;@+else n=box_flag+cur_val;
+ scan_optional_equals();
+ if (set_box_allowed) scan_box(n);
+ else{@+print_err("Improper ");print_esc("setbox");
+@.Improper \\setbox@>
+ help2("Sorry, \\setbox is not allowed after \\halign in a display,",@/
+ "or between \\accent and an accented character.");error();
+ }
+ } @+break;
+
+@ The |space_factor| or |prev_depth| settings are changed when a |set_aux|
+command is sensed. Similarly, |prev_graf| is changed in the presence of
+|set_prev_graf|, and |dead_cycles| or |insert_penalties| in the presence of
+|set_page_int|. These definitions are always global.
+
+When some dimension of a box register is changed, the change isn't exactly
+global; but \TeX\ does not look at the \.{\\global} switch.
+
+@<Assignments@>=
+case set_aux: alter_aux();@+break;
+case set_prev_graf: alter_prev_graf();@+break;
+case set_page_dimen: alter_page_so_far();@+break;
+case set_page_int: alter_integer();@+break;
+case set_box_dimen: alter_box_dimen();@+break;
+
+@ @<Declare subprocedures for |prefixed_command|@>=
+static void alter_aux(void)
+{@+halfword c; /*|hmode| or |vmode|*/
+if (cur_chr!=abs(mode)) report_illegal_case();
+else{@+c=cur_chr;scan_optional_equals();
+ if (c==vmode)
+ {@+scan_normal_dimen;prev_depth=cur_val;
+ }
+ else{@+scan_int();
+ if ((cur_val <= 0)||(cur_val > 32767))
+ {@+print_err("Bad space factor");
+@.Bad space factor@>
+ help1("I allow only values in the range 1..32767 here.");
+ int_error(cur_val);
+ }
+ else space_factor=cur_val;
+ }
+ }
+}
+
+@ @<Declare subprocedures for |prefixed_command|@>=
+static void alter_prev_graf(void)
+{@+int p; /*index into |nest|*/
+nest[nest_ptr]=cur_list;p=nest_ptr;
+while (abs(nest[p].mode_field)!=vmode) decr(p);
+scan_optional_equals();scan_int();
+if (cur_val < 0)
+ {@+print_err("Bad ");print_esc("prevgraf");
+@.Bad \\prevgraf@>
+ help1("I allow only nonnegative values here.");
+ int_error(cur_val);
+ }
+else{@+nest[p].pg_field=cur_val;cur_list=nest[nest_ptr];
+ }
+}
+
+@ @<Declare subprocedures for |prefixed_command|@>=
+static void alter_page_so_far(void)
+{@+int c; /*index into |page_so_far|*/
+c=cur_chr;scan_optional_equals();scan_normal_dimen;
+page_so_far[c]=cur_val;
+}
+
+@ @<Declare subprocedures for |prefixed_command|@>=
+static void alter_integer(void)
+{@+small_number c;
+ /*0 for \.{\\deadcycles}, 1 for \.{\\insertpenalties}, etc.*/
+c=cur_chr;scan_optional_equals();scan_int();
+if (c==0) dead_cycles=cur_val
+@/@<Cases for |alter_integer|@>@;@/
+else insert_penalties=cur_val;
+}
+
+@ @<Declare subprocedures for |prefixed_command|@>=
+static void alter_box_dimen(void)
+{@+small_number c; /*|width_offset| or |height_offset| or |depth_offset|*/
+pointer @!b; /*box register*/
+c=cur_chr;scan_register_num();fetch_box(b);scan_optional_equals();
+scan_normal_dimen;
+if (b!=null) mem[b+c].sc=cur_val;
+}
+
+@ Paragraph shapes are set up in the obvious way.
+
+@<Assignments@>=
+case set_shape: {@+q=cur_chr;scan_optional_equals();scan_int();n=cur_val;
+ if (n <= 0) p=null;
+ else if (q > par_shape_loc)
+ {@+n=(cur_val/2)+1;p=get_node(2*n+1);info(p)=n;
+ n=cur_val;mem[p+1].i=n; /*number of penalties*/
+ for (j=p+2; j<=p+n+1; j++)
+ {@+scan_int();mem[j].i=cur_val; /*penalty values*/
+ }
+ if (!odd(n)) mem[p+n+2].i=0; /*unused*/
+ }
+ else{@+p=get_node(2*n+1);info(p)=n;
+ for (j=1; j<=n; j++)
+ {@+scan_normal_dimen;
+ mem[p+2*j-1].sc=cur_val; /*indentation*/
+ scan_normal_dimen;
+ mem[p+2*j].sc=cur_val; /*width*/
+ }
+ }
+ define(q, shape_ref, p);
+ } @+break;
+
+@ Here's something that isn't quite so obvious. It guarantees that
+|info(par_shape_ptr)| can hold any positive~|n| for which |get_node(2*n+1)|
+doesn't overflow the memory capacity.
+
+@<Check the ``constant''...@>=
+if (2*max_halfword < mem_top-mem_min) bad=41;
+
+@ New hyphenation data is loaded by the |hyph_data| command.
+
+@<Put each...@>=
+primitive("hyphenation", hyph_data, 0);
+@!@:hyphenation\_}{\.{\\hyphenation} primitive@>
+primitive("patterns", hyph_data, 1);
+@!@:patterns\_}{\.{\\patterns} primitive@>
+
+@ @<Cases of |print_cmd_chr|...@>=
+case hyph_data: if (chr_code==1) print_esc("patterns");
+ else print_esc("hyphenation");@+break;
+
+@ @<Assignments@>=
+case hyph_data: if (cur_chr==1)
+ {
+#ifdef @!INIT
+new_patterns();goto done;@;
+#endif
+ print_err("Patterns can be loaded only by INITEX");
+@.Patterns can be...@>
+ help0;error();
+ @/do@+{get_token();}@+ while (!(cur_cmd==right_brace)); /*flush the patterns*/
+ return;
+ }
+ else{@+new_hyph_exceptions();goto done;
+ } @+break;
+
+@ All of \TeX's parameters are kept in |eqtb| except the font information,
+the interaction mode, and the hyphenation tables; these are strictly global.
+
+@<Assignments@>=
+case assign_font_dimen: {@+find_font_dimen(true);k=cur_val;
+ scan_optional_equals();scan_normal_dimen;font_info[k].sc=cur_val;
+ } @+break;
+case assign_font_int: {@+n=cur_chr;scan_font_ident();f=cur_val;
+ scan_optional_equals();scan_int();
+ if (n==0) hyphen_char[f]=cur_val;@+else skew_char[f]=cur_val;
+ } @+break;
+
+@ @<Put each...@>=
+primitive("hyphenchar", assign_font_int, 0);
+@!@:hyphen\_char\_}{\.{\\hyphenchar} primitive@>
+primitive("skewchar", assign_font_int, 1);
+@!@:skew\_char\_}{\.{\\skewchar} primitive@>
+
+@ @<Cases of |print_cmd_chr|...@>=
+case assign_font_int: if (chr_code==0) print_esc("hyphenchar");
+ else print_esc("skewchar");@+break;
+
+@ Here is where the information for a new font gets loaded.
+
+@<Assignments@>=
+case def_font: new_font(a);@+break;
+
+@ @<Declare subprocedures for |prefixed_command|@>=
+static void new_font(small_number @!a)
+{@+
+pointer u; /*user's font identifier*/
+scaled @!s; /*stated ``at'' size, or negative of scaled magnification*/
+int @!f; /*runs through existing fonts*/
+str_number @!t; /*name for the frozen font identifier*/
+int @!old_setting; /*holds |selector| setting*/
+str_number @!flushable_string; /*string not yet referenced*/
+if (job_name==0) open_log_file();
+ /*avoid confusing \.{texput} with the font name*/
+@.texput@>
+get_r_token();u=cur_cs;
+if (u >= hash_base) t=text(u);
+else if (u >= single_base)
+ if (u==null_cs) t=s_no("FONT");@+else t=u-single_base;
+else{@+old_setting=selector;selector=new_string;
+ print("FONT");printn(u-active_base);selector=old_setting;
+@.FONTx@>
+ str_room(1);t=make_string();
+ }
+define(u, set_font, null_font);scan_optional_equals();scan_file_name();
+@<Scan the font size specification@>;
+@<If this font has already been loaded, set |f| to the internal font number
+and |goto common_ending|@>;
+f=read_font_info(u, cur_name, cur_area, s);
+common_ending: define(u, set_font, f);eqtb[font_id_base+f]=eqtb[u];font_id_text(f)=t;
+}
+
+@ @<Scan the font size specification@>=
+name_in_progress=true; /*this keeps |cur_name| from being changed*/
+if (scan_keyword("at")) @<Put the \(p)(positive) `at' size into |s|@>@;
+@.at@>
+else if (scan_keyword("scaled"))
+@.scaled@>
+ {@+scan_int();s=-cur_val;
+ if ((cur_val <= 0)||(cur_val > 32768))
+ {@+print_err("Illegal magnification has been changed to 1000");@/
+@.Illegal magnification...@>
+ help1("The magnification ratio must be between 1 and 32768.");
+ int_error(cur_val);s=-1000;
+ }
+ }
+else s=-1000;
+name_in_progress=false
+
+@ @<Put the \(p)(positive) `at' size into |s|@>=
+{@+scan_normal_dimen;s=cur_val;
+if ((s <= 0)||(s >= 01000000000))
+ {@+print_err("Improper `at' size (");
+ print_scaled(s);print("pt), replaced by 10pt");
+@.Improper `at' size...@>
+ help2("I can only handle fonts at positive sizes that are",@/
+ "less than 2048pt, so I've changed what you said to 10pt.");
+ error();s=10*unity;
+ }
+}
+
+@ When the user gives a new identifier to a font that was previously loaded,
+the new name becomes the font identifier of record. Font names `\.{xyz}' and
+`\.{XYZ}' are considered to be different.
+
+@<If this font has already been loaded...@>=
+flushable_string=str_ptr-1;
+for (f=font_base+1; f<=font_ptr; f++)
+ if (str_eq_str(font_name[f], cur_name)&&str_eq_str(font_area[f], cur_area))
+ {@+if (cur_name==flushable_string)
+ {@+flush_string;cur_name=font_name[f];
+ }
+ if (s > 0)
+ {@+if (s==font_size[f]) goto common_ending;
+ }
+ else if (font_size[f]==xn_over_d(font_dsize[f],-s, 1000))
+ goto common_ending;
+ }
+
+@ @<Cases of |print_cmd_chr|...@>=
+case set_font: {@+print("select font ");slow_print(font_name[chr_code]);
+ if (font_size[chr_code]!=font_dsize[chr_code])
+ {@+print(" at ");print_scaled(font_size[chr_code]);
+ print("pt");
+ }
+ } @+break;
+
+@ @<Put each...@>=
+primitive("batchmode", set_interaction, batch_mode);
+@!@:batch\_mode\_}{\.{\\batchmode} primitive@>
+primitive("nonstopmode", set_interaction, nonstop_mode);
+@!@:nonstop\_mode\_}{\.{\\nonstopmode} primitive@>
+primitive("scrollmode", set_interaction, scroll_mode);
+@!@:scroll\_mode\_}{\.{\\scrollmode} primitive@>
+primitive("errorstopmode", set_interaction, error_stop_mode);
+@!@:error\_stop\_mode\_}{\.{\\errorstopmode} primitive@>
+
+@ @<Cases of |print_cmd_chr|...@>=
+case set_interaction: switch (chr_code) {
+ case batch_mode: print_esc("batchmode");@+break;
+ case nonstop_mode: print_esc("nonstopmode");@+break;
+ case scroll_mode: print_esc("scrollmode");@+break;
+ default:print_esc("errorstopmode");
+ } @+break;
+
+@ @<Assignments@>=
+case set_interaction: new_interaction();@+break;
+
+@ @<Declare subprocedures for |prefixed_command|@>=
+static void new_interaction(void)
+{@+print_ln();
+interaction=cur_chr;
+@<Initialize the print |selector| based on |interaction|@>;
+if (log_opened) selector=selector+2;
+}
+
+@ The \.{\\afterassignment} command puts a token into the global
+variable |after_token|. This global variable is examined just after
+every assignment has been performed.
+
+@<Glob...@>=
+static halfword @!after_token; /*zero, or a saved token*/
+
+@ @<Set init...@>=
+after_token=0;
+
+@ @<Cases of |main_control| that don't...@>=
+any_mode(after_assignment): {@+get_token();after_token=cur_tok;
+ } @+break;
+
+@ @<Insert a token saved by \.{\\afterassignment}, if any@>=
+if (after_token!=0)
+ {@+cur_tok=after_token;back_input();after_token=0;
+ }
+
+@ Here is a procedure that might be called `Get the next non-blank non-relax
+non-call non-assignment token'.
+
+@<Declare act...@>=
+static void do_assignments(void)
+{@+
+loop{@+@<Get the next non-blank non-relax...@>;
+ if (cur_cmd <= max_non_prefixed_command) return;
+ set_box_allowed=false;prefixed_command();set_box_allowed=true;
+ }
+}
+
+@ @<Cases of |main_control| that don't...@>=
+any_mode(after_group): {@+get_token();save_for_after(cur_tok);
+ } @+break;
+
+@ Files for \.{\\read} are opened and closed by the |in_stream| command.
+
+@<Put each...@>=
+primitive("openin", in_stream, 1);
+@!@:open\_in\_}{\.{\\openin} primitive@>
+primitive("closein", in_stream, 0);
+@!@:close\_in\_}{\.{\\closein} primitive@>
+
+@ @<Cases of |print_cmd_chr|...@>=
+case in_stream: if (chr_code==0) print_esc("closein");
+ else print_esc("openin");@+break;
+
+@ @<Cases of |main_control| that don't...@>=
+any_mode(in_stream): open_or_close_in();@+break;
+
+@ @<Declare act...@>=
+static void open_or_close_in(void)
+{@+int c; /*1 for \.{\\openin}, 0 for \.{\\closein}*/
+int @!n; /*stream number*/
+c=cur_chr;scan_four_bit_int();n=cur_val;
+if (read_open[n]!=closed)
+ {@+a_close(&read_file[n]);read_open[n]=closed;
+ }
+if (c!=0)
+ {@+scan_optional_equals();scan_file_name();
+ pack_cur_name(".tex");
+ if (a_open_in(&read_file[n]))
+ { read_open[n]=just_open;
+ @<Set new |read_file_num[n]|@>@;
+ }
+ }
+}
+
+@ The user can issue messages to the terminal, regardless of the
+current mode.
+
+@<Cases of |main_control| that don't...@>=
+any_mode(message): issue_message();@+break;
+
+@ @<Put each...@>=
+primitive("message", message, 0);
+@!@:message\_}{\.{\\message} primitive@>
+primitive("errmessage", message, 1);
+@!@:err\_message\_}{\.{\\errmessage} primitive@>
+
+@ @<Cases of |print_cmd_chr|...@>=
+case message: if (chr_code==0) print_esc("message");
+ else print_esc("errmessage");@+break;
+
+@ @<Declare act...@>=
+static void issue_message(void)
+{@+int old_setting; /*holds |selector| setting*/
+int @!c; /*identifies \.{\\message} and \.{\\errmessage}*/
+str_number @!s; /*the message*/
+c=cur_chr;link(garbage)=scan_toks(false, true);
+old_setting=selector;selector=new_string;
+token_show(def_ref);selector=old_setting;
+flush_list(def_ref);
+str_room(1);s=make_string();
+if (c==0) @<Print string |s| on the terminal@>@;
+else@<Print string |s| as an error message@>;
+flush_string;
+}
+
+@ @<Print string |s| on the terminal@>=
+{@+if (term_offset+length(s) > max_print_line-2) print_ln();
+else if ((term_offset > 0)||(file_offset > 0)) print_char(' ');
+slow_print(s);update_terminal;
+}
+
+@ If \.{\\errmessage} occurs often in |scroll_mode|, without user-defined
+\.{\\errhelp}, we don't want to give a long help message each time. So we
+give a verbose explanation only once.
+
+@<Glob...@>=
+static bool @!long_help_seen; /*has the long \.{\\errmessage} help been used?*/
+
+@ @<Set init...@>=long_help_seen=false;
+
+@ @<Print string |s| as an error message@>=
+{@+print_err("");slow_print(s);
+if (err_help!=null) use_err_help=true;
+else if (long_help_seen) help1("(That was another \\errmessage.)")@;
+else{@+if (interaction < error_stop_mode) long_help_seen=true;
+ help4("This error message was generated by an \\errmessage",@/
+ "command, so I can't give any explicit help.",@/
+ "Pretend that you're Hercule Poirot: Examine all clues,",@/
+@^Poirot, Hercule@>
+ "and deduce the truth by order and method.");
+ }
+error();use_err_help=false;
+}
+
+@ The |error| routine calls on |give_err_help| if help is requested from
+the |err_help| parameter.
+
+@p static void give_err_help(void)
+{@+token_show(err_help);
+}
+
+@ The \.{\\uppercase} and \.{\\lowercase} commands are implemented by
+building a token list and then changing the cases of the letters in it.
+
+@<Cases of |main_control| that don't...@>=
+any_mode(case_shift): shift_case();@+break;
+
+@ @<Put each...@>=
+primitive("lowercase", case_shift, lc_code_base);
+@!@:lowercase\_}{\.{\\lowercase} primitive@>
+primitive("uppercase", case_shift, uc_code_base);
+@!@:uppercase\_}{\.{\\uppercase} primitive@>
+
+@ @<Cases of |print_cmd_chr|...@>=
+case case_shift: if (chr_code==lc_code_base) print_esc("lowercase");
+ else print_esc("uppercase");@+break;
+
+@ @<Declare act...@>=
+static void shift_case(void)
+{@+pointer b; /*|lc_code_base| or |uc_code_base|*/
+pointer @!p; /*runs through the token list*/
+halfword @!t; /*token*/
+eight_bits @!c; /*character code*/
+b=cur_chr;p=scan_toks(false, false);p=link(def_ref);
+while (p!=null)
+ {@+@<Change the case of the token in |p|, if a change is appropriate@>;
+ p=link(p);
+ }
+back_list(link(def_ref));free_avail(def_ref); /*omit reference count*/
+}
+
+@ When the case of a |chr_code| changes, we don't change the |cmd|.
+We also change active characters, using the fact that
+|cs_token_flag+active_base| is a multiple of~256.
+@^data structure assumptions@>
+
+@<Change the case of the token in |p|, if a change is appropriate@>=
+t=info(p);
+if (t < cs_token_flag+single_base)
+ {@+c=t%256;
+ if (equiv(b+c)!=0) info(p)=t-c+equiv(b+c);
+ }
+
+@ We come finally to the last pieces missing from |main_control|, namely the
+`\.{\\show}' commands that are useful when debugging.
+
+@<Cases of |main_control| that don't...@>=
+any_mode(xray): show_whatever();@+break;
+
+@ @d show_code 0 /* \.{\\show} */
+@d show_box_code 1 /* \.{\\showbox} */
+@d show_the_code 2 /* \.{\\showthe} */
+@d show_lists_code 3 /* \.{\\showlists} */
+
+@<Put each...@>=
+primitive("show", xray, show_code);
+@!@:show\_}{\.{\\show} primitive@>
+primitive("showbox", xray, show_box_code);
+@!@:show\_box\_}{\.{\\showbox} primitive@>
+primitive("showthe", xray, show_the_code);
+@!@:show\_the\_}{\.{\\showthe} primitive@>
+primitive("showlists", xray, show_lists_code);
+@!@:show\_lists\_code\_}{\.{\\showlists} primitive@>
+
+@ @<Cases of |print_cmd_chr|...@>=
+case xray: switch (chr_code) {
+ case show_box_code: print_esc("showbox");@+break;
+ case show_the_code: print_esc("showthe");@+break;
+ case show_lists_code: print_esc("showlists");@+break;
+ @<Cases of |xray| for |print_cmd_chr|@>@;@/
+ default:print_esc("show");
+ } @+break;
+
+@ @<Declare act...@>=
+static void show_whatever(void)
+{@+
+pointer p; /*tail of a token list to show*/
+small_number @!t; /*type of conditional being shown*/
+int @!m; /*upper bound on |fi_or_else| codes*/
+int @!l; /*line where that conditional began*/
+int @!n; /*level of \.{\\if...\\fi} nesting*/
+switch (cur_chr) {
+case show_lists_code: {@+begin_diagnostic();show_activities();
+ } @+break;
+case show_box_code: @<Show the current contents of a box@>@;@+break;
+case show_code: @<Show the current meaning of a token, then |goto common_ending|@>@;
+@<Cases for |show_whatever|@>@;@/
+default:@<Show the current value of some parameter or register, then |goto
+common_ending|@>@;
+} @/
+@<Complete a potentially long \.{\\show} command@>;
+common_ending: if (interaction < error_stop_mode)
+ {@+help0;decr(error_count);
+ }
+else if (tracing_online > 0)
+ {@+@t@>@;@/
+ help3("This isn't an error message; I'm just \\showing something.",@/
+ "Type `I\\show...' to show more (e.g., \\show\\cs,",@/
+ "\\showthe\\count10, \\showbox255, \\showlists).");
+ }
+else{@+@t@>@;@/
+ help5("This isn't an error message; I'm just \\showing something.",@/
+ "Type `I\\show...' to show more (e.g., \\show\\cs,",@/
+ "\\showthe\\count10, \\showbox255, \\showlists).",@/
+ "And type `I\\tracingonline=1\\show...' to show boxes and",@/
+ "lists on your terminal as well as in the transcript file.");
+ }
+error();
+}
+
+@ @<Show the current meaning of a token...@>=
+{@+get_token();
+if (interaction==error_stop_mode) wake_up_terminal;
+print_nl("> ");
+if (cur_cs!=0)
+ {@+sprint_cs(cur_cs);print_char('=');
+ }
+print_meaning();goto common_ending;
+}
+
+@ @<Cases of |print_cmd_chr|...@>=
+case undefined_cs: print("undefined");@+break;
+case call: case long_call: case outer_call:
+ case long_outer_call: {@+n=cmd-call;
+ if (info(link(chr_code))==protected_token) n=n+4;
+ if (odd(n/4)) print_esc("protected");
+ if (odd(n)) print_esc("long");
+ if (odd(n/2)) print_esc("outer");
+ if (n > 0) print_char(' ');
+ print("macro");
+ } @+break;
+case end_template: print_esc("outer endtemplate");@+break;
+
+@ @<Show the current contents of a box@>=
+{@+scan_register_num();fetch_box(p);begin_diagnostic();
+print_nl("> \\box");print_int(cur_val);print_char('=');
+if (p==null) print("void");@+else show_box(p);
+}
+
+@ @<Show the current value of some parameter...@>=
+{@+the_toks();
+if (interaction==error_stop_mode) wake_up_terminal;
+print_nl("> ");token_show(temp_head);
+flush_list(link(temp_head));goto common_ending;
+}
+
+@ @<Complete a potentially long \.{\\show} command@>=
+end_diagnostic(true);print_err("OK");
+@.OK@>
+if (selector==term_and_log) if (tracing_online <= 0)
+ {@+selector=term_only;print(" (see the transcript file)");
+ selector=term_and_log;
+ }
+
+@* Dumping and undumping the tables.
+After \.{INITEX} has seen a collection of fonts and macros, it
+can write all the necessary information on an auxiliary file so
+that production versions of \TeX\ are able to initialize their
+memory at high speed. The present section of the program takes
+care of such output and input. We shall consider simultaneously
+the processes of storing and restoring,
+so that the inverse relation between them is clear.
+@.INITEX@>
+
+The global variable |format_ident| is a string that is printed right
+after the |banner| line when \TeX\ is ready to start. For \.{INITEX} this
+string says simply `\.{ (INITEX)}'; for other versions of \TeX\ it says,
+for example, `\.{ (preloaded format=plain 1982.11.19)}', showing the year,
+month, and day that the format file was created. We have |format_ident==0|
+before \TeX's tables are loaded.
+
+@<Glob...@>=
+static str_number @!format_ident, frozen_format_ident;
+
+@ @<Set init...@>=
+format_ident=frozen_format_ident=0;
+
+@ We keep a copy of the initial value, be able to test for it later.
+
+@<Initialize table entries...@>=
+format_ident=frozen_format_ident=s_no(" (INITEX)");
+
+@ @<Declare act...@>=
+#ifdef @!INIT
+static void store_fmt_file(void)
+{@+
+int j, @!k, @!l; /*all-purpose indices*/
+int @!p, @!q; /*all-purpose pointers*/
+int @!x; /*something to dump*/
+four_quarters @!w; /*four ASCII codes*/
+@<If dumping is not allowed, abort@>;
+@<Create the |format_ident|, open the format file, and inform the user that
+dumping has begun@>;
+@<Dump constants for consistency check@>;
+@<Dump the string pool@>;
+@<Dump the dynamic memory@>;
+@<Dump the table of equivalents@>;
+@<Dump the font information@>;
+@<Dump the hyphenation tables@>;
+@<Dump the full file names@>;
+@<Dump the file and line information@>;
+@<Dump a couple more things and the closing check word@>;
+@<Close the format file@>;
+}
+#endif
+
+@ Corresponding to the procedure that dumps a format file, we have a function
+that reads one in. The function returns |false| if the dumped format is
+incompatible with the present \TeX\ table sizes, etc.
+
+@d too_small(X) {@+wake_up_terminal;
+ wterm_ln("---! Must increase the %s", X);
+@.Must increase the x@>
+ goto bad_fmt;
+ }
+
+@p @t\4@>@<Declare the function called |open_fmt_file|@>@;
+static bool load_fmt_file(void)
+{@+
+int j, @!k; /*all-purpose indices*/
+int @!p, @!q; /*all-purpose pointers*/
+int @!x; /*something undumped*/
+four_quarters @!w; /*four ASCII codes*/
+@<Undump constants for consistency check@>;
+@<Undump the string pool@>;
+@<Undump the dynamic memory@>;
+@<Undump the table of equivalents@>;
+@<Undump the font information@>;
+@<Undump the hyphenation tables@>;
+@<Undump the full file names@>;
+@<Undump the file and line information@>;
+@<Undump a couple more things and the closing check word@>;
+return true; /*it worked!*/
+bad_fmt: wake_up_terminal;
+ wterm_ln("(Fatal format file error; I'm stymied)");
+@.Fatal format file error@>
+return false;
+}
+
+@ The user is not allowed to dump a format file unless |save_ptr==0|.
+This condition implies that |cur_level==level_one|, hence
+the |xeq_level| array is constant and it need not be dumped.
+
+@<If dumping is not allowed, abort@>=
+if (save_ptr!=0)
+ {@+print_err("You can't dump inside a group");
+@.You can't dump...@>
+ help1("`{...\\dump}' is a no-no.");succumb;
+ }
+
+@ Format files consist of |memory_word| items, and we use the following
+macros to dump words of different types:
+
+@d dump_wd(A) {@+fmt_file.d=A;put(fmt_file);@+}
+@d dump_int(A) {@+fmt_file.d.i=A;put(fmt_file);@+}
+@d dump_hh(A) {@+fmt_file.d.hh=A;put(fmt_file);@+}
+@d dump_qqqq(A) {@+fmt_file.d.qqqq=A;put(fmt_file);@+}
+
+@<Glob...@>=
+static word_file @!fmt_file; /*for input or output of format information*/
+
+@ The inverse macros are slightly more complicated, since we need to check
+the range of the values we are reading in. We say `|undump(a)(b)(x)|' to
+read an integer value |x| that is supposed to be in the range |a <= x <= b|.
+System error messages should be suppressed when undumping.
+@^system dependencies@>
+
+@d undump_wd(A) {@+get(fmt_file);A=fmt_file.d;@+}
+@d undump_int(A) {@+get(fmt_file);A=fmt_file.d.i;@+}
+@d undump_hh(A) {@+get(fmt_file);A=fmt_file.d.hh;@+}
+@d undump_qqqq(A) {@+get(fmt_file);A=fmt_file.d.qqqq;@+}
+@d undump(A, B, C) {@+undump_int(x);if ((x < A)||(x > B)) goto bad_fmt;@+else C=x;@+}
+@d undump_size(A, B, C, D) {@+undump_int(x);
+ if (x < A) goto bad_fmt;if (x > B) too_small(C)@;@+else D=x;@+}
+
+@ The next few sections of the program should make it clear how we use the
+dump/undump macros.
+
+@<Dump constants for consistency check@>=
+dump_int(0);@/
+@<Dump the \eTeX\ state@>@/
+@<Dump the \Prote\ state@>@/
+@<Dump the |ROM| array@>@/
+dump_int(mem_bot);@/
+dump_int(mem_top);@/
+dump_int(eqtb_size);@/
+dump_int(hash_prime);@/
+dump_int(hyph_size)
+
+@ Sections of a \.{WEB} program that are ``commented out'' still contribute
+strings to the string pool; therefore \.{INITEX} and \TeX\ will have
+the same strings. (And it is, of course, a good thing that they do.)
+@.WEB@>
+@^string pool@>
+
+@<Undump constants for consistency check@>=
+x=fmt_file.d.i;
+if (x!=0) goto bad_fmt; /*check that strings are the same*/
+@/@<Undump the \eTeX\ state@>@/
+@/@<Undump the \Prote\ state@>@/
+@/@<Undump the |ROM| array@>@/
+undump_int(x);
+if (x!=mem_bot) goto bad_fmt;
+undump_int(x);
+if (x!=mem_top) goto bad_fmt;
+undump_int(x);
+if (x!=eqtb_size) goto bad_fmt;
+undump_int(x);
+if (x!=hash_prime) goto bad_fmt;
+undump_int(x);
+if (x!=hyph_size) goto bad_fmt
+
+@ @d dump_four_ASCII
+ w.b0=qi(so(str_pool[k]));w.b1=qi(so(str_pool[k+1]));
+ w.b2=qi(so(str_pool[k+2]));w.b3=qi(so(str_pool[k+3]));
+ dump_qqqq(w)
+
+@<Dump the string pool@>=
+dump_int(pool_ptr);
+dump_int(str_ptr);
+for (k=0; k<=str_ptr; k++) dump_int(str_start[k]);
+k=0;
+while (k+4 < pool_ptr)
+ {@+dump_four_ASCII;k=k+4;
+ }
+k=pool_ptr-4;dump_four_ASCII;
+print_ln();print_int(str_ptr);print(" strings of total length ");
+print_int(pool_ptr)
+
+@ @d undump_four_ASCII
+ undump_qqqq(w);
+ str_pool[k]=si(qo(w.b0));str_pool[k+1]=si(qo(w.b1));
+ str_pool[k+2]=si(qo(w.b2));str_pool[k+3]=si(qo(w.b3))
+
+@<Undump the string pool@>=
+undump_size(0, pool_size,"string pool size", pool_ptr);
+undump_size(0, max_strings,"max strings", str_ptr);
+for (k=0; k<=str_ptr; k++) undump(0, pool_ptr, str_start[k]);
+k=0;
+while (k+4 < pool_ptr)
+ {@+undump_four_ASCII;k=k+4;
+ }
+k=pool_ptr-4;undump_four_ASCII;
+init_str_ptr=str_ptr;init_pool_ptr=pool_ptr
+
+@ By sorting the list of available spaces in the variable-size portion of
+|mem|, we are usually able to get by without having to dump very much
+of the dynamic memory.
+
+We recompute |var_used| and |dyn_used|, so that \.{INITEX} dumps valid
+information even when it has not been gathering statistics.
+
+@<Dump the dynamic memory@>=
+sort_avail();var_used=0;
+dump_int(lo_mem_max);dump_int(rover);
+if (eTeX_ex) for (k=int_val; k<=tok_val; k++) dump_int(sa_root[k]);
+p=mem_bot;q=rover;x=0;
+@/do@+{for (k=p; k<=q+1; k++) dump_wd(mem[k]);
+x=x+q+2-p;var_used=var_used+q-p;
+p=q+node_size(q);q=rlink(q);
+}@+ while (!(q==rover));
+var_used=var_used+lo_mem_max-p;dyn_used=mem_end+1-hi_mem_min;@/
+for (k=p; k<=lo_mem_max; k++) dump_wd(mem[k]);
+x=x+lo_mem_max+1-p;
+dump_int(hi_mem_min);dump_int(avail);
+for (k=hi_mem_min; k<=mem_end; k++) dump_wd(mem[k]);
+p=avail;
+while (p!=null)
+{@+fl_mem[p]=0; /*clear unallocated |fl_mem|*/
+ decr(dyn_used);p=link(p);
+}
+dump_int(var_used);dump_int(dyn_used);
+print_ln();print_int(x);print_char('&');print_int(mem_end+1-hi_mem_min);
+print(" memory locations dumped; current usage is ");
+print_int(var_used);print_char('&');print_int(dyn_used)
+
+@ @<Undump the dynamic memory@>=
+undump(lo_mem_stat_max+1000, hi_mem_stat_min-1, lo_mem_max);
+undump(lo_mem_stat_max+1, lo_mem_max, rover);
+if (eTeX_ex) for (k=int_val; k<=tok_val; k++)
+ undump(null, lo_mem_max, sa_root[k]);
+p=mem_bot;q=rover;
+@/do@+{for (k=p; k<=q+1; k++) undump_wd(mem[k]);
+p=q+node_size(q);
+if ((p > lo_mem_max)||((q >= rlink(q))&&(rlink(q)!=rover))) goto bad_fmt;
+q=rlink(q);
+}@+ while (!(q==rover));
+for (k=p; k<=lo_mem_max; k++) undump_wd(mem[k]);
+if (mem_min < mem_bot-2) /*make more low memory available*/
+ {@+p=llink(rover);q=mem_min+1;
+ link(mem_min)=null;info(mem_min)=null; /*we don't use the bottom word*/
+ rlink(p)=q;llink(rover)=q;@/
+ rlink(q)=rover;llink(q)=p;link(q)=empty_flag;
+ node_size(q)=mem_bot-q;
+ }
+undump(lo_mem_max+1, hi_mem_stat_min, hi_mem_min);
+undump(null, mem_top, avail);mem_end=mem_top;
+for (k=hi_mem_min; k<=mem_end; k++) undump_wd(mem[k]);
+undump_int(var_used);undump_int(dyn_used)
+
+@ @<Dump the table of equivalents@>=
+@<Dump regions 1 to 4 of |eqtb|@>;
+@<Dump regions 5 and 6 of |eqtb|@>;
+dump_int(par_loc);dump_int(write_loc);@/
+dump_int(input_loc);@/
+@<Dump the hash table@>@;
+
+@ @<Undump the table of equivalents@>=
+@<Undump regions 1 to 6 of |eqtb|@>;
+undump(hash_base, frozen_control_sequence, par_loc);
+par_token=cs_token_flag+par_loc;@/
+undump(hash_base, frozen_control_sequence, write_loc);@/
+undump(hash_base, frozen_control_sequence, input_loc);
+input_token=cs_token_flag+input_loc;@/
+@<Undump the hash table@>@;
+
+@ The table of equivalents usually contains repeated information, so we dump it
+in compressed form: The sequence of $n+2$ values $(n,x_1,\ldots,x_n,m)$ in the
+format file represents $n+m$ consecutive entries of |eqtb|, with |m| extra
+copies of $x_n$, namely $(x_1,\ldots,x_n,x_n,\ldots,x_n)$.
+
+@<Dump regions 1 to 4 of |eqtb|@>=
+k=active_base;
+@/do@+{j=k;
+while (j < int_base-1)
+ {@+if ((equiv(j)==equiv(j+1))&&(eq_type(j)==eq_type(j+1))&&@|
+ (eq_level(j)==eq_level(j+1))) goto found1;
+ incr(j);
+ }
+l=int_base;goto done1; /*|j==int_base-1|*/
+found1: incr(j);l=j;
+while (j < int_base-1)
+ {@+if ((equiv(j)!=equiv(j+1))||(eq_type(j)!=eq_type(j+1))||@|
+ (eq_level(j)!=eq_level(j+1))) goto done1;
+ incr(j);
+ }
+done1: dump_int(l-k);
+while (k < l)
+ {@+dump_wd(eqtb[k]);incr(k);
+ }
+k=j+1;dump_int(k-l);
+}@+ while (!(k==int_base))
+
+@ @<Dump regions 5 and 6 of |eqtb|@>=
+@/do@+{j=k;
+while (j < eqtb_size)
+ {@+if (eqtb[j].i==eqtb[j+1].i) goto found2;
+ incr(j);
+ }
+l=eqtb_size+1;goto done2; /*|j==eqtb_size|*/
+found2: incr(j);l=j;
+while (j < eqtb_size)
+ {@+if (eqtb[j].i!=eqtb[j+1].i) goto done2;
+ incr(j);
+ }
+done2: dump_int(l-k);
+while (k < l)
+ {@+dump_wd(eqtb[k]);incr(k);
+ }
+k=j+1;dump_int(k-l);
+}@+ while (!(k > eqtb_size))
+
+@ @<Undump regions 1 to 6 of |eqtb|@>=
+k=active_base;
+@/do@+{undump_int(x);
+if ((x < 1)||(k+x > eqtb_size+1)) goto bad_fmt;
+for (j=k; j<=k+x-1; j++) undump_wd(eqtb[j]);
+k=k+x;
+undump_int(x);
+if ((x < 0)||(k+x > eqtb_size+1)) goto bad_fmt;
+for (j=k; j<=k+x-1; j++) eqtb[j]=eqtb[k-1];
+k=k+x;
+}@+ while (!(k > eqtb_size))
+
+@ A different scheme is used to compress the hash table, since its lower
+region is usually sparse. When |text(p)!=0| for |p <= hash_used|, we output
+two words, |p| and |hash[p]|. The hash table is, of course, densely packed
+for |p >= hash_used|, so the remaining entries are output in a~block.
+
+@<Dump the hash table@>=
+dump_int(hash_used);cs_count=frozen_control_sequence-1-hash_used;
+for (p=hash_base; p<=hash_used; p++) if (text(p)!=0)
+ {@+dump_int(p);dump_hh(hash[p]);incr(cs_count);
+ }
+for (p=hash_used+1; p<=undefined_control_sequence-1; p++) dump_hh(hash[p]);
+dump_int(cs_count);@/
+print_ln();print_int(cs_count);print(" multiletter control sequences")
+
+@ @<Undump the hash table@>=
+undump(hash_base, frozen_control_sequence, hash_used);p=hash_base-1;
+@/do@+{undump(p+1, hash_used, p);undump_hh(hash[p]);
+}@+ while (!(p==hash_used));
+for (p=hash_used+1; p<=undefined_control_sequence-1; p++) undump_hh(hash[p]);
+undump_int(cs_count)
+
+@ @<Dump the font information@>=
+dump_int(fmem_ptr);
+for (k=0; k<=fmem_ptr-1; k++) dump_wd(font_info[k]);
+dump_int(font_ptr);
+for (k=null_font; k<=font_ptr; k++)
+ @<Dump the array info for internal font number |k|@>;
+print_ln();print_int(fmem_ptr-7);print(" words of font info for ");
+print_int(font_ptr-font_base);print(" preloaded font");
+if (font_ptr!=font_base+1) print_char('s')
+
+@ @<Undump the font information@>=
+undump_size(7, font_mem_size,"font mem size", fmem_ptr);
+for (k=0; k<=fmem_ptr-1; k++) undump_wd(font_info[k]);
+undump_size(font_base, font_max,"font max", font_ptr);
+for (k=null_font; k<=font_ptr; k++)
+ @<Undump the array info for internal font number |k|@>@;
+
+@ @<Dump the array info for internal font number |k|@>=
+{@+dump_qqqq(font_check[k]);
+dump_int(font_size[k]);
+dump_int(font_dsize[k]);
+dump_int(font_params[k]);@/
+dump_int(hyphen_char[k]);
+dump_int(skew_char[k]);@/
+dump_int(font_name[k]);
+dump_int(font_area[k]);@/
+dump_int(font_bc[k]);
+dump_int(font_ec[k]);@/
+dump_int(char_base[k]);
+dump_int(width_base[k]);
+dump_int(height_base[k]);@/
+dump_int(depth_base[k]);
+dump_int(italic_base[k]);
+dump_int(lig_kern_base[k]);@/
+dump_int(kern_base[k]);
+dump_int(exten_base[k]);
+dump_int(param_base[k]);@/
+dump_int(font_glue[k]);@/
+dump_int(bchar_label[k]);
+dump_int(font_bchar[k]);
+dump_int(font_false_bchar[k]);@/
+print_nl("\\font");printn_esc(font_id_text(k));print_char('=');
+print_file_name(font_name[k], font_area[k],empty_string);
+if (font_size[k]!=font_dsize[k])
+ {@+print(" at ");print_scaled(font_size[k]);print("pt");
+ }
+}
+
+@ @<Undump the array info for internal font number |k|@>=
+{@+undump_qqqq(font_check[k]);@/
+undump_int(font_size[k]);
+undump_int(font_dsize[k]);
+undump(min_halfword, max_halfword, font_params[k]);@/
+undump_int(hyphen_char[k]);
+undump_int(skew_char[k]);@/
+undump(0, str_ptr, font_name[k]);
+undump(0, str_ptr, font_area[k]);@/
+undump(0, 255, font_bc[k]);
+undump(0, 255, font_ec[k]);@/
+undump_int(char_base[k]);
+undump_int(width_base[k]);
+undump_int(height_base[k]);@/
+undump_int(depth_base[k]);
+undump_int(italic_base[k]);
+undump_int(lig_kern_base[k]);@/
+undump_int(kern_base[k]);
+undump_int(exten_base[k]);
+undump_int(param_base[k]);@/
+undump(min_halfword, lo_mem_max, font_glue[k]);@/
+undump(0, fmem_ptr-1, bchar_label[k]);
+undump(min_quarterword, non_char, font_bchar[k]);
+undump(min_quarterword, non_char, font_false_bchar[k]);
+}
+
+@ @<Dump the hyphenation tables@>=
+dump_int(hyph_count);
+for (k=0; k<=hyph_size; k++) if (hyph_word[k]!=0)
+ {@+dump_int(k);dump_int(hyph_word[k]);dump_int(hyph_list[k]);
+ }
+print_ln();print_int(hyph_count);print(" hyphenation exception");
+if (hyph_count!=1) print_char('s');
+if (trie_not_ready) init_trie();
+dump_int(trie_max);
+dump_int(hyph_start);
+for (k=0; k<=trie_max; k++) dump_hh(trie[k]);
+dump_int(trie_op_ptr);
+for (k=1; k<=trie_op_ptr; k++)
+ {@+dump_int(hyf_distance[k]);
+ dump_int(hyf_num[k]);
+ dump_int(hyf_next[k]);
+ }
+print_nl("Hyphenation trie of length ");print_int(trie_max);
+@.Hyphenation trie...@>
+print(" has ");print_int(trie_op_ptr);print(" op");
+if (trie_op_ptr!=1) print_char('s');
+print(" out of ");print_int(trie_op_size);
+for (k=255; k>=0; k--) if (trie_used[k] > min_quarterword)
+ {@+print_nl(" ");print_int(qo(trie_used[k]));
+ print(" for language ");print_int(k);
+ dump_int(k);dump_int(qo(trie_used[k]));
+ }
+
+@ Only ``nonempty'' parts of |op_start| need to be restored.
+
+@<Undump the hyphenation tables@>=
+undump(0, hyph_size, hyph_count);
+for (k=1; k<=hyph_count; k++)
+ {@+undump(0, hyph_size, j);
+ undump(0, str_ptr, hyph_word[j]);
+ undump(min_halfword, max_halfword, hyph_list[j]);
+ }
+undump_size(0, trie_size,"trie size", j);
+#ifdef @!INIT
+trie_max=j;
+#endif
+undump(0, j, hyph_start);
+for (k=0; k<=j; k++) undump_hh(trie[k]);
+undump_size(0, trie_op_size,"trie op size", j);
+#ifdef @!INIT
+trie_op_ptr=j;
+#endif
+for (k=1; k<=j; k++)
+ {@+undump(0, 63, hyf_distance[k]); /*a |small_number|*/
+ undump(0, 63, hyf_num[k]);
+ undump(min_quarterword, max_quarterword, hyf_next[k]);
+ }
+#ifdef @!INIT
+for (k=0; k<=255; k++) trie_used[k]=min_quarterword;
+#endif
+@;@/
+k=256;
+while (j > 0)
+ {@+undump(0, k-1, k);undump(1, j, x);
+#ifdef @!INIT
+trie_used[k]=qi(x);
+#endif
+@;@/
+ j=j-x;op_start[k]=qo(j);
+ }
+#ifdef @!INIT
+trie_not_ready=false
+#endif
+
+@ We have already printed a lot of statistics, so we set |tracing_stats=0|
+to prevent them from appearing again.
+
+@<Dump a couple more things and the closing check word@>=
+dump_int(interaction);dump_int(format_ident);dump_int(69069);
+tracing_stats=0
+
+@ @<Undump a couple more things and the closing check word@>=
+undump(batch_mode, error_stop_mode, interaction);
+if (interaction_option>=0) interaction=interaction_option; /* \TeX\ Live */
+undump(0, str_ptr, format_ident);
+undump_int(x);
+if ((x!=69069)||eof(fmt_file)) goto bad_fmt
+
+@ @<Create the |format_ident|...@>=
+selector=new_string;
+print(" (preloaded format=");printn(job_name);print_char(' ');
+print_int(year);print_char('.');
+print_int(month);print_char('.');print_int(day);print_char(')');
+if (interaction==batch_mode) selector=log_only;
+else selector=term_and_log;
+str_room(1);
+format_ident=make_string();
+pack_job_name(format_extension);
+while (!w_open_out(&fmt_file))
+ prompt_file_name("format file name", format_extension);
+print_nl("Beginning to dump on file ");
+@.Beginning to dump...@>
+slow_print(w_make_name_string(&fmt_file));flush_string;
+print_nl("");slow_print(format_ident)
+
+@ @<Close the format file@>=
+w_close(&fmt_file)
+
+@* The main program.
+This is it: the part of \TeX\ that executes all those procedures we have
+written.
+
+Well---almost. Let's leave space for a few more routines that we may
+have forgotten.
+
+@p @<Last-minute procedures@>@;
+
+@ We have noted that there are two versions of \TeX82. One, called \.{INITEX},
+@.INITEX@>
+has to be run first; it initializes everything from scratch, without
+reading a format file, and it has the capability of dumping a format file.
+The other one is called `\.{VIRTEX}'; it is a ``virgin'' program that needs
+@.VIRTEX@>
+to input a format file in order to get started. \.{VIRTEX} typically has
+more memory capacity than \.{INITEX}, because it does not need the space
+consumed by the auxiliary hyphenation tables and the numerous calls on
+|primitive|, etc.
+
+The \.{VIRTEX} program cannot read a format file instantaneously, of course;
+the best implementations therefore allow for production versions of \TeX\ that
+not only avoid the loading routine for \PASCAL\ object code, they also have
+a format file pre-loaded. This is impossible to do if we stick to standard
+\PASCAL; but there is a simple way to fool many systems into avoiding the
+initialization, as follows:\quad(1)~We declare a global integer variable
+called |ready_already|. The probability is negligible that this
+variable holds any particular value like 314159 when \.{VIRTEX} is first
+loaded.\quad(2)~After we have read in a format file and initialized
+everything, we set |ready_already=314159|.\quad(3)~Soon \.{VIRTEX}
+will print `\.*', waiting for more input; and at this point we
+interrupt the program and save its core image in some form that the
+operating system can reload speedily.\quad(4)~When that core image is
+activated, the program starts again at the beginning; but now
+|ready_already==314159| and all the other global variables have
+their initial values too. The former chastity has vanished!
+
+In other words, if we allow ourselves to test the condition
+|ready_already==314159|, before |ready_already| has been
+assigned a value, we can avoid the lengthy initialization. Dirty tricks
+rarely pay off so handsomely.
+@^dirty \PASCAL@>
+@^system dependencies@>
+
+On systems that allow such preloading, the standard program called \.{TeX}
+should be the one that has \.{plain} format preloaded, since that agrees
+with {\sl The \TeX book}. Other versions, e.g., \.{AmSTeX}, should also
+@:TeXbook}{\sl The \TeX book@>
+@.AmSTeX@>
+@.plain@>
+be provided for commonly used formats.
+
+@<Glob...@>=
+static int @!ready_already; /*a sacrifice of purity for economy*/
+
+@ Now this is really it: \TeX\ starts and ends here.
+
+The initial test involving |ready_already| should be deleted if the
+\PASCAL\ runtime system is smart enough to detect such a ``mistake.''
+@^system dependencies@>
+
+@p
+int main(int argc, char *argv[]) {@! /*|start_here|*/
+ main_init(argc, argv); /* \TeX\ Live */
+history=fatal_error_stop; /*in case we quit during initialization*/
+t_open_out; /*open the terminal for output*/
+if (ready_already==314159) goto start_of_TEX;
+@<Check the ``constant'' values...@>@;
+if (bad > 0)
+ {@+wterm_ln("Ouch---my internal constants have been clobbered!"
+ "---case %d", bad);
+@.Ouch...clobbered@>
+ exit(0);
+ }
+get_strings_started();
+initialize(); /*set global variables to their starting values*/
+#ifdef @!INIT
+if (iniversion) /* \TeX\ Live */
+{ init_prim(); /*call |primitive| for each primitive*/
+ init_str_ptr=str_ptr;init_pool_ptr=pool_ptr;fix_date_and_time(); }
+#endif
+ready_already=314159;
+start_of_TEX: @<Initialize the output routines@>;
+@<Get the first line of input and prepare to start@>;
+history=spotless; /*ready to go!*/
+main_control(); /*come to life*/
+final_cleanup(); /*prepare for death*/
+close_files_and_terminate();
+ready_already=0;
+return 0; }
+
+@ Here we do whatever is needed to complete \TeX's job gracefully on the
+local operating system. The code here might come into play after a fatal
+error; it must therefore consist entirely of ``safe'' operations that
+cannot produce error messages. For example, it would be a mistake to call
+|str_room| or |make_string| at this time, because a call on |overflow|
+might lead to an infinite loop.
+@^system dependencies@>
+(Actually there's one way to get error messages, via |prepare_mag|;
+but that can't cause infinite recursion.)
+@^recursion@>
+
+If |final_cleanup| is bypassed, this program doesn't bother to close
+the input files that may still be open.
+
+@<Last-minute...@>=
+static void close_files_and_terminate(void)
+{@+int k; /*all-purpose index*/
+@<Finish the extensions@>;new_line_char=-1;
+#ifdef @!STAT
+if (tracing_stats > 0) @<Output statistics about this job@>;@;
+#endif
+wake_up_terminal;@<Finish the \.{DVI} file@>;
+if (log_opened)
+ {@+wlog_cr;a_close(&log_file);selector=selector-2;
+ if (selector==term_only)
+ {@+print_nl("Transcript written on ");
+@.Transcript written...@>
+ slow_print(log_name);print_char('.');print_nl("");
+ }
+ }
+}
+
+@ The present section goes directly to the log file instead of using
+|print| commands, because there's no need for these strings to take
+up |str_pool| memory when a non-{\bf stat} version of \TeX\ is being used.
+
+@<Output statistics...@>=
+if (log_opened)
+ {@+wlog_ln(" ");
+ wlog_ln("Here is how much of TeX's memory you used:");
+@.Here is how much...@>
+ wlog(" %d string", str_ptr-init_str_ptr);
+ if (str_ptr!=init_str_ptr+1) wlog( "s" );
+ wlog_ln( " out of %d", max_strings-init_str_ptr);@/
+ wlog_ln( " %d string characters out of %d", pool_ptr-init_pool_ptr,
+ pool_size-init_pool_ptr);@/
+ wlog_ln(" %d words of memory out of %d", lo_mem_max-mem_min+mem_end-hi_mem_min+2,@|
+ mem_end+1-mem_min);@/
+ wlog_ln(" %d multiletter control sequences out of %d", cs_count, hash_size);@/
+ wlog(" %d words of font info for %d font", fmem_ptr, font_ptr-font_base);
+ if (font_ptr!=font_base+1) wlog("s");
+ wlog_ln( ", out of %d for %d", font_mem_size, font_max-font_base);@/
+ wlog(" %d hyphenation exception", hyph_count);
+ if (hyph_count!=1) wlog("s");
+ wlog_ln( " out of %d", hyph_size);@/
+ wlog_ln(" %di,%dn,%dp,%db,%ds stack positions out of %di,%dn,%dp,%db,%ds",
+ max_in_stack, max_nest_stack,@|
+ max_param_stack,@|
+ max_buf_stack+1,@|
+ max_save_stack+6,@|
+ stack_size,
+ nest_size,
+ param_size,
+ buf_size,
+ save_size );
+ }
+
+@ We get to the |final_cleanup| routine when \.{\\end} or \.{\\dump} has
+been scanned and |its_all_over|\kern-2pt.
+
+@<Last-minute...@>=
+static void final_cleanup(void)
+{@+
+int c; /*0 for \.{\\end}, 1 for \.{\\dump}*/
+c=cur_chr;if (c!=1) new_line_char=-1;
+if (job_name==0) open_log_file();
+while (input_ptr > 0)
+ if (state==token_list) end_token_list();@+else end_file_reading();
+while (open_parens > 0)
+ {@+print(" )");decr(open_parens);
+ }
+if (cur_level > level_one)
+ {@+print_nl("(");print_esc("end occurred ");
+ print("inside a group at level ");
+@:end\_}{\.{(\\end occurred...)}@>
+ print_int(cur_level-level_one);print_char(')');
+ if (eTeX_ex) show_save_groups();
+ }
+while (cond_ptr!=null)
+ {@+print_nl("(");print_esc("end occurred ");
+ print("when ");print_cmd_chr(if_test, cur_if);
+ if (if_line!=0)
+ {@+print(" on line ");print_int(if_line);
+ }
+ print(" was incomplete)");
+ if_line=if_line_field(cond_ptr);
+ cur_if=subtype(cond_ptr);temp_ptr=cond_ptr;
+ cond_ptr=link(cond_ptr);free_node(temp_ptr, if_node_size);
+ }
+if (history!=spotless)
+ if (((history==warning_issued)||(interaction < error_stop_mode)))
+ if (selector==term_and_log)
+ {@+selector=term_only;
+ print_nl("(see the transcript file for additional information)");
+@.see the transcript file...@>
+ selector=term_and_log;
+ }
+if (c==1)
+ {
+#ifdef @!INIT
+for (c=top_mark_code; c<=split_bot_mark_code; c++)
+ if (cur_mark[c]!=null) delete_token_ref(cur_mark[c]);
+ if (sa_mark!=null)
+ if (do_marks(destroy_marks, 0, sa_mark)) sa_mark=null;
+ for (c=last_box_code; c<=vsplit_code; c++) flush_node_list(disc_ptr[c]);
+ if (last_glue!=max_halfword) delete_glue_ref(last_glue);
+ store_fmt_file();return;
+#endif
+ print_nl("(\\dump is performed only by INITEX)");return;
+@:dump\_}{\.{\\dump...only by INITEX}@>
+ }
+}
+
+@ @<Last-minute...@>=
+#ifdef @!INIT
+static void init_prim(void) /*initialize all the primitives*/
+{@+no_new_control_sequence=false;
+first=0;
+@<Put each...@>;
+no_new_control_sequence=true;
+}
+#endif
+
+@ When we begin the following code, \TeX's tables may still contain garbage;
+the strings might not even be present. Thus we must proceed cautiously to get
+bootstrapped in.
+
+But when we finish this part of the program, \TeX\ is ready to call on the
+|main_control| routine to do its work.
+
+@<Get the first line...@>=
+{@+@<Initialize the input routines@>;
+@<Enable \eTeX\ and furthermore Prote, if requested@>@;@/
+if ((format_ident==0)||(buffer[loc]=='&'))
+ {@+if (format_ident!=0) initialize(); /*erase preloaded format*/
+ if (!open_fmt_file()) exit(0);
+ if (!load_fmt_file())
+ {@+w_close(&fmt_file);exit(0);
+ }
+ w_close(&fmt_file);
+ while ((loc < limit)&&(buffer[loc]==' ')) incr(loc);
+ }
+if (eTeX_ex) wterm_ln("entering extended mode");
+if (Prote_ex) {@+Prote_initialize();
+ }
+if (end_line_char_inactive) decr(limit);
+else buffer[limit]=end_line_char;
+fix_date_and_time();@/
+@<Initialize the print |selector|...@>;
+if ((loc < limit)&&(cat_code(buffer[loc])!=escape)) start_input();
+ /*\.{\\input} assumed*/
+}
+
+@* Debugging.
+Once \TeX\ is working, you should be able to diagnose most errors with
+the \.{\\show} commands and other diagnostic features. But for the initial
+stages of debugging, and for the revelation of really deep mysteries, you
+can compile \TeX\ with a few more aids, including the \PASCAL\ runtime
+checks and its debugger. An additional routine called |debug_help|
+will also come into play when you type `\.D' after an error message;
+|debug_help| also occurs just before a fatal error causes \TeX\ to succumb.
+@^debugging@>
+@^system dependencies@>
+
+The interface to |debug_help| is primitive, but it is good enough when used
+with a \PASCAL\ debugger that allows you to set breakpoints and to read
+variables and change their values. After getting the prompt `\.{debug \#}', you
+type either a negative number (this exits |debug_help|), or zero (this
+goes to a location where you can set a breakpoint, thereby entering into
+dialog with the \PASCAL\ debugger), or a positive number |m| followed by
+an argument |n|. The meaning of |m| and |n| will be clear from the
+program below. (If |m==13|, there is an additional argument, |l|.)
+@.debug \#@>
+
+@<Last-minute...@>=
+#ifdef @!DEBUG
+static void debug_help(void) /*routine to display various things*/
+{@+
+int k, @!l, @!m, @!n;
+clear_terminal;
+ loop{@+wake_up_terminal;
+ print_nl("debug # (-1 to exit):");update_terminal;
+@.debug \#@>
+ if (fscanf(term_in.f," %d",&m)<1 ||
+ m < 0) return;
+ else if (m==0)
+ {@+goto breakpoint;@/ /*go to every declared label at least once*/
+ breakpoint: m=0; /*'BREAKPOINT'*/@/
+ }
+ else{@+fscanf(term_in.f," %d",&n);
+ switch (m) {
+ @t\4@>@<Numbered cases for |debug_help|@>@;
+ default:print("?");
+ }
+ }
+ }
+}
+#endif
+
+@ @<Numbered cases...@>=
+case 1: print_word(mem[n]);@+break; /*display |mem[n]| in all forms*/
+case 2: print_int(info(n));@+break;
+case 3: print_int(link(n));@+break;
+case 4: print_word(eqtb[n]);@+break;
+case 5: print_word(font_info[n]);@+break;
+case 6: print_word(save_stack[n]);@+break;
+case 7: show_box(n);@+break;
+ /*show a box, abbreviated by |show_box_depth| and |show_box_breadth|*/
+case 8: {@+breadth_max=10000;depth_threshold=pool_size-pool_ptr-10;
+ show_node_list(n); /*show a box in its entirety*/
+ } @+break;
+case 9: show_token_list(n, null, 1000);@+break;
+case 10: slow_print(n);@+break;
+case 11: check_mem(n > 0);@+break; /*check wellformedness; print new busy locations if |n > 0|*/
+case 12: search_mem(n);@+break; /*look for pointers to |n|*/
+case 13: {@+fscanf(term_in.f," %d",&l);print_cmd_chr(n, l);
+ } @+break;
+case 14: for (k=0; k<=n; k++) printn(buffer[k]);@+break;
+case 15: {@+font_in_short_display=null_font;short_display(n);
+ } @+break;
+case 16: panicking=!panicking;@+break;
+
+@* Extensions.
+The program above includes a bunch of ``hooks'' that allow further
+capabilities to be added without upsetting \TeX's basic structure.
+Most of these hooks are concerned with ``whatsit'' nodes, which are
+intended to be used for special purposes; whenever a new extension to
+\TeX\ involves a new kind of whatsit node, a corresponding change needs
+to be made to the routines below that deal with such nodes,
+but it will usually be unnecessary to make many changes to the
+other parts of this program.
+
+In order to demonstrate how extensions can be made, we shall treat
+`\.{\\write}', `\.{\\openout}', `\.{\\closeout}', `\.{\\immediate}',
+`\.{\\special}', and `\.{\\setlanguage}' as if they were extensions.
+These commands are actually primitives of \TeX, and they should
+appear in all implementations of the system; but let's try to imagine
+that they aren't. Then the program below illustrates how a person
+could add them.
+
+Sometimes, of course, an extension will require changes to \TeX\ itself;
+no system of hooks could be complete enough for all conceivable extensions.
+The features associated with `\.{\\write}' are almost all confined to the
+following paragraphs, but there are small parts of the |print_ln| and
+|print_char| procedures that were introduced specifically to \.{\\write}
+characters. Furthermore one of the token lists recognized by the scanner
+is a |write_text|; and there are a few other miscellaneous places where we
+have already provided for some aspect of \.{\\write}. The goal of a \TeX\
+extender should be to minimize alterations to the standard parts of the
+program, and to avoid them completely if possible. He or she should also
+be quite sure that there's no easy way to accomplish the desired goals
+with the standard features that \TeX\ already has. ``Think thrice before
+extending,'' because that may save a lot of work, and it will also keep
+incompatible extensions of \TeX\ from proliferating.
+@^system dependencies@>
+@^extensions to \TeX@>
+
+@ First let's consider the format of whatsit nodes that are used to represent
+the data associated with \.{\\write} and its relatives. Recall that a whatsit
+has |type==whatsit_node|, and the |subtype| is supposed to distinguish
+different kinds of whatsits. Each node occupies two or more words; the
+exact number is immaterial, as long as it is readily determined from the
+|subtype| or other data.
+
+We shall introduce five |subtype| values here, corresponding to the
+control sequences \.{\\openout}, \.{\\write}, \.{\\closeout}, \.{\\special}, and
+\.{\\setlanguage}. The second word of I/O whatsits has a |write_stream| field
+that identifies the write-stream number (0 to 15, or 16 for out-of-range and
+positive, or 17 for out-of-range and negative).
+In the case of \.{\\write} and \.{\\special}, there is also a field that
+points to the reference count of a token list that should be sent. In the
+case of \.{\\openout}, we need three words and three auxiliary subfields
+to hold the string numbers for name, area, and extension.
+
+@d write_node_size 2 /*number of words in a write/whatsit node*/
+@d open_node_size 3 /*number of words in an open/whatsit node*/
+@d open_node 0 /*|subtype| in whatsits that represent files to \.{\\openout}*/
+@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 what_lang(A) link(A+1) /*language number, in the range |0 dotdot 255|*/
+@d what_lhm(A) type(A+1) /*minimum left fragment, in the range |1 dotdot 63|*/
+@d what_rhm(A) subtype(A+1) /*minimum right fragment, in the range |1 dotdot 63|*/
+@d write_tokens(A) link(A+1) /*reference count of token list to write*/
+@d write_stream(A) info(A+1) /*stream number (0 to 17)*/
+@d open_name(A) link(A+1) /*string number of file name to open*/
+@d open_area(A) info(A+2) /*string number of file area for |open_name|*/
+@d open_ext(A) link(A+2) /*string number of file extension for |open_name|*/
+
+@ The sixteen possible \.{\\write} streams are represented by the |write_file|
+array. The |j|th file is open if and only if |write_open[j]==true|. The last
+two streams are special; |write_open[16]| represents a stream number
+greater than 15, while |write_open[17]| represents a negative stream number,
+and both of these variables are always |false|.
+
+@<Glob...@>=
+static alpha_file @!write_file[16];
+static bool @!write_open[18];
+
+@ @<Set init...@>=
+for (k=0; k<=17; k++) write_open[k]=false;
+
+@ Extensions might introduce new command codes; but it's best to use
+|extension| with a modifier, whenever possible, so that |main_control|
+stays the same.
+
+@d immediate_code 4 /*command modifier for \.{\\immediate}*/
+@d latex_first_extension_code 5
+@d latespecial_node (latex_first_extension_code+0) /*|subtype| in whatsits that represent \.{\\special} things expanded during output*/
+@d set_language_code (latex_first_extension_code+1) /*command modifier for \.{\\setlanguage}*/
+@d TeX_last_extension_cmd_mod set_language_code
+
+@<Put each...@>=
+primitive("openout", extension, open_node);@/
+@!@:open\_out\_}{\.{\\openout} primitive@>
+primitive("write", extension, write_node);write_loc=cur_val;@/
+@!@:write\_}{\.{\\write} primitive@>
+primitive("closeout", extension, close_node);@/
+@!@:close\_out\_}{\.{\\closeout} primitive@>
+primitive("special", extension, special_node);@/
+@!@:special\_}{\.{\\special} primitive@>
+primitive("immediate", extension, immediate_code);@/
+@!@:immediate\_}{\.{\\immediate} primitive@>
+primitive("setlanguage", extension, set_language_code);@/
+@!@:set\_language\_}{\.{\\setlanguage} primitive@>
+
+@ The variable |write_loc| just introduced is used to provide an
+appropriate error message in case of ``runaway'' write texts.
+
+@<Glob...@>=
+static pointer @!write_loc; /*|eqtb| address of \.{\\write}*/
+
+@ @<Cases of |print_cmd_chr|...@>=
+case extension: switch (chr_code) {
+ case open_node: print_esc("openout");@+break;
+ case write_node: print_esc("write");@+break;
+ case close_node: print_esc("closeout");@+break;
+ case special_node: print_esc("special");@+break;
+ case immediate_code: print_esc("immediate");@+break;
+ case set_language_code: print_esc("setlanguage");@+break;
+ @/@<Cases of |extension| for |print_cmd_chr|@>@/
+ default:print("[unknown extension!]");
+ } @+break;
+
+
+
+@ When an |extension| command occurs in |main_control|, in any mode,
+the |do_extension| routine is called.
+
+@<Cases of |main_control| that are for extensions...@>=
+any_mode(extension): do_extension();
+
+@ @<Declare act...@>=
+@t\4@>@<Declare procedures needed in |do_extension|@>@;
+static void do_extension(void)
+{@+int @!k; /*all-purpose integer*/
+pointer @!p; /*all-purpose pointer*/
+switch (cur_chr) {
+case open_node: @<Implement \.{\\openout}@>@;@+break;
+case write_node: @<Implement \.{\\write}@>@;@+break;
+case close_node: @<Implement \.{\\closeout}@>@;@+break;
+case special_node: @<Implement \.{\\special}@>@;@+break;
+case immediate_code: @<Implement \.{\\immediate}@>@;@+break;
+case set_language_code: @<Implement \.{\\setlanguage}@>@;@+break;
+@/@<Cases for |do_extension|@>@/
+default:confusion("ext1");
+@:this can't happen ext1}{\quad ext1@>
+}
+}
+
+@ Here is a subroutine that creates a whatsit node having a given |subtype|
+and a given number of words. It initializes only the first word of the whatsit,
+and appends it to the current list.
+
+@<Declare procedures needed in |do_extension|@>=
+static void new_whatsit(small_number @!s, small_number @!w)
+{@+pointer p; /*the new node*/
+p=get_node(w);type(p)=whatsit_node;subtype(p)=s;
+link(tail)=p;tail=p;
+}
+
+@ The next subroutine uses |cur_chr| to decide what sort of whatsit is
+involved, and also inserts a |write_stream| number.
+
+@<Declare procedures needed in |do_ext...@>=
+static void new_write_whatsit(small_number @!w)
+{@+new_whatsit(cur_chr, w);
+if (w!=write_node_size) scan_four_bit_int();
+else{@+scan_int();
+ if (cur_val < 0) cur_val=17;
+ else if (cur_val > 15) cur_val=16;
+ }
+write_stream(tail)=cur_val;
+}
+
+@ @<Implement \.{\\openout}@>=
+{@+new_write_whatsit(open_node_size);
+scan_optional_equals();scan_file_name();@/
+open_name(tail)=cur_name;open_area(tail)=cur_area;open_ext(tail)=cur_ext;
+}
+
+@ When `\.{\\write 12\{...\}}' appears, we scan the token list `\.{\{...\}}'
+without expanding its macros; the macros will be expanded later when this
+token list is rescanned.
+
+@<Implement \.{\\write}@>=
+{@+k=cur_cs;new_write_whatsit(write_node_size);@/
+cur_cs=k;p=scan_toks(false, false);write_tokens(tail)=def_ref;
+}
+
+@ @<Implement \.{\\closeout}@>=
+{@+new_write_whatsit(write_node_size);write_tokens(tail)=null;
+}
+
+@ When `\.{\\special\{...\}}' appears, we expand the macros in the token
+list as in \.{\\xdef} and \.{\\mark}. When marked with \.{shipout}, we keep
+tokens unexpanded for now.
+
+@<Implement \.{\\special}@>=
+{@+if (scan_keyword("shipout"))
+{@+new_whatsit(latespecial_node, write_node_size);write_stream(tail)=null;
+p=scan_toks(false, false);write_tokens(tail)=def_ref;
+} else
+{@+new_whatsit(special_node, write_node_size);write_stream(tail)=null;
+p=scan_toks(false, true);write_tokens(tail)=def_ref;
+} }
+
+@ Each new type of node that appears in our data structure must be capable
+of being displayed, copied, destroyed, and so on. The routines that we
+need for write-oriented whatsits are somewhat like those for mark nodes;
+other extensions might, of course, involve more subtlety here.
+
+@<Basic printing...@>=
+static void print_write_whatsit(char *@!s, pointer @!p)
+{@+print_esc(s);
+if (write_stream(p) < 16) print_int(write_stream(p));
+else if (write_stream(p)==16) print_char('*');
+@.*\relax@>
+else print_char('-');
+}
+
+@ @<Display the whatsit...@>=
+switch (subtype(p)) {
+case open_node: {@+print_write_whatsit("openout", p);
+ print_char('=');print_file_name(open_name(p), open_area(p), open_ext(p));
+ } @+break;
+case write_node: {@+print_write_whatsit("write", p);
+ print_mark(write_tokens(p));
+ } @+break;
+case close_node: print_write_whatsit("closeout", p);@+break;
+case latespecial_node: {@+print_esc("special");print(" shipout");
+ print_mark(write_tokens(p));
+ } @+break;
+case special_node: {@+print_esc("special");
+ print_mark(write_tokens(p));
+ } @+break;
+case language_node: {@+print_esc("setlanguage");
+ print_int(what_lang(p));print(" (hyphenmin ");
+ print_int(what_lhm(p));print_char(',');
+ print_int(what_rhm(p));print_char(')');
+ } @+break;
+@/@<Cases for displaying the |whatsit| node@>@/
+default:print("whatsit?");
+}
+
+@ @<Make a partial copy of the whatsit...@>=
+switch (subtype(p)) {
+case open_node: {@+r=get_node(open_node_size);words=open_node_size;
+ } @+break;
+case write_node: case special_node: case latespecial_node: {@+r=get_node(write_node_size);
+ add_token_ref(write_tokens(p));words=write_node_size;
+ } @+break;
+case close_node: case language_node: {@+r=get_node(small_node_size);
+ words=small_node_size;
+ } @+break;
+@/@<Cases for making a partial copy of the whatsit node@>@/
+default:confusion("ext2");
+@:this can't happen ext2}{\quad ext2@>
+}
+
+@ @<Wipe out the whatsit...@>=
+{@+switch (subtype(p)) {
+case open_node: free_node(p, open_node_size);@+break;
+case write_node: case special_node: case latespecial_node: {@+delete_token_ref(write_tokens(p));
+ free_node(p, write_node_size);goto done;
+ }
+case close_node: case language_node: free_node(p, small_node_size);@+break;
+@/@<Cases for wiping out the whatsit node@>@/
+default:confusion("ext3");
+@:this can't happen ext3}{\quad ext3@>
+} @/
+goto done;
+}
+
+@ @<Incorporate a whatsit node into a vbox@>=do_nothing
+
+@ @<Incorporate a whatsit node into an hbox@>=do_nothing
+
+@ @<Let |d| be the width of the whatsit |p|@>=d=0
+
+@ @d adv_past(A) @+if (subtype(A)==language_node)
+ {@+cur_lang=what_lang(A);l_hyf=what_lhm(A);r_hyf=what_rhm(A);
+ set_hyph_index;
+ }
+
+@<Advance \(p)past a whatsit node in the \(l)|line_break| loop@>=@+
+adv_past(cur_p)
+
+@ @<Advance \(p)past a whatsit node in the \(p)pre-hyphenation loop@>=@+
+adv_past(s)
+
+@ @<Prepare to move whatsit |p| to the current page, then |goto contribute|@>=
+goto contribute
+
+@ @<Process whatsit |p| in |vert_break| loop, |goto not_found|@>=
+goto not_found
+
+@ @<Output the whatsit node |p| in a vlist@>=
+out_what(p)
+
+@ @<Output the whatsit node |p| in an hlist@>=
+out_what(p)
+
+@ After all this preliminary shuffling, we come finally to the routines
+that actually send out the requested data. Let's do \.{\\special} first
+(it's easier).
+
+@<Declare procedures needed in |hlist_out|, |vlist_out|@>=
+static void special_out(pointer @!p)
+{@+pointer @!q, @!r; /*temporary variables for list manipulation*/
+int @!old_mode; /*saved |mode|*/
+
+if (subtype(p)==latespecial_node)
+ {@+@<Expand macros in the token list and make |link(def_ref)| point to the
+result@>;
+ write_tokens(p)=def_ref;
+ }
+}
+
+@ To write a token list, we must run it through \TeX's scanner, expanding
+macros and \.{\\the} and \.{\\number}, etc. This might cause runaways,
+if a delimited macro parameter isn't matched, and runaways would be
+extremely confusing since we are calling on \TeX's scanner in the middle
+of a \.{\\shipout} command. Therefore we will put a dummy control sequence as
+a ``stopper,'' right after the token list. This control sequence is
+artificially defined to be \.{\\outer}.
+@:end\_write\_}{\.{\\endwrite}@>
+
+@<Initialize table...@>=
+text(end_write)=s_no("endwrite");eq_level(end_write)=level_one;
+eq_type(end_write)=outer_call;equiv(end_write)=null;
+
+@ @<Declare procedures needed in |hlist_out|, |vlist_out|@>=
+static void write_out(pointer @!p)
+{@+int old_setting; /*holds print |selector|*/
+int @!old_mode; /*saved |mode|*/
+small_number @!j; /*write stream number*/
+pointer @!q, @!r; /*temporary variables for list manipulation*/
+@<Expand macros in the token list and make |link(def_ref)| point to the result@>;
+old_setting=selector;j=write_stream(p);
+if (write_open[j]) selector=j;
+else{@+ /*write to the terminal if file isn't open*/
+ if ((j==17)&&(selector==term_and_log)) selector=log_only;
+ print_nl("");
+ }
+token_show(def_ref);print_ln();
+flush_list(def_ref);selector=old_setting;
+}
+
+@ The final line of this routine is slightly subtle; at least, the author
+didn't think about it until getting burnt! There is a used-up token list
+@^Knuth, Donald Ervin@>
+on the stack, namely the one that contained |end_write_token|. (We
+insert this artificial `\.{\\endwrite}' to prevent runaways, as explained
+above.) If it were not removed, and if there were numerous writes on a
+single page, the stack would overflow.
+
+@d end_write_token cs_token_flag+end_write
+
+@<Expand macros in the token list and...@>=
+q=get_avail();info(q)=right_brace_token+'}';@/
+fl_mem[q]=FILE_LINE(system_file,system_insert);
+r=get_avail();link(q)=r;info(r)=end_write_token;ins_list(q);@/
+fl_mem[r]=FILE_LINE(system_file,system_insert);
+begin_token_list(write_tokens(p), write_text);@/
+q=get_avail();info(q)=left_brace_token+'{';ins_list(q);
+fl_mem[q]=FILE_LINE(system_file,system_insert);
+
+ /*now we're ready to scan
+ `\.\{$\langle\,$token list$\,\rangle$\.{\} \\endwrite}'*/
+old_mode=mode;mode=0;
+ /*disable \.{\\prevdepth}, \.{\\spacefactor}, \.{\\lastskip}, \.{\\prevgraf}*/
+cur_cs=write_loc;q=scan_toks(false, true); /*expand macros, etc.*/
+get_token();@+if (cur_tok!=end_write_token)
+ @<Recover from an unbalanced write command@>;
+mode=old_mode;
+end_token_list() /*conserve stack space*/
+
+@ @<Recover from an unbalanced write command@>=
+{@+print_err("Unbalanced write command");
+@.Unbalanced write...@>
+help2("On this page there's a \\write with fewer real {'s than }'s.",@/
+"I can't handle that very well; good luck.");error();
+@/do@+{get_token();
+}@+ while (!(cur_tok==end_write_token));
+}
+
+@ The |out_what| procedure takes care of outputting whatsit nodes for
+|vlist_out| and |hlist_out|\kern-.3pt.
+
+@<Declare procedures needed in |hlist_out|, |vlist_out|@>=
+@t\4@>@<Declare procedures needed in |out_what|@>@;
+static void out_what(pointer @!p)
+{@+small_number j; /*write stream number*/
+switch (subtype(p)) {
+case open_node: case write_node: case close_node: @<Do some work that has
+been queued up for \.{\\write}@>@;@+break;
+case special_node: special_out(p);@+break;
+case language_node: do_nothing;@+break;
+@/@<Cases for |out_what|@>@/
+default:confusion("ext4");
+@:this can't happen ext4}{\quad ext4@>
+}
+}
+
+@ We don't implement \.{\\write} inside of leaders. (The reason is that
+the number of times a leader box appears might be different in different
+implementations, due to machine-dependent rounding in the glue calculations.)
+@^leaders@>
+
+@<Do some work that has been queued up...@>=
+if (!doing_leaders)
+ {@+j=write_stream(p);
+ if (subtype(p)==write_node) write_out(p);
+ else{@+if (write_open[j]) a_close(&write_file[j]);
+ if (subtype(p)==close_node) write_open[j]=false;
+ else if (j < 16)
+ {@+cur_name=open_name(p);cur_area=open_area(p);
+ cur_ext=open_ext(p);
+ pack_cur_name(".tex");
+ while (!a_open_out(&write_file[j]))
+ prompt_file_name("output file name",".tex");
+ write_open[j]=true;
+ }
+ }
+ }
+
+@ The presence of `\.{\\immediate}' causes the |do_extension| procedure
+to descend to one level of recursion. Nothing happens unless \.{\\immediate}
+is followed by `\.{\\openout}', `\.{\\write}', or `\.{\\closeout}'.
+@^recursion@>
+
+@<Implement \.{\\immediate}@>=
+{@+get_x_token();
+if ((cur_cmd==extension)&&(cur_chr <= close_node))
+ {@+p=tail;do_extension(); /*append a whatsit node*/
+ out_what(tail); /*do the action immediately*/
+ flush_node_list(tail);tail=p;link(p)=null;
+ }
+else back_input();
+}
+
+@ The \.{\\language} extension is somewhat different.
+We need a subroutine that comes into play when a character of
+a non-|clang| language is being appended to the current paragraph.
+
+@<Declare action...@>=
+static void fix_language(void)
+{@+ASCII_code @!l; /*the new current language*/
+if (language <= 0) l=0;
+else if (language > 255) l=0;
+else l=language;
+if (l!=clang)
+ {@+new_whatsit(language_node, small_node_size);
+ what_lang(tail)=l;clang=l;@/
+ what_lhm(tail)=norm_min(left_hyphen_min);
+ what_rhm(tail)=norm_min(right_hyphen_min);
+ }
+}
+
+@ @<Implement \.{\\setlanguage}@>=
+if (abs(mode)!=hmode) report_illegal_case();
+else{@+new_whatsit(language_node, small_node_size);
+ scan_int();
+ if (cur_val <= 0) clang=0;
+ else if (cur_val > 255) clang=0;
+ else clang=cur_val;
+ what_lang(tail)=clang;
+ what_lhm(tail)=norm_min(left_hyphen_min);
+ what_rhm(tail)=norm_min(right_hyphen_min);
+ }
+
+@ @<Finish the extensions@>=
+for (k=0; k<=15; k++) if (write_open[k]) a_close(&write_file[k]);
+
+@* The extended features of \eTeX.
+The program has three modes of operation: (1)~In \TeX\ compatibility mode
+it fully deserves the name \TeX\ and there are neither extended features
+nor additional primitive commands. There are, however, a few
+modifications that would be legitimate in any implementation of \TeX\
+such as, e.g., preventing inadequate results of the glue to \.{DVI}
+unit conversion during |ship_out|. (2)~In extended mode there are
+additional primitive commands and the extended features of \eTeX\ are
+available. (3)~In \Prote\ mode there are supplementary primitive
+commands that will be discussed in the section below.
+
+The distinction between these three modes of operation initially takes
+place when a `virgin' \.{eINITEX} starts without reading a format file.
+Later on the values of all \eTeX\ state variables are inherited when
+\.{eVIRTEX} (or \.{eINITEX}) reads a format file.
+
+The code below is designed to work for cases where `$|@t\#\&{ifdef} \.{INIT}@>|\ldots|@t\#\&{endif}@>|$'
+is a run-time switch.
+
+@<Enable \eTeX\ and furthermore Prote, if requested@>=
+#ifdef @!INIT
+if (iniversion && (buffer[loc]=='*'||etexp)) /* \TeX\ Live */
+ {@+no_new_control_sequence=false;
+ @<Generate all \eTeX\ primitives@>@;
+ if (buffer[loc]=='*') incr(loc); /* \TeX\ Live */
+ eTeX_mode=1; /*enter extended mode*/
+ @<Initialize variables for \eTeX\ extended mode@>@;
+ if (buffer[loc]=='*'||ltxp) {@+
+ @<Check \Prote\ ``constant'' values for consistency@>@;
+ @<Generate all \Prote\ primitives@>@;
+ if (buffer[loc]=='*')incr(loc);
+ Prote_mode=1; /*enter \Prote\ mode*/
+ }
+ }
+#endif
+@;@/
+if (!no_new_control_sequence) /*just entered extended mode ?*/
+ no_new_control_sequence=true;@+else
+
+@ The \eTeX\ features available in extended mode are grouped into two
+categories: (1)~Some of them are permanently enabled and have no
+semantic effect as long as none of the additional primitives are
+executed. (2)~The remaining \eTeX\ features are optional and can be
+individually enabled and disabled. For each optional feature there is
+an \eTeX\ state variable named \.{\\...state}; the feature is enabled,
+resp.\ disabled by assigning a positive, resp.\ non-positive value to
+that integer.
+
+@d eTeX_state_base (int_base+eTeX_state_code)
+@d eTeX_state(A) eqtb[eTeX_state_base+A].i /*an \eTeX\ state variable*/
+@#
+@d eTeX_version_code eTeX_int /*code for \.{\\eTeXversion}*/
+
+@<Generate all \eTeX...@>=
+primitive("lastnodetype", last_item, last_node_type_code);
+@!@:last\_node\_type\_}{\.{\\lastnodetype} primitive@>
+primitive("eTeXversion", last_item, eTeX_version_code);
+@!@:eTeX\_version\_}{\.{\\eTeXversion} primitive@>
+primitive("eTeXrevision", convert, eTeX_revision_code);@/
+@!@:eTeX\_revision\_}{\.{\\eTeXrevision} primitive@>
+
+@ @<Cases of |last_item| for |print_cmd_chr|@>=
+case last_node_type_code: print_esc("lastnodetype");@+break;
+case eTeX_version_code: print_esc("eTeXversion");@+break;
+
+@ @<Cases for fetching an integer value@>=
+case eTeX_version_code: cur_val=eTeX_version;@+break;
+
+@ @d eTeX_ex (eTeX_mode==1) /*is this extended mode?*/
+
+@<Glob...@>=
+static int @!eTeX_mode; /*identifies compatibility and extended mode*/
+
+@ @<Initialize table entries...@>=
+eTeX_mode=0; /*initially we are in compatibility mode*/
+@<Initialize variables for \eTeX\ compatibility mode@>@;
+
+@ @<Dump the \eTeX\ state@>=
+dump_int(eTeX_mode);
+for (j=0; j<=eTeX_states-1; j++) eTeX_state(j)=0; /*disable all enhancements*/
+
+@ @<Undump the \eTeX\ state@>=
+undump(0, 1, eTeX_mode);
+if (eTeX_ex)
+ {@+@<Initialize variables for \eTeX\ extended mode@>;
+ }
+else{@+@<Initialize variables for \eTeX\ compatibility mode@>;
+ }
+
+@ The |eTeX_enabled| function simply returns its first argument as
+result. This argument is |true| if an optional \eTeX\ feature is
+currently enabled; otherwise, if the argument is |false|, the function
+gives an error message.
+
+@<Declare \eTeX\ procedures for use...@>=
+static bool eTeX_enabled(bool @!b, quarterword @!j, halfword @!k)
+{@+if (!b)
+ {@+print_err("Improper ");print_cmd_chr(j, k);
+ help1("Sorry, this optional e-TeX feature has been disabled.");error();
+ }
+return b;
+}
+
+@ First we implement the additional \eTeX\ parameters in the table of
+equivalents.
+
+@<Generate all \eTeX...@>=
+primitive("everyeof", assign_toks, every_eof_loc);
+@!@:every\_eof\_}{\.{\\everyeof} primitive@>
+primitive("tracingassigns", assign_int, int_base+tracing_assigns_code);@/
+@!@:tracing\_assigns\_}{\.{\\tracingassigns} primitive@>
+primitive("tracinggroups", assign_int, int_base+tracing_groups_code);@/
+@!@:tracing\_groups\_}{\.{\\tracinggroups} primitive@>
+primitive("tracingifs", assign_int, int_base+tracing_ifs_code);@/
+@!@:tracing\_ifs\_}{\.{\\tracingifs} primitive@>
+primitive("tracingscantokens", assign_int, int_base+tracing_scan_tokens_code);@/
+@!@:tracing\_scan\_tokens\_}{\.{\\tracingscantokens} primitive@>
+primitive("tracingnesting", assign_int, int_base+tracing_nesting_code);@/
+@!@:tracing\_nesting\_}{\.{\\tracingnesting} primitive@>
+primitive("savingvdiscards", assign_int, int_base+saving_vdiscards_code);@/
+@!@:saving\_vdiscards\_}{\.{\\savingvdiscards} primitive@>
+primitive("savinghyphcodes", assign_int, int_base+saving_hyph_codes_code);@/
+@!@:saving\_hyph\_codes\_}{\.{\\savinghyphcodes} primitive@>
+
+@ @d every_eof equiv(every_eof_loc)
+
+@<Cases of |assign_toks| for |print_cmd_chr|@>=
+case every_eof_loc: print_esc("everyeof");@+break;
+
+@ @<Cases for |print_param|@>=
+case tracing_assigns_code: print_esc("tracingassigns");@+break;
+case tracing_groups_code: print_esc("tracinggroups");@+break;
+case tracing_ifs_code: print_esc("tracingifs");@+break;
+case tracing_scan_tokens_code: print_esc("tracingscantokens");@+break;
+case tracing_nesting_code: print_esc("tracingnesting");@+break;
+case saving_vdiscards_code: print_esc("savingvdiscards");@+break;
+case saving_hyph_codes_code: print_esc("savinghyphcodes");@+break;
+
+@ In order to handle \.{\\everyeof} we need an array |eof_seen| of
+boolean variables.
+
+@<Glob...@>=
+static bool @!eof_seen0[max_in_open],
+ *const @!eof_seen = @!eof_seen0-1; /*has eof been seen?*/
+
+@ The |print_group| procedure prints the current level of grouping and
+the name corresponding to |cur_group|.
+
+@<Declare \eTeX\ procedures for tr...@>=
+static void print_group(bool @!e)
+{@+
+switch (cur_group) {
+ case bottom_level: {@+print("bottom level");return;
+ }
+ case simple_group: case semi_simple_group:
+ {@+if (cur_group==semi_simple_group) print("semi ");
+ print("simple");
+ } @+break;
+ case hbox_group: case adjusted_hbox_group:
+ {@+if (cur_group==adjusted_hbox_group) print("adjusted ");
+ print("hbox");
+ } @+break;
+ case vbox_group: print("vbox");@+break;
+ case vtop_group: print("vtop");@+break;
+ case align_group: case no_align_group:
+ {@+if (cur_group==no_align_group) print("no ");
+ print("align");
+ } @+break;
+ case output_group: print("output");@+break;
+ case disc_group: print("disc");@+break;
+ case insert_group: print("insert");@+break;
+ case vcenter_group: print("vcenter");@+break;
+ case math_group: case math_choice_group:
+ case math_shift_group: case math_left_group:
+ {@+print("math");
+ if (cur_group==math_choice_group) print(" choice");
+ else if (cur_group==math_shift_group) print(" shift");
+ else if (cur_group==math_left_group) print(" left");
+ }
+ } /*there are no other cases*/
+print(" group (level ");print_int(qo(cur_level));print_char(')');
+if (saved(-1)!=0)
+ {@+if (e) print(" entered at line ");else print(" at line ");
+ print_int(saved(-1));
+ }
+}
+
+@ The |group_trace| procedure is called when a new level of grouping
+begins (|e==false|) or ends (|e==true|) with |saved(-1)| containing the
+line number.
+
+@<Declare \eTeX\ procedures for tr...@>=
+#ifdef @!STAT
+static void group_trace(bool @!e)
+{@+begin_diagnostic();print_char('{');
+if (e) print("leaving ");else print("entering ");
+print_group(e);print_char('}');end_diagnostic(false);
+}
+#endif
+
+@ The \.{\\currentgrouplevel} and \.{\\currentgrouptype} commands return
+the current level of grouping and the type of the current group
+respectively.
+
+@d current_group_level_code (eTeX_int+1) /*code for \.{\\currentgrouplevel}*/
+@d current_group_type_code (eTeX_int+2) /*code for \.{\\currentgrouptype}*/
+
+@<Generate all \eTeX...@>=
+primitive("currentgrouplevel", last_item, current_group_level_code);
+@!@:current\_group\_level\_}{\.{\\currentgrouplevel} primitive@>
+primitive("currentgrouptype", last_item, current_group_type_code);
+@!@:current\_group\_type\_}{\.{\\currentgrouptype} primitive@>
+
+@ @<Cases of |last_item| for |print_cmd_chr|@>=
+case current_group_level_code: print_esc("currentgrouplevel");@+break;
+case current_group_type_code: print_esc("currentgrouptype");@+break;
+
+@ @<Cases for fetching an integer value@>=
+case current_group_level_code: cur_val=cur_level-level_one;@+break;
+case current_group_type_code: cur_val=cur_group;@+break;
+
+@ The \.{\\currentiflevel}, \.{\\currentiftype}, and
+\.{\\currentifbranch} commands return the current level of conditionals
+and the type and branch of the current conditional.
+
+@d current_if_level_code (eTeX_int+3) /*code for \.{\\currentiflevel}*/
+@d current_if_type_code (eTeX_int+4) /*code for \.{\\currentiftype}*/
+@d current_if_branch_code (eTeX_int+5) /*code for \.{\\currentifbranch}*/
+
+@<Generate all \eTeX...@>=
+primitive("currentiflevel", last_item, current_if_level_code);
+@!@:current\_if\_level\_}{\.{\\currentiflevel} primitive@>
+primitive("currentiftype", last_item, current_if_type_code);
+@!@:current\_if\_type\_}{\.{\\currentiftype} primitive@>
+primitive("currentifbranch", last_item, current_if_branch_code);
+@!@:current\_if\_branch\_}{\.{\\currentifbranch} primitive@>
+
+@ @<Cases of |last_item| for |print_cmd_chr|@>=
+case current_if_level_code: print_esc("currentiflevel");@+break;
+case current_if_type_code: print_esc("currentiftype");@+break;
+case current_if_branch_code: print_esc("currentifbranch");@+break;
+
+@ @<Cases for fetching an integer value@>=
+case current_if_level_code: {@+q=cond_ptr;cur_val=0;
+ while (q!=null)
+ {@+incr(cur_val);q=link(q);
+ }
+ } @+break;
+case current_if_type_code: if (cond_ptr==null) cur_val=0;
+ else if (cur_if < unless_code) cur_val=cur_if+1;
+ else cur_val=-(cur_if-unless_code+1);@+break;
+case current_if_branch_code:
+ if ((if_limit==or_code)||(if_limit==else_code)) cur_val=1;
+ else if (if_limit==fi_code) cur_val=-1;
+ else cur_val=0;@+break;
+
+@ The \.{\\fontcharwd}, \.{\\fontcharht}, \.{\\fontchardp}, and
+\.{\\fontcharic} commands return information about a character in a
+font.
+
+@d font_char_wd_code eTeX_dim /*code for \.{\\fontcharwd}*/
+@d font_char_ht_code (eTeX_dim+1) /*code for \.{\\fontcharht}*/
+@d font_char_dp_code (eTeX_dim+2) /*code for \.{\\fontchardp}*/
+@d font_char_ic_code (eTeX_dim+3) /*code for \.{\\fontcharic}*/
+
+@<Generate all \eTeX...@>=
+primitive("fontcharwd", last_item, font_char_wd_code);
+@!@:font\_char\_wd\_}{\.{\\fontcharwd} primitive@>
+primitive("fontcharht", last_item, font_char_ht_code);
+@!@:font\_char\_ht\_}{\.{\\fontcharht} primitive@>
+primitive("fontchardp", last_item, font_char_dp_code);
+@!@:font\_char\_dp\_}{\.{\\fontchardp} primitive@>
+primitive("fontcharic", last_item, font_char_ic_code);
+@!@:font\_char\_ic\_}{\.{\\fontcharic} primitive@>
+
+@ @<Cases of |last_item| for |print_cmd_chr|@>=
+case font_char_wd_code: print_esc("fontcharwd");@+break;
+case font_char_ht_code: print_esc("fontcharht");@+break;
+case font_char_dp_code: print_esc("fontchardp");@+break;
+case font_char_ic_code: print_esc("fontcharic");@+break;
+
+@ @<Cases for fetching a dimension value@>=
+case font_char_wd_code:
+case font_char_ht_code:
+case font_char_dp_code:
+case font_char_ic_code: {@+scan_font_ident();q=cur_val;scan_char_num();
+ if ((font_bc[q] <= cur_val)&&(font_ec[q] >= cur_val))
+ {@+i=char_info(q, qi(cur_val));
+ switch (m) {
+ case font_char_wd_code: cur_val=char_width(q, i);@+break;
+ case font_char_ht_code: cur_val=char_height(q, height_depth(i));@+break;
+ case font_char_dp_code: cur_val=char_depth(q, height_depth(i));@+break;
+ case font_char_ic_code: cur_val=char_italic(q, i);
+ } /*there are no other cases*/
+ }
+ else cur_val=0;
+ } @+break;
+
+@ The \.{\\parshapedimen}, \.{\\parshapeindent}, and \.{\\parshapelength}
+commands return the indent and length parameters of the current
+\.{\\parshape} specification.
+
+@d par_shape_length_code (eTeX_dim+4) /*code for \.{\\parshapelength}*/
+@d par_shape_indent_code (eTeX_dim+5) /*code for \.{\\parshapeindent}*/
+@d par_shape_dimen_code (eTeX_dim+6) /*code for \.{\\parshapedimen}*/
+
+@<Generate all \eTeX...@>=
+primitive("parshapelength", last_item, par_shape_length_code);
+@!@:par\_shape\_length\_}{\.{\\parshapelength} primitive@>
+primitive("parshapeindent", last_item, par_shape_indent_code);
+@!@:par\_shape\_indent\_}{\.{\\parshapeindent} primitive@>
+primitive("parshapedimen", last_item, par_shape_dimen_code);
+@!@:par\_shape\_dimen\_}{\.{\\parshapedimen} primitive@>
+
+@ @<Cases of |last_item| for |print_cmd_chr|@>=
+case par_shape_length_code: print_esc("parshapelength");@+break;
+case par_shape_indent_code: print_esc("parshapeindent");@+break;
+case par_shape_dimen_code: print_esc("parshapedimen");@+break;
+
+@ @<Cases for fetching a dimension value@>=
+case par_shape_length_code:
+case par_shape_indent_code:
+case par_shape_dimen_code: {@+q=cur_chr-par_shape_length_code;scan_int();
+ if ((par_shape_ptr==null)||(cur_val <= 0)) cur_val=0;
+ else{@+if (q==2)
+ {@+q=cur_val%2;cur_val=(cur_val+q)/2;
+ }
+ if (cur_val > info(par_shape_ptr)) cur_val=info(par_shape_ptr);
+ cur_val=mem[par_shape_ptr+2*cur_val-q].sc;
+ }
+ cur_val_level=dimen_val;
+ } @+break;
+
+@ The \.{\\showgroups} command displays all currently active grouping
+levels.
+
+@d show_groups 4 /* \.{\\showgroups} */
+
+@<Generate all \eTeX...@>=
+primitive("showgroups", xray, show_groups);
+@!@:show\_groups\_}{\.{\\showgroups} primitive@>
+
+@ @<Cases of |xray| for |print_cmd_chr|@>=
+case show_groups: print_esc("showgroups");@+break;
+
+@ @<Cases for |show_whatever|@>=
+case show_groups: {@+begin_diagnostic();show_save_groups();
+ } @+break;
+
+@ @<Types...@>=
+typedef int32_t save_pointer; /*index into |save_stack|*/
+
+@ The modifications of \TeX\ required for the display produced by the
+|show_save_groups| procedure were first discussed by Donald~E. Knuth in
+{\sl TUGboat\/} {\bf 11}, 165--170 and 499--511, 1990.
+@^Knuth, Donald Ervin@>
+
+In order to understand a group type we also have to know its mode.
+Since unrestricted horizontal modes are not associated with grouping,
+they are skipped when traversing the semantic nest.
+
+@<Declare \eTeX\ procedures for use...@>=
+static void show_save_groups(void)
+{@+
+int p; /*index into |nest|*/
+int @!m; /*mode*/
+save_pointer @!v; /*saved value of |save_ptr|*/
+quarterword @!l; /*saved value of |cur_level|*/
+group_code @!c; /*saved value of |cur_group|*/
+int @!a; /*to keep track of alignments*/
+int @!i;
+quarterword @!j;
+char *@!s;
+p=nest_ptr;nest[p]=cur_list; /*put the top level into the array*/
+v=save_ptr;l=cur_level;c=cur_group;
+save_ptr=cur_boundary;decr(cur_level);@/
+a=1;
+print_nl("");print_ln();
+loop@+{@+print_nl("### ");print_group(true);
+ if (cur_group==bottom_level) goto done;
+ @/do@+{m=nest[p].mode_field;
+ if (p > 0) decr(p);else m=vmode;
+ }@+ while (!(m!=hmode));
+ print(" (");
+ switch (cur_group) {
+ case simple_group: {@+incr(p);goto found2;
+ }
+ case hbox_group: case adjusted_hbox_group: s="hbox";@+break;
+ case vbox_group: s="vbox";@+break;
+ case vtop_group: s="vtop";@+break;
+ case align_group: if (a==0)
+ {@+if (m==-vmode) s="halign";else s="valign";
+ a=1;goto found1;
+ }
+ else{@+if (a==1) print("align entry");else print_esc("cr");
+ if (p >= a) p=p-a;
+ a=0;goto found;
+ } @+break;
+ case no_align_group:
+ {@+incr(p);a=-1;print_esc("noalign");goto found2;
+ }
+ case output_group:
+ {@+print_esc("output");goto found;
+ }
+ case math_group: goto found2;
+ case disc_group: case math_choice_group:
+ {@+if (cur_group==disc_group) print_esc("discretionary");
+ else print_esc("mathchoice");
+ for (i=1; i<=3; i++) if (i <= saved(-2)) print("{}");
+ goto found2;
+ }
+ case insert_group:
+ {@+if (saved(-2)==255) print_esc("vadjust");
+ else{@+print_esc("insert");print_int(saved(-2));
+ }
+ goto found2;
+ }
+ case vcenter_group: {@+s="vcenter";goto found1;
+ }
+ case semi_simple_group: {@+incr(p);print_esc("begingroup");goto found;
+ }
+ case math_shift_group:
+ {@+if (m==mmode) print_char('$');
+ else if (nest[p].mode_field==mmode)
+ {@+print_cmd_chr(eq_no, saved(-2));goto found;
+ }
+ print_char('$');goto found;
+ }
+ case math_left_group:
+ {@+if (type(nest[p+1].eTeX_aux_field)==left_noad) print_esc("left");
+ else print_esc("middle");
+ goto found;
+ }
+ } /*there are no other cases*/
+ @<Show the box context@>;
+ found1: print_esc(s);@<Show the box packaging info@>;
+ found2: print_char('{');
+ found: print_char(')');decr(cur_level);
+ cur_group=save_level(save_ptr);save_ptr=save_index(save_ptr);
+ }
+done: save_ptr=v;cur_level=l;cur_group=c;
+}
+
+@ @<Show the box packaging info@>=
+if (saved(-2)!=0)
+ {@+print_char(' ');
+ if (saved(-3)==exactly) print("to");else print("spread");
+ print_scaled(saved(-2));print("pt");
+ }
+
+@ @<Show the box context@>=
+i=saved(-4);
+if (i!=0)
+ if (i < box_flag)
+ {@+if (abs(nest[p].mode_field)==vmode) j=hmove;else j=vmove;
+ if (i > 0) print_cmd_chr(j, 0);else print_cmd_chr(j, 1);
+ print_scaled(abs(i));print("pt");
+ }
+ else if (i < ship_out_flag)
+ {@+if (i >= global_box_flag)
+ {@+print_esc("global");i=i-(global_box_flag-box_flag);
+ }
+ print_esc("setbox");print_int(i-box_flag);print_char('=');
+ }
+ else print_cmd_chr(leader_ship, i-(leader_flag-a_leaders))
+
+@ The |scan_general_text| procedure is much like |scan_toks(false, false)|,
+but will be invoked via |expand|, i.e., recursively.
+@^recursion@>
+
+@<Declare \eTeX\ procedures for sc...@>=
+static void scan_general_text(void);
+
+@ The token list (balanced text) created by |scan_general_text| begins
+at |link(temp_head)| and ends at |cur_val|. (If |cur_val==temp_head|,
+the list is empty.)
+
+@<Declare \eTeX\ procedures for tok...@>=
+static void scan_general_text(void)
+{@+
+int s; /*to save |scanner_status|*/
+pointer @!w; /*to save |warning_index|*/
+pointer @!d; /*to save |def_ref|*/
+pointer @!p; /*tail of the token list being built*/
+pointer @!q; /*new node being added to the token list via |store_new_token|*/
+halfword @!unbalance; /*number of unmatched left braces*/
+s=scanner_status;w=warning_index;d=def_ref;
+scanner_status=absorbing;warning_index=cur_cs;
+def_ref=get_avail();token_ref_count(def_ref)=null;p=def_ref;
+scan_left_brace(); /*remove the compulsory left brace*/
+unbalance=1;
+loop@+{@+get_token();
+ if (cur_tok < right_brace_limit)
+ if (cur_cmd < right_brace) incr(unbalance);
+ else{@+decr(unbalance);
+ if (unbalance==0) goto found;
+ }
+ store_new_token(cur_tok);
+ }
+found: q=link(def_ref);free_avail(def_ref); /*discard reference count*/
+if (q==null) cur_val=temp_head;@+else cur_val=p;
+link(temp_head)=q;
+scanner_status=s;warning_index=w;def_ref=d;
+}
+
+@ The \.{\\showtokens} command displays a token list.
+
+@d show_tokens 5 /* \.{\\showtokens} , must be odd! */
+
+@<Generate all \eTeX...@>=
+primitive("showtokens", xray, show_tokens);
+@!@:show\_tokens\_}{\.{\\showtokens} primitive@>
+
+@ @<Cases of |xray| for |print_cmd_chr|@>=
+case show_tokens: print_esc("showtokens");@+break;
+
+@ The \.{\\unexpanded} primitive prevents expansion of tokens much as
+the result from \.{\\the} applied to a token variable. The
+\.{\\detokenize} primitive converts a token list into a list of
+character tokens much as if the token list were written to a file. We
+use the fact that the command modifiers for \.{\\unexpanded} and
+\.{\\detokenize} are odd whereas those for \.{\\the} and \.{\\showthe}
+are even.
+
+@<Generate all \eTeX...@>=
+primitive("unexpanded", the, 1);@/
+@!@:unexpanded\_}{\.{\\unexpanded} primitive@>
+primitive("detokenize", the, show_tokens);@/
+@!@:detokenize\_}{\.{\\detokenize} primitive@>
+
+@ @<Cases of |the| for |print_cmd_chr|@>=;
+else if (chr_code==1) print_esc("unexpanded");
+else print_esc("detokenize")
+
+@ @<Handle \.{\\unexpanded} or \.{\\detokenize} and |return|@>=
+if (odd(cur_chr))
+ {@+c=cur_chr;scan_general_text();
+ if (c==1) return cur_val;
+ else{@+old_setting=selector;selector=new_string;b=pool_ptr;
+ p=get_avail();link(p)=link(temp_head);
+ token_show(p);flush_list(p);
+ selector=old_setting;return str_toks(b);
+ }
+
+ }
+
+@ The \.{\\showifs} command displays all currently active conditionals.
+
+@d show_ifs 6 /* \.{\\showifs} */
+
+@<Generate all \eTeX...@>=
+primitive("showifs", xray, show_ifs);
+@!@:show\_ifs\_}{\.{\\showifs} primitive@>
+
+@ @<Cases of |xray| for |print_cmd_chr|@>=
+case show_ifs: print_esc("showifs");@+break;
+
+@ @d print_if_line(A) if (A!=0)
+ {@+print(" entered on line ");print_int(A);
+ }
+
+@<Cases for |show_whatever|@>=
+case show_ifs: {@+begin_diagnostic();print_nl("");print_ln();
+ if (cond_ptr==null)
+ {@+print_nl("### ");print("no active conditionals");
+ }
+ else{@+p=cond_ptr;n=0;
+ @/do@+{incr(n);p=link(p);@+}@+ while (!(p==null));
+ p=cond_ptr;t=cur_if;l=if_line;m=if_limit;
+ @/do@+{print_nl("### level ");print_int(n);print(": ");
+ print_cmd_chr(if_test, t);
+ if (m==fi_code) print_esc("else");
+ print_if_line(l);
+ decr(n);t=subtype(p);l=if_line_field(p);m=type(p);p=link(p);
+ }@+ while (!(p==null));
+ }
+ } @+break;
+
+@ The \.{\\interactionmode} primitive allows to query and set the
+interaction mode.
+
+@<Generate all \eTeX...@>=
+primitive("interactionmode", set_page_int, 2);
+@!@:interaction\_mode\_}{\.{\\interactionmode} primitive@>
+
+@ @<Cases of |set_page_int| for |print_cmd_chr|@>=;
+else if (chr_code==2) print_esc("interactionmode")
+
+@ @<Cases for `Fetch the |dead_cycles| or the |insert_penalties|'@>=;
+else if (m==2) cur_val=interaction
+
+@ @<Declare \eTeX\ procedures for use...@>=
+static void new_interaction(void);
+
+@ @<Cases for |alter_integer|@>=;
+else if (c==2)
+ {@+if ((cur_val < batch_mode)||(cur_val > error_stop_mode))
+ {@+print_err("Bad interaction mode");
+@.Bad interaction mode@>
+ help2("Modes are 0=batch, 1=nonstop, 2=scroll, and",@/
+ "3=errorstop. Proceed, and I'll ignore this case.");
+ int_error(cur_val);
+ }
+ else{@+cur_chr=cur_val;new_interaction();
+ }
+ }
+
+@ The |middle| feature of \eTeX\ allows one ore several \.{\\middle}
+delimiters to appear between \.{\\left} and \.{\\right}.
+
+@<Generate all \eTeX...@>=
+primitive("middle", left_right, middle_noad);
+@!@:middle\_}{\.{\\middle} primitive@>
+
+@ @<Cases of |left_right| for |print_cmd_chr|@>=;
+else if (chr_code==middle_noad) print_esc("middle")
+
+@ The |scan_tokens| feature of \eTeX\ defines the \.{\\scantokens}
+primitive.
+
+@<Generate all \eTeX...@>=
+primitive("scantokens", input, 2);
+@!@:scan\_tokens\_}{\.{\\scantokens} primitive@>
+
+@ @<Cases of |input| for |print_cmd_chr|@>=;
+else if (chr_code==2) print_esc("scantokens")
+
+@ @<Cases for |input|@>=;
+else if (cur_chr==2) pseudo_start()
+
+@ The global variable |pseudo_files| is used to maintain a stack of
+pseudo files. The |info| field of each pseudo file points to a linked
+list of variable size nodes representing lines not yet processed: the
+|info| field of the first word contains the size of this node, all the
+following words contain ASCII codes.
+
+@<Glob...@>=
+static pointer @!pseudo_files; /*stack of pseudo files*/
+
+@ @<Set init...@>=
+pseudo_files=null;
+
+@ The |pseudo_start| procedure initiates reading from a pseudo file.
+
+@<Declare \eTeX\ procedures for ex...@>=
+static void pseudo_start(void);
+
+@ @<Declare \eTeX\ procedures for tok...@>=
+static void pseudo_start(void)
+{@+int old_setting; /*holds |selector| setting*/
+str_number @!s; /*string to be converted into a pseudo file*/
+pool_pointer @!l, @!m; /*indices into |str_pool|*/
+pointer @!p, @!q, @!r; /*for list construction*/
+four_quarters @!w; /*four ASCII codes*/
+int @!nl, @!sz;
+scan_general_text();
+old_setting=selector;selector=new_string;
+token_show(temp_head);selector=old_setting;
+flush_list(link(temp_head));
+str_room(1);s=make_string();
+@<Convert string |s| into a new pseudo file@>;
+flush_string;
+@<Initiate input from new pseudo file@>;
+}
+
+@ @<Convert string |s| into a new pseudo file@>=
+str_pool[pool_ptr]=si(' ');l=str_start[s];
+nl=si(new_line_char);
+p=get_avail();q=p;
+while (l < pool_ptr)
+ {@+m=l;
+ while ((l < pool_ptr)&&(str_pool[l]!=nl)) incr(l);
+ sz=(l-m+7)/4;
+ if (sz==1) sz=2;
+ r=get_node(sz);link(q)=r;q=r;info(q)=hi(sz);
+ while (sz > 2)
+ {@+decr(sz);incr(r);
+ w.b0=qi(so(str_pool[m]));w.b1=qi(so(str_pool[m+1]));
+ w.b2=qi(so(str_pool[m+2]));w.b3=qi(so(str_pool[m+3]));
+ mem[r].qqqq=w;m=m+4;
+ }
+ w.b0=qi(' ');w.b1=qi(' ');w.b2=qi(' ');w.b3=qi(' ');
+ if (l > m)
+ {@+w.b0=qi(so(str_pool[m]));
+ if (l > m+1)
+ {@+w.b1=qi(so(str_pool[m+1]));
+ if (l > m+2)
+ {@+w.b2=qi(so(str_pool[m+2]));
+ if (l > m+3) w.b3=qi(so(str_pool[m+3]));
+ }
+ }
+ }
+ mem[r+1].qqqq=w;
+ if (str_pool[l]==nl) incr(l);
+ }
+info(p)=link(p);link(p)=pseudo_files;pseudo_files=p
+
+@ @<Initiate input from new pseudo file@>=
+begin_file_reading(); /*set up |cur_file| and new level of input*/
+line=0;limit=start;loc=limit+1; /*force line read*/
+if (tracing_scan_tokens > 0)
+ {@+if (term_offset > max_print_line-3) print_ln();
+ else if ((term_offset > 0)||(file_offset > 0)) print_char(' ');
+ name=19;print("( ");incr(open_parens);update_terminal;
+ }
+else name=18
+
+@ Here we read a line from the current pseudo file into |buffer|.
+
+@<Declare \eTeX\ procedures for tr...@>=
+static bool pseudo_input(void) /*inputs the next line or returns |false|*/
+{@+pointer p; /*current line from pseudo file*/
+int @!sz; /*size of node |p|*/
+four_quarters @!w; /*four ASCII codes*/
+int @!r; /*loop index*/
+last=first; /*cf.\ Matthew 19\thinspace:\thinspace30*/
+p=info(pseudo_files);
+if (p==null) return false;
+else{@+info(pseudo_files)=link(p);sz=ho(info(p));
+ if (4*sz-3 >= buf_size-last)
+ @<Report overflow of the input buffer, and abort@>;
+ last=first;
+ for (r=p+1; r<=p+sz-1; r++)
+ {@+w=mem[r].qqqq;
+ buffer[last]=w.b0;buffer[last+1]=w.b1;
+ buffer[last+2]=w.b2;buffer[last+3]=w.b3;
+ last=last+4;
+ }
+ if (last >= max_buf_stack) max_buf_stack=last+1;
+ while ((last > first)&&(buffer[last-1]==' ')) decr(last);
+ free_node(p, sz);
+ return true;
+ }
+}
+
+@ When we are done with a pseudo file we `close' it.
+
+@<Declare \eTeX\ procedures for tr...@>=
+static void pseudo_close(void) /*close the top level pseudo file*/
+{@+pointer p, @!q;
+p=link(pseudo_files);q=info(pseudo_files);
+free_avail(pseudo_files);pseudo_files=p;
+while (q!=null)
+ {@+p=q;q=link(p);free_node(p, ho(info(p)));
+ }
+}
+
+@ @<Dump the \eTeX\ state@>=
+while (pseudo_files!=null) pseudo_close(); /*flush pseudo files*/
+
+@ @<Generate all \eTeX...@>=
+primitive("readline", read_to_cs, 1);@/
+@!@:read\_line\_}{\.{\\readline} primitive@>
+
+@ @<Cases of |read| for |print_cmd_chr|@>=;
+else print_esc("readline")
+
+@ @<Handle \.{\\readline} and |goto done|@>=
+if (j==1)
+ {@+while (loc <= limit) /*current line not yet finished*/
+ {@+cur_chr=buffer[loc];incr(loc);
+ if (cur_chr==' ') cur_tok=space_token;
+ @+else cur_tok=cur_chr+other_token;
+ store_new_token(cur_tok);
+ }
+ goto done;
+ }
+
+@ Here we define the additional conditionals of \eTeX\ as well as the
+\.{\\unless} prefix.
+
+@d if_def_code 17 /* `\.{\\ifdefined}' */
+@d if_cs_code 18 /* `\.{\\ifcsname}' */
+@d if_font_char_code 19 /* `\.{\\iffontchar}' */
+@d eTeX_last_if_test_cmd_mod if_font_char_code
+@d eTeX_last_expand_after_cmd_mod 1
+
+@<Generate all \eTeX...@>=
+primitive("unless", expand_after, 1);@/
+@!@:unless\_}{\.{\\unless} primitive@>
+primitive("ifdefined", if_test, if_def_code);
+@!@:if\_defined\_}{\.{\\ifdefined} primitive@>
+primitive("ifcsname", if_test, if_cs_code);
+@!@:if\_cs\_name\_}{\.{\\ifcsname} primitive@>
+primitive("iffontchar", if_test, if_font_char_code);
+@!@:if\_font\_char\_}{\.{\\iffontchar} primitive@>
+
+@ @<Cases of |expandafter| for |print_cmd_chr|@>=
+case 1: print_esc("unless");@+break;
+
+@ @<Cases of |if_test| for |print_cmd_chr|@>=
+case if_def_code: print_esc("ifdefined");@+break;
+case if_cs_code: print_esc("ifcsname");@+break;
+case if_font_char_code: print_esc("iffontchar");@+break;
+
+@ The result of a boolean condition is reversed when the conditional is
+preceded by \.{\\unless}.
+
+@<Negate a boolean conditional and |goto reswitch|@>=
+{@+get_token();
+if ((cur_cmd==if_test)&&(cur_chr!=if_case_code))
+ {@+cur_chr=cur_chr+unless_code;goto reswitch;
+ }
+print_err("You can't use `");print_esc("unless");print("' before `");
+@.You can't use \\unless...@>
+print_cmd_chr(cur_cmd, cur_chr);print_char('\'');
+help1("Continue, and I'll forget that it ever happened.");
+back_error();
+}
+
+@ The conditional \.{\\ifdefined} tests if a control sequence is
+defined.
+
+We need to reset |scanner_status|, since \.{\\outer} control sequences
+are allowed, but we might be scanning a macro definition or preamble.
+
+@<Cases for |conditional|@>=
+case if_def_code: {@+save_scanner_status=scanner_status;
+ scanner_status=normal;
+ get_next();b=(cur_cmd!=undefined_cs);
+ scanner_status=save_scanner_status;
+ } @+break;
+
+@ The conditional \.{\\ifcsname} is equivalent to \.{\{\\expandafter}
+\.{\}\\expandafter} \.{\\ifdefined} \.{\\csname}, except that no new
+control sequence will be entered into the hash table (once all tokens
+preceding the mandatory \.{\\endcsname} have been expanded).
+
+@<Cases for |conditional|@>=
+case if_cs_code: {@+n=get_avail();p=n; /*head of the list of characters*/
+ @/do@+{get_x_token();
+ if (cur_cs==0) store_new_token(cur_tok);
+ }@+ while (!(cur_cs!=0));
+ if (cur_cmd!=end_cs_name) @<Complain about missing \.{\\endcsname}@>;
+ @<Look up the characters of list |n| in the hash table, and set |cur_cs|@>;
+ flush_list(n);
+ b=(eq_type(cur_cs)!=undefined_cs);
+ } @+break;
+
+@ @<Look up the characters of list |n| in the hash table...@>=
+m=first;p=link(n);
+while (p!=null)
+ {@+if (m >= max_buf_stack)
+ {@+max_buf_stack=m+1;
+ if (max_buf_stack==buf_size)
+ overflow("buffer size", buf_size);
+@:TeX capacity exceeded buffer size}{\quad buffer size@>
+ }
+ buffer[m]=info(p)%0400;incr(m);p=link(p);
+ }
+if (m==first) cur_cs=null_cs; /*the list is empty*/
+else if (m > first+1)
+ cur_cs=id_lookup(first, m-first); /*|no_new_control_sequence| is |true|*/
+else cur_cs=single_base+buffer[first] /*the list has length one*/
+
+@ The conditional \.{\\iffontchar} tests the existence of a character in
+a font.
+
+@<Cases for |conditional|@>=
+case if_font_char_code: {@+scan_font_ident();n=cur_val;scan_char_num();
+ if ((font_bc[n] <= cur_val)&&(font_ec[n] >= cur_val))
+ b=char_exists(char_info(n, qi(cur_val)));
+ else b=false;
+ } @+break;
+
+@ The \.{protected} feature of \eTeX\ defines the \.{\\protected} prefix
+command for macro definitions. Such macros are protected against
+expansions when lists of expanded tokens are built, e.g., for \.{\\edef}
+or during \.{\\write}.
+
+@<Generate all \eTeX...@>=
+primitive("protected", prefix, 8);
+@!@:protected\_}{\.{\\protected} primitive@>
+
+@ @<Cases of |prefix| for |print_cmd_chr|@>=;
+else if (chr_code==8) print_esc("protected")
+
+@ The |get_x_or_protected| procedure is like |get_x_token| except that
+protected macros are not expanded.
+
+@<Declare \eTeX\ procedures for sc...@>=
+static void get_x_or_protected(void) /*sets |cur_cmd|, |cur_chr|, |cur_tok|,
+ and expands non-protected macros*/
+{@+
+loop@+{@+get_token();
+ if (cur_cmd <= max_command) return;
+ if ((cur_cmd >= call)&&(cur_cmd < end_template))
+ if (info(link(cur_chr))==protected_token) return;
+ expand();
+ }
+}
+
+@ A group entered (or a conditional started) in one file may end in a
+different file. Such slight anomalies, although perfectly legitimate,
+may cause errors that are difficult to locate. In order to be able to
+give a warning message when such anomalies occur, \eTeX\ uses the
+|grp_stack| and |if_stack| arrays to record the initial |cur_boundary|
+and |cond_ptr| values for each input file.
+
+@<Glob...@>=
+static save_pointer @!grp_stack[max_in_open+1]; /*initial |cur_boundary|*/
+static pointer @!if_stack[max_in_open+1]; /*initial |cond_ptr|*/
+
+@ When a group ends that was apparently entered in a different input
+file, the |group_warning| procedure is invoked in order to update the
+|grp_stack|. If moreover \.{\\tracingnesting} is positive we want to
+give a warning message. The situation is, however, somewhat complicated
+by two facts: (1)~There may be |grp_stack| elements without a
+corresponding \.{\\input} file or \.{\\scantokens} pseudo file (e.g.,
+error insertions from the terminal); and (2)~the relevant information is
+recorded in the |name_field| of the |input_stack| only loosely
+synchronized with the |in_open| variable indexing |grp_stack|.
+
+@<Declare \eTeX\ procedures for tr...@>=
+static void group_warning(void)
+{@+int i; /*index into |grp_stack|*/
+bool @!w; /*do we need a warning?*/
+base_ptr=input_ptr;input_stack[base_ptr]=cur_input;
+ /*store current state*/
+i=in_open;w=false;
+while ((grp_stack[i]==cur_boundary)&&(i > 0))
+ {@+@<Set variable |w| to indicate if this case should be reported@>;
+ grp_stack[i]=save_index(save_ptr);decr(i);
+ }
+if (w)
+ {@+print_nl("Warning: end of ");print_group(true);
+@.Warning: end of...@>
+ print(" of a different file");print_ln();
+ if (tracing_nesting > 1) show_context();
+ if (history==spotless) history=warning_issued;
+ }
+}
+
+@ This code scans the input stack in order to determine the type of the
+current input file.
+
+@<Set variable |w| to...@>=
+if (tracing_nesting > 0)
+ {@+while ((input_stack[base_ptr].state_field==token_list)||@|
+ (input_stack[base_ptr].index_field > i)) decr(base_ptr);
+ if (input_stack[base_ptr].name_field > 17) w=true;
+ }
+
+@ When a conditional ends that was apparently started in a different
+input file, the |if_warning| procedure is invoked in order to update the
+|if_stack|. If moreover \.{\\tracingnesting} is positive we want to
+give a warning message (with the same complications as above).
+
+@<Declare \eTeX\ procedures for tr...@>=
+static void if_warning(void)
+{@+int i; /*index into |if_stack|*/
+bool @!w; /*do we need a warning?*/
+base_ptr=input_ptr;input_stack[base_ptr]=cur_input;
+ /*store current state*/
+i=in_open;w=false;
+while (if_stack[i]==cond_ptr)
+ {@+@<Set variable |w| to...@>;
+ if_stack[i]=link(cond_ptr);decr(i);
+ }
+if (w)
+ {@+print_nl("Warning: end of ");print_cmd_chr(if_test, cur_if);
+@.Warning: end of...@>
+ print_if_line(if_line);print(" of a different file");print_ln();
+ if (tracing_nesting > 1) show_context();
+ if (history==spotless) history=warning_issued;
+ }
+}
+
+@ Conversely, the |file_warning| procedure is invoked when a file ends
+and some groups entered or conditionals started while reading from that
+file are still incomplete.
+
+@<Declare \eTeX\ procedures for tr...@>=
+static void file_warning(void)
+{@+pointer p; /*saved value of |save_ptr| or |cond_ptr|*/
+quarterword @!l; /*saved value of |cur_level| or |if_limit|*/
+quarterword @!c; /*saved value of |cur_group| or |cur_if|*/
+int @!i; /*saved value of |if_line|*/
+p=save_ptr;l=cur_level;c=cur_group;save_ptr=cur_boundary;
+while (grp_stack[in_open]!=save_ptr)
+ {@+decr(cur_level);
+ print_nl("Warning: end of file when ");
+@.Warning: end of file when...@>
+ print_group(true);print(" is incomplete");@/
+ cur_group=save_level(save_ptr);save_ptr=save_index(save_ptr);
+ }
+save_ptr=p;cur_level=l;cur_group=c; /*restore old values*/
+p=cond_ptr;l=if_limit;c=cur_if;i=if_line;
+while (if_stack[in_open]!=cond_ptr)
+ {@+print_nl("Warning: end of file when ");
+@.Warning: end of file when...@>
+ print_cmd_chr(if_test, cur_if);
+ if (if_limit==fi_code) print_esc("else");
+ print_if_line(if_line);print(" is incomplete");@/
+ if_line=if_line_field(cond_ptr);cur_if=subtype(cond_ptr);
+ if_limit=type(cond_ptr);cond_ptr=link(cond_ptr);
+ }
+cond_ptr=p;if_limit=l;cur_if=c;if_line=i; /*restore old values*/
+print_ln();
+if (tracing_nesting > 1) show_context();
+if (history==spotless) history=warning_issued;
+}
+
+@ Here are the additional \eTeX\ primitives for expressions.
+
+@<Generate all \eTeX...@>=
+primitive("numexpr", last_item, eTeX_expr-int_val+int_val);
+@!@:num\_expr\_}{\.{\\numexpr} primitive@>
+primitive("dimexpr", last_item, eTeX_expr-int_val+dimen_val);
+@!@:dim\_expr\_}{\.{\\dimexpr} primitive@>
+primitive("glueexpr", last_item, eTeX_expr-int_val+glue_val);
+@!@:glue\_expr\_}{\.{\\glueexpr} primitive@>
+primitive("muexpr", last_item, eTeX_expr-int_val+mu_val);
+@!@:mu\_expr\_}{\.{\\muexpr} primitive@>
+
+@ @<Cases of |last_item| for |print_cmd_chr|@>=
+case eTeX_expr-int_val+int_val: print_esc("numexpr");@+break;
+case eTeX_expr-int_val+dimen_val: print_esc("dimexpr");@+break;
+case eTeX_expr-int_val+glue_val: print_esc("glueexpr");@+break;
+case eTeX_expr-int_val+mu_val: print_esc("muexpr");@+break;
+
+@ This code for reducing |cur_val_level| and\slash or negating the
+result is similar to the one for all the other cases of
+|scan_something_internal|, with the difference that |scan_expr| has
+already increased the reference count of a glue specification.
+
+@<Process an expression and |return|@>=
+{@+if (m < eTeX_mu)
+ {@+switch (m) {
+ @/@<Cases for fetching a glue value@>@/
+ } /*there are no other cases*/
+ cur_val_level=glue_val;
+ }
+else if (m < eTeX_expr)
+ {@+switch (m) {
+ @/@<Cases for fetching a mu value@>@/
+ } /*there are no other cases*/
+ cur_val_level=mu_val;
+ }
+else{@+cur_val_level=m-eTeX_expr+int_val;scan_expr();
+ }
+while (cur_val_level > level)
+ {@+if (cur_val_level==glue_val)
+ {@+m=cur_val;cur_val=width(m);delete_glue_ref(m);
+ }
+ else if (cur_val_level==mu_val) mu_error();
+ decr(cur_val_level);
+ }
+if (negative)
+ if (cur_val_level >= glue_val)
+ {@+m=cur_val;cur_val=new_spec(m);delete_glue_ref(m);
+ @<Negate all three glue components of |cur_val|@>;
+ }
+ else negate(cur_val);
+return;
+}
+
+@ @<Declare \eTeX\ procedures for sc...@>=
+static void scan_expr(void);
+
+@ The |scan_expr| procedure scans and evaluates an expression.
+
+@<Declare procedures needed for expressions@>=
+@t\4@>@<Declare subprocedures for |scan_expr|@>@;
+static void scan_expr(void) /*scans and evaluates an expression*/
+{@+
+bool a, @!b; /*saved values of |arith_error|*/
+small_number @!l; /*type of expression*/
+small_number @!r; /*state of expression so far*/
+small_number @!s; /*state of term so far*/
+small_number @!o; /*next operation or type of next factor*/
+int @!e; /*expression so far*/
+int @!t; /*term so far*/
+int @!f; /*current factor*/
+int @!n; /*numerator of combined multiplication and division*/
+pointer @!p; /*top of expression stack*/
+pointer @!q; /*for stack manipulations*/
+l=cur_val_level;a=arith_error;b=false;p=null;
+@<Scan and evaluate an expression |e| of type |l|@>;
+if (b)
+ {@+print_err("Arithmetic overflow");
+@.Arithmetic overflow@>
+ help2("I can't evaluate this expression,",@/
+ "since the result is out of range.");
+ error();
+ if (l >= glue_val)
+ {@+delete_glue_ref(e);e=zero_glue;add_glue_ref(e);
+ }
+ else e=0;
+ }
+arith_error=a;cur_val=e;cur_val_level=l;
+}
+
+@ Evaluating an expression is a recursive process: When the left
+parenthesis of a subexpression is scanned we descend to the next level
+of recursion; the previous level is resumed with the matching right
+parenthesis.
+
+@d expr_none 0 /*\.( seen, or \.( $\langle\it expr\rangle$ \.) seen*/
+@d expr_add 1 /*\.( $\langle\it expr\rangle$ \.+ seen*/
+@d expr_sub 2 /*\.( $\langle\it expr\rangle$ \.- seen*/
+@d expr_mult 3 /*$\langle\it term\rangle$ \.* seen*/
+@d expr_div 4 /*$\langle\it term\rangle$ \./ seen*/
+@d expr_scale 5 /*$\langle\it term\rangle$ \.*
+ $\langle\it factor\rangle$ \./ seen*/
+
+@<Scan and eval...@>=
+restart: r=expr_none;e=0;s=expr_none;t=0;n=0;
+resume: if (s==expr_none) o=l;@+else o=int_val;
+@<Scan a factor |f| of type |o| or start a subexpression@>;
+found: @<Scan the next operator and set |o|@>;
+arith_error=b;
+@<Make sure that |f| is in the proper range@>;
+switch (s) {@<Cases for evaluation of the current term@>@;
+} /*there are no other cases*/
+if (o > expr_sub) s=o;@+else@<Evaluate the current expression@>;
+b=arith_error;
+if (o!=expr_none) goto resume;
+if (p!=null) @<Pop the expression stack and |goto found|@>@;
+
+@ @<Scan the next op...@>=
+@<Get the next non-blank non-call token@>;
+if (cur_tok==other_token+'+') o=expr_add;
+else if (cur_tok==other_token+'-') o=expr_sub;
+else if (cur_tok==other_token+'*') o=expr_mult;
+else if (cur_tok==other_token+'/') o=expr_div;
+else{@+o=expr_none;
+ if (p==null)
+ {@+if (cur_cmd!=relax) back_input();
+ }
+ else if (cur_tok!=other_token+')')
+ {@+print_err("Missing ) inserted for expression");
+@.Missing ) inserted@>
+ help1("I was expecting to see `+', `-', `*', `/', or `)'. Didn't.");
+ back_error();
+ }
+ }
+
+@ @<Scan a factor...@>=
+@<Get the next non-blank non-call token@>;
+if (cur_tok==other_token+'(')
+ @<Push the expression stack and |goto restart|@>;
+back_input();
+if (o==int_val) scan_int();
+else if (o==dimen_val) scan_normal_dimen;
+else if (o==glue_val) scan_normal_glue();
+else scan_mu_glue();
+f=cur_val
+
+@ @<Declare \eTeX\ procedures for sc...@>=
+static void scan_normal_glue(void);@/
+static void scan_mu_glue(void);
+
+@ Here we declare two trivial procedures in order to avoid mutually
+recursive procedures with parameters.
+
+@<Declare procedures needed for expressions@>=
+static void scan_normal_glue(void)
+{@+scan_glue(glue_val);
+}
+@#
+static void scan_mu_glue(void)
+{@+scan_glue(mu_val);
+}
+
+@ Parenthesized subexpressions can be inside expressions, and this
+nesting has a stack. Seven local variables represent the top of the
+expression stack: |p| points to pushed-down entries, if any; |l|
+specifies the type of expression currently beeing evaluated; |e| is the
+expression so far and |r| is the state of its evaluation; |t| is the
+term so far and |s| is the state of its evaluation; finally |n| is the
+numerator for a combined multiplication and division, if any.
+
+@d expr_node_size 4 /*number of words in stack entry for subexpressions*/
+@d expr_e_field(A) mem[A+1].i /*saved expression so far*/
+@d expr_t_field(A) mem[A+2].i /*saved term so far*/
+@d expr_n_field(A) mem[A+3].i /*saved numerator*/
+
+@<Push the expression...@>=
+{@+q=get_node(expr_node_size);link(q)=p;type(q)=l;
+subtype(q)=4*s+r;
+expr_e_field(q)=e;expr_t_field(q)=t;expr_n_field(q)=n;
+p=q;l=o;goto restart;
+}
+
+@ @<Pop the expression...@>=
+{@+f=e;q=p;
+e=expr_e_field(q);t=expr_t_field(q);n=expr_n_field(q);
+s=subtype(q)/4;r=subtype(q)%4;
+l=type(q);p=link(q);free_node(q, expr_node_size);
+goto found;
+}
+
+@ We want to make sure that each term and (intermediate) result is in
+the proper range. Integer values must not exceed |infinity|
+($2^{31}-1$) in absolute value, dimensions must not exceed |max_dimen|
+($2^{30}-1$). We avoid the absolute value of an integer, because this
+might fail for the value $-2^{31}$ using 32-bit arithmetic.
+
+@d num_error(A) /*clear a number or dimension and set |arith_error|*/
+ {@+arith_error=true;A=0;
+ }
+@d glue_error(A) /*clear a glue spec and set |arith_error|*/
+ {@+arith_error=true;delete_glue_ref(A);A=new_spec(zero_glue);
+ }
+
+@<Make sure that |f|...@>=
+if ((l==int_val)||(s > expr_sub))
+ {@+if ((f > infinity)||(f < -infinity)) num_error(f);
+ }
+else if (l==dimen_val)
+ {@+if (abs(f) > max_dimen) num_error(f);
+ }
+else{@+if ((abs(width(f)) > max_dimen)||@|
+ (abs(stretch(f)) > max_dimen)||@|
+ (abs(shrink(f)) > max_dimen)) glue_error(f);
+ }
+
+@ Applying the factor |f| to the partial term |t| (with the operator
+|s|) is delayed until the next operator |o| has been scanned. Here we
+handle the first factor of a partial term. A glue spec has to be copied
+unless the next operator is a right parenthesis; this allows us later on
+to simply modify the glue components.
+
+@d normalize_glue(A)
+ if (stretch(A)==0) stretch_order(A)=normal;
+ if (shrink(A)==0) shrink_order(A)=normal
+
+@<Cases for evaluation of the current term@>=
+case expr_none: if ((l >= glue_val)&&(o!=expr_none))
+ {@+t=new_spec(f);delete_glue_ref(f);normalize_glue(t);
+ }
+ else t=f;@+break;
+
+@ When a term |t| has been completed it is copied to, added to, or
+subtracted from the expression |e|.
+
+@d expr_add_sub(A, B, C) add_or_sub(A, B, C, r==expr_sub)
+@d expr_a(A, B) expr_add_sub(A, B, max_dimen)
+
+@<Evaluate the current expression@>=
+{@+s=expr_none;
+if (r==expr_none) e=t;
+else if (l==int_val) e=expr_add_sub(e, t, infinity);
+else if (l==dimen_val) e=expr_a(e, t);
+else@<Compute the sum or difference of two glue specs@>;
+r=o;
+}
+
+@ The function |add_or_sub(x, y, max_answer, negative)| computes the sum
+(for |negative==false|) or difference (for |negative==true|) of |x| and
+|y|, provided the absolute value of the result does not exceed
+|max_answer|.
+
+@<Declare subprocedures for |scan_expr|@>=
+static int add_or_sub(int @!x, int @!y, int @!max_answer, bool @!negative)
+{@+int a; /*the answer*/
+if (negative) negate(y);
+if (x >= 0)
+ if (y <= max_answer-x) a=x+y;@+else num_error(a)@;
+else if (y >= -max_answer-x) a=x+y;@+else num_error(a);
+return a;
+}
+
+@ We know that |stretch_order(e) > normal| implies |stretch(e)!=0| and
+|shrink_order(e) > normal| implies |shrink(e)!=0|.
+
+@<Compute the sum or diff...@>=
+{@+width(e)=expr_a(width(e), width(t));
+if (stretch_order(e)==stretch_order(t))
+ stretch(e)=expr_a(stretch(e), stretch(t));
+else if ((stretch_order(e) < stretch_order(t))&&(stretch(t)!=0))
+ {@+stretch(e)=stretch(t);stretch_order(e)=stretch_order(t);
+ }
+if (shrink_order(e)==shrink_order(t))
+ shrink(e)=expr_a(shrink(e), shrink(t));
+else if ((shrink_order(e) < shrink_order(t))&&(shrink(t)!=0))
+ {@+shrink(e)=shrink(t);shrink_order(e)=shrink_order(t);
+ }
+delete_glue_ref(t);normalize_glue(e);
+}
+
+@ If a multiplication is followed by a division, the two operations are
+combined into a `scaling' operation. Otherwise the term |t| is
+multiplied by the factor |f|.
+
+@d expr_m(A) A=nx_plus_y(A, f, 0)
+
+@<Cases for evaluation of the current term@>=
+case expr_mult: if (o==expr_div)
+ {@+n=f;o=expr_scale;
+ }
+ else if (l==int_val) t=mult_integers(t, f);
+ else if (l==dimen_val) expr_m(t);
+ else{@+expr_m(width(t));expr_m(stretch(t));expr_m(shrink(t));
+ } @+break;
+
+@ Here we divide the term |t| by the factor |f|.
+
+@d expr_d(A) A=quotient(A, f)
+
+@<Cases for evaluation of the current term@>=
+case expr_div: if (l < glue_val) expr_d(t);
+ else{@+expr_d(width(t));expr_d(stretch(t));expr_d(shrink(t));
+ } @+break;
+
+@ The function |quotient(n, d)| computes the rounded quotient
+$q=\lfloor n/d+{1\over2}\rfloor$, when $n$ and $d$ are positive.
+
+@<Declare subprocedures for |scan_expr|@>=
+static int quotient(int @!n, int @!d)
+{@+bool negative; /*should the answer be negated?*/
+int @!a; /*the answer*/
+if (d==0) num_error(a)@;
+else{@+if (d > 0) negative=false;
+ else{@+negate(d);negative=true;
+ }
+ if (n < 0)
+ {@+negate(n);negative=!negative;
+ }
+ a=n/d;n=n-a*d;d=n-d; /*avoid certain compiler optimizations!*/
+ if (d+n >= 0) incr(a);
+ if (negative) negate(a);
+ }
+return a;
+}
+
+@ Here the term |t| is multiplied by the quotient $n/f$.
+
+@d expr_s(A) A=fract(A, n, f, max_dimen)
+
+@<Cases for evaluation of the current term@>=
+case expr_scale: if (l==int_val) t=fract(t, n, f, infinity);
+ else if (l==dimen_val) expr_s(t);
+ else{@+expr_s(width(t));expr_s(stretch(t));expr_s(shrink(t));
+ }
+
+@ Finally, the function |fract(x, n, d, max_answer)| computes the integer
+$q=\lfloor xn/d+{1\over2}\rfloor$, when $x$, $n$, and $d$ are positive
+and the result does not exceed |max_answer|. We can't use floating
+point arithmetic since the routine must produce identical results in all
+cases; and it would be too dangerous to multiply by~|n| and then divide
+by~|d|, in separate operations, since overflow might well occur. Hence
+this subroutine simulates double precision arithmetic, somewhat
+analogous to \MF's |make_fraction| and |take_fraction| routines.
+
+@<Declare subprocedures for |scan_expr|@>=
+static int fract(int @!x, int @!n, int @!d, int @!max_answer)
+{@+
+bool negative; /*should the answer be negated?*/
+int @!a; /*the answer*/
+int @!f; /*a proper fraction*/
+int @!h; /*smallest integer such that |2*h >= d|*/
+int @!r; /*intermediate remainder*/
+int @!t; /*temp variable*/
+if (d==0) goto too_big;
+a=0;
+if (d > 0) negative=false;
+else{@+negate(d);negative=true;
+ }
+if (x < 0)
+ {@+negate(x);negative=!negative;
+ }
+else if (x==0) goto done;
+if (n < 0)
+ {@+negate(n);negative=!negative;
+ }
+t=n/d;
+if (t > max_answer/x) goto too_big;
+a=t*x;n=n-t*d;
+if (n==0) goto found;
+t=x/d;
+if (t > (max_answer-a)/n) goto too_big;
+a=a+t*n;x=x-t*d;
+if (x==0) goto found;
+if (x < n)
+ {@+t=x;x=n;n=t;
+ } /*now |0 < n <= x < d|*/
+@<Compute \(f)$f=\lfloor xn/d+{1\over2}\rfloor$@>@;
+if (f > (max_answer-a)) goto too_big;
+a=a+f;
+found: if (negative) negate(a);
+goto done;
+too_big: num_error(a);
+done: return a;
+}
+
+@ The loop here preserves the following invariant relations
+between |f|, |x|, |n|, and~|r|:
+(i)~$f+\lfloor(xn+(r+d))/d\rfloor=\lfloor x_0n_0/d+{1\over2}\rfloor$;
+(ii)~|-d <= r < 0 < n <= x < d|, where $x_0$, $n_0$ are the original values of~$x$
+and $n$.
+
+Notice that the computation specifies |(x-d)+x| instead of |(x+x)-d|,
+because the latter could overflow.
+
+@<Compute \(f)$f=\lfloor xn/d+{1\over2}\rfloor$@>=
+f=0;r=(d/2)-d;h=-r;
+loop@+{@+if (odd(n))
+ {@+r=r+x;
+ if (r >= 0)
+ {@+r=r-d;incr(f);
+ }
+ }
+ n=n/2;
+ if (n==0) goto found1;
+ if (x < h) x=x+x;
+ else{@+t=x-d;x=t+x;f=f+n;
+ if (x < n)
+ {@+if (x==0) goto found1;
+ t=x;x=n;n=t;
+ }
+ }
+ }
+found1:
+
+@ The \.{\\gluestretch}, \.{\\glueshrink}, \.{\\gluestretchorder}, and
+\.{\\glueshrinkorder} commands return the stretch and shrink components
+and their orders of ``infinity'' of a glue specification.
+
+@d glue_stretch_order_code (eTeX_int+6) /*code for \.{\\gluestretchorder}*/
+@d glue_shrink_order_code (eTeX_int+7) /*code for \.{\\glueshrinkorder}*/
+@d glue_stretch_code (eTeX_dim+7) /*code for \.{\\gluestretch}*/
+@d glue_shrink_code (eTeX_dim+8) /*code for \.{\\glueshrink}*/
+
+@<Generate all \eTeX...@>=
+primitive("gluestretchorder", last_item, glue_stretch_order_code);
+@!@:glue\_stretch\_order\_}{\.{\\gluestretchorder} primitive@>
+primitive("glueshrinkorder", last_item, glue_shrink_order_code);
+@!@:glue\_shrink\_order\_}{\.{\\glueshrinkorder} primitive@>
+primitive("gluestretch", last_item, glue_stretch_code);
+@!@:glue\_stretch\_}{\.{\\gluestretch} primitive@>
+primitive("glueshrink", last_item, glue_shrink_code);
+@!@:glue\_shrink\_}{\.{\\glueshrink} primitive@>
+
+@ @<Cases of |last_item| for |print_cmd_chr|@>=
+case glue_stretch_order_code: print_esc("gluestretchorder");@+break;
+case glue_shrink_order_code: print_esc("glueshrinkorder");@+break;
+case glue_stretch_code: print_esc("gluestretch");@+break;
+case glue_shrink_code: print_esc("glueshrink");@+break;
+
+@ @<Cases for fetching an integer value@>=
+case glue_stretch_order_code: case glue_shrink_order_code:
+ {@+scan_normal_glue();q=cur_val;
+ if (m==glue_stretch_order_code) cur_val=stretch_order(q);
+ else cur_val=shrink_order(q);
+ delete_glue_ref(q);
+ }
+
+@ @<Cases for fetching a dimension value@>=
+case glue_stretch_code: case glue_shrink_code:
+ {@+scan_normal_glue();q=cur_val;
+ if (m==glue_stretch_code) cur_val=stretch(q);
+ else cur_val=shrink(q);
+ delete_glue_ref(q);
+ }
+
+@ The \.{\\mutoglue} and \.{\\gluetomu} commands convert ``math'' glue
+into normal glue and vice versa; they allow to manipulate math glue with
+\.{\\gluestretch} etc.
+
+@d mu_to_glue_code eTeX_glue /*code for \.{\\mutoglue}*/
+@d glue_to_mu_code eTeX_mu /*code for \.{\\gluetomu}*/
+
+@<Generate all \eTeX...@>=
+primitive("mutoglue", last_item, mu_to_glue_code);
+@!@:mu\_to\_glue\_}{\.{\\mutoglue} primitive@>
+primitive("gluetomu", last_item, glue_to_mu_code);
+@!@:glue\_to\_mu\_}{\.{\\gluetomu} primitive@>
+
+@ @<Cases of |last_item| for |print_cmd_chr|@>=
+case mu_to_glue_code: print_esc("mutoglue");@+break;
+case glue_to_mu_code: print_esc("gluetomu");@+break;
+
+@ @<Cases for fetching a glue value@>=
+case mu_to_glue_code: scan_mu_glue();
+
+@ @<Cases for fetching a mu value@>=
+case glue_to_mu_code: scan_normal_glue();
+
+@ \eTeX\ (in extended mode) supports 32768 (i.e., $2^{15}$) count,
+dimen, skip, muskip, box, and token registers. As in \TeX\ the first
+256 registers of each kind are realized as arrays in the table of
+equivalents; the additional registers are realized as tree structures
+built from variable-size nodes with individual registers existing only
+when needed. Default values are used for nonexistent registers: zero
+for count and dimen values, |zero_glue| for glue (skip and muskip)
+values, void for boxes, and |null| for token lists (and current marks
+discussed below).
+
+Similarly there are 32768 mark classes; the command \.{\\marks}|n|
+creates a mark node for a given mark class |0 <= n <= 32767| (where
+\.{\\marks0} is synonymous to \.{\\mark}). The page builder (actually
+the |fire_up| routine) and the |vsplit| routine maintain the current
+values of |top_mark|, |first_mark|, |bot_mark|, |split_first_mark|, and
+|split_bot_mark| for each mark class. They are accessed as
+\.{\\topmarks}|n| etc., and \.{\\topmarks0} is again synonymous to
+\.{\\topmark}. As in \TeX\ the five current marks for mark class zero
+are realized as |cur_mark| array. The additional current marks are
+again realized as tree structure with individual mark classes existing
+only when needed.
+
+@<Generate all \eTeX...@>=
+primitive("marks", mark, marks_code);
+@!@:marks\_}{\.{\\marks} primitive@>
+primitive("topmarks", top_bot_mark, top_mark_code+marks_code);
+@!@:top\_marks\_}{\.{\\topmarks} primitive@>
+primitive("firstmarks", top_bot_mark, first_mark_code+marks_code);
+@!@:first\_marks\_}{\.{\\firstmarks} primitive@>
+primitive("botmarks", top_bot_mark, bot_mark_code+marks_code);
+@!@:bot\_marks\_}{\.{\\botmarks} primitive@>
+primitive("splitfirstmarks", top_bot_mark, split_first_mark_code+marks_code);
+@!@:split\_first\_marks\_}{\.{\\splitfirstmarks} primitive@>
+primitive("splitbotmarks", top_bot_mark, split_bot_mark_code+marks_code);
+@!@:split\_bot\_marks\_}{\.{\\splitbotmarks} primitive@>
+
+@ The |scan_register_num| procedure scans a register number that must
+not exceed 255 in compatibility mode resp.\ 32767 in extended mode.
+
+@<Declare \eTeX\ procedures for ex...@>=
+static void scan_register_num(void);
+
+@ @<Declare procedures that scan restricted classes of integers@>=
+static void scan_register_num(void)
+{@+scan_int();
+if ((cur_val < 0)||(cur_val > max_reg_num))
+ {@+print_err("Bad register code");
+@.Bad register code@>
+ help2(max_reg_help_line,"I changed this one to zero.");
+ int_error(cur_val);cur_val=0;
+ }
+}
+
+@ @<Initialize variables for \eTeX\ comp...@>=
+max_reg_num=255;
+max_reg_help_line="A register number must be between 0 and 255.";
+
+@ @<Initialize variables for \eTeX\ ext...@>=
+max_reg_num=32767;
+max_reg_help_line="A register number must be between 0 and 32767.";
+
+@ @<Glob...@>=
+static halfword @!max_reg_num; /*largest allowed register number*/
+static char *@!max_reg_help_line; /*first line of help message*/
+
+@ There are seven almost identical doubly linked trees, one for the
+sparse array of the up to 32512 additional registers of each kind and
+one for the sparse array of the up to 32767 additional mark classes.
+The root of each such tree, if it exists, is an index node containing 16
+pointers to subtrees for 4096 consecutive array elements. Similar index
+nodes are the starting points for all nonempty subtrees for 4096, 256,
+and 16 consecutive array elements. These four levels of index nodes are
+followed by a fifth level with nodes for the individual array elements.
+
+Each index node is nine words long. The pointers to the 16 possible
+subtrees or are kept in the |info| and |link| fields of the last eight
+words. (It would be both elegant and efficient to declare them as
+array, unfortunately \PASCAL\ doesn't allow this.)
+
+The fields in the first word of each index node and in the nodes for the
+array elements are closely related. The |link| field points to the next
+lower index node and the |sa_index| field contains four bits (one
+hexadecimal digit) of the register number or mark class. For the lowest
+index node the |link| field is |null| and the |sa_index| field indicates
+the type of quantity (|int_val|, |dimen_val|, |glue_val|, |mu_val|,
+|box_val|, |tok_val|, or |mark_val|). The |sa_used| field in the index
+nodes counts how many of the 16 pointers are non-null.
+
+The |sa_index| field in the nodes for array elements contains the four
+bits plus 16 times the type. Therefore such a node represents a count
+or dimen register if and only if |sa_index < dimen_val_limit|; it
+represents a skip or muskip register if and only if
+|dimen_val_limit <= sa_index < mu_val_limit|; it represents a box register
+if and only if |mu_val_limit <= sa_index < box_val_limit|; it represents a
+token list register if and only if
+|box_val_limit <= sa_index < tok_val_limit|; finally it represents a mark
+class if and only if |tok_val_limit <= sa_index|.
+
+The |new_index| procedure creates an index node (returned in |cur_ptr|)
+having given contents of the |sa_index| and |link| fields.
+
+@d box_val 4 /*the additional box registers*/
+@d mark_val 6 /*the additional mark classes*/
+@#
+@d dimen_val_limit 0x20 /*$2^4\cdot(|dimen_val|+1)$*/
+@d mu_val_limit 0x40 /*$2^4\cdot(|mu_val|+1)$*/
+@d box_val_limit 0x50 /*$2^4\cdot(|box_val|+1)$*/
+@d tok_val_limit 0x60 /*$2^4\cdot(|tok_val|+1)$*/
+@#
+@d index_node_size 9 /*size of an index node*/
+@d sa_index(A) type(A) /*a four-bit address or a type or both*/
+@d sa_used(A) subtype(A) /*count of non-null pointers*/
+
+@<Declare \eTeX\ procedures for ex...@>=
+static void new_index(quarterword @!i, pointer @!q)
+{@+int k; /*loop index*/
+cur_ptr=get_node(index_node_size);sa_index(cur_ptr)=i;
+sa_used(cur_ptr)=0;link(cur_ptr)=q;
+for (k=1; k<=index_node_size-1; k++) /*clear all 16 pointers*/
+ mem[cur_ptr+k]=sa_null;
+}
+
+@ The roots of the seven trees for the additional registers and mark
+classes are kept in the |sa_root| array. The first six locations must
+be dumped and undumped; the last one is also known as |sa_mark|.
+
+@d sa_mark sa_root[mark_val] /*root for mark classes*/
+
+@<Glob...@>=
+static pointer @!sa_root0[mark_val-int_val+1],
+ *const @!sa_root = @!sa_root0-int_val; /*roots of sparse arrays*/
+static pointer @!cur_ptr; /*value returned by |new_index| and |find_sa_element|*/
+static memory_word @!sa_null; /*two |null| pointers*/
+
+@ @<Set init...@>=
+sa_mark=null;sa_null.hh.lh=null;sa_null.hh.rh=null;
+
+@ @<Initialize table...@>=
+for (i=int_val; i<=tok_val; i++) sa_root[i]=null;
+
+@ Given a type |t| and a sixteen-bit number |n|, the |find_sa_element|
+procedure returns (in |cur_ptr|) a pointer to the node for the
+corresponding array element, or |null| when no such element exists. The
+third parameter |w| is set |true| if the element must exist, e.g.,
+because it is about to be modified. The procedure has two main
+branches: one follows the existing tree structure, the other (only used
+when |w| is |true|) creates the missing nodes.
+
+We use macros to extract the four-bit pieces from a sixteen-bit register
+number or mark class and to fetch or store one of the 16 pointers from
+an index node.
+
+@d if_cur_ptr_is_null_then_return_or_goto(A) /*some tree element is missing*/
+ {@+if (cur_ptr==null)
+ if (w) goto A;@+else return;
+ }
+@#
+@d hex_dig1(A) A/4096 /*the fourth lowest hexadecimal digit*/
+@d hex_dig2(A) (A/256)%16 /*the third lowest hexadecimal digit*/
+@d hex_dig3(A) (A/16)%16 /*the second lowest hexadecimal digit*/
+@d hex_dig4(A) A%16 /*the lowest hexadecimal digit*/
+@#
+@d get_sa_ptr if (odd(i)) cur_ptr=link(q+(i/2)+1);
+ else cur_ptr=info(q+(i/2)+1)
+ /*set |cur_ptr| to the pointer indexed by |i| from index node |q|*/
+@d put_sa_ptr(A) if (odd(i)) link(q+(i/2)+1)=A;
+ else info(q+(i/2)+1)=A
+ /*store the pointer indexed by |i| in index node |q|*/
+@d add_sa_ptr {@+put_sa_ptr(cur_ptr);incr(sa_used(q));
+ } /*add |cur_ptr| as the pointer indexed by |i| in index node |q|*/
+@d delete_sa_ptr {@+put_sa_ptr(null);decr(sa_used(q));
+ } /*delete the pointer indexed by |i| in index node |q|*/
+
+@<Declare \eTeX\ procedures for ex...@>=
+static void find_sa_element(small_number @!t, halfword @!n, bool @!w)
+ /*sets |cur_val| to sparse array element location or |null|*/
+{@+
+pointer q; /*for list manipulations*/
+small_number @!i; /*a four bit index*/
+cur_ptr=sa_root[t];
+if_cur_ptr_is_null_then_return_or_goto(not_found);@/
+q=cur_ptr;i=hex_dig1(n);get_sa_ptr;
+if_cur_ptr_is_null_then_return_or_goto(not_found1);@/
+q=cur_ptr;i=hex_dig2(n);get_sa_ptr;
+if_cur_ptr_is_null_then_return_or_goto(not_found2);@/
+q=cur_ptr;i=hex_dig3(n);get_sa_ptr;
+if_cur_ptr_is_null_then_return_or_goto(not_found3);@/
+q=cur_ptr;i=hex_dig4(n);get_sa_ptr;
+if ((cur_ptr==null)&&w) goto not_found4;
+return;
+not_found: new_index(t, null); /*create first level index node*/
+sa_root[t]=cur_ptr;q=cur_ptr;i=hex_dig1(n);
+not_found1: new_index(i, q); /*create second level index node*/
+add_sa_ptr;q=cur_ptr;i=hex_dig2(n);
+not_found2: new_index(i, q); /*create third level index node*/
+add_sa_ptr;q=cur_ptr;i=hex_dig3(n);
+not_found3: new_index(i, q); /*create fourth level index node*/
+add_sa_ptr;q=cur_ptr;i=hex_dig4(n);
+not_found4: @<Create a new array element of type |t| with index |i|@>;
+link(cur_ptr)=q;add_sa_ptr;
+}
+
+@ The array elements for registers are subject to grouping and have an
+|sa_lev| field (quite analogous to |eq_level|) instead of |sa_used|.
+Since saved values as well as shorthand definitions (created by e.g.,
+\.{\\countdef}) refer to the location of the respective array element,
+we need a reference count that is kept in the |sa_ref| field. An array
+element can be deleted (together with all references to it) when its
+|sa_ref| value is |null| and its value is the default value.
+@^reference counts@>
+
+Skip, muskip, box, and token registers use two word nodes, their values
+are stored in the |sa_ptr| field.
+Count and dimen registers use three word nodes, their
+values are stored in the |sa_int| resp.\ |sa_dim| field in the third
+word; the |sa_ptr| field is used under the name |sa_num| to store
+the register number. Mark classes use four word nodes. The last three
+words contain the five types of current marks
+
+@d sa_lev sa_used /*grouping level for the current value*/
+@d pointer_node_size 2 /*size of an element with a pointer value*/
+@d sa_type(A) (sa_index(A)/16) /*type part of combined type/index*/
+@d sa_ref(A) info(A+1) /*reference count of a sparse array element*/
+@d sa_ptr(A) link(A+1) /*a pointer value*/
+@#
+@d word_node_size 3 /*size of an element with a word value*/
+@d sa_num(A) sa_ptr(A) /*the register number*/
+@d sa_int(A) mem[A+2].i /*an integer*/
+@d sa_dim(A) mem[A+2].sc /*a dimension (a somewhat esotheric distinction)*/
+@#
+@d mark_class_node_size 4 /*size of an element for a mark class*/
+@#
+@d fetch_box(A) /*fetch |box(cur_val)|*/
+ if (cur_val < 256) A=box(cur_val);
+ else{@+find_sa_element(box_val, cur_val, false);
+ if (cur_ptr==null) A=null;@+else A=sa_ptr(cur_ptr);
+ }
+
+@<Create a new array element...@>=
+if (t==mark_val) /*a mark class*/
+ {@+cur_ptr=get_node(mark_class_node_size);
+ mem[cur_ptr+1]=sa_null;mem[cur_ptr+2]=sa_null;mem[cur_ptr+3]=sa_null;
+ }
+else{@+if (t <= dimen_val) /*a count or dimen register*/
+ {@+cur_ptr=get_node(word_node_size);sa_int(cur_ptr)=0;
+ sa_num(cur_ptr)=n;
+ }
+ else{@+cur_ptr=get_node(pointer_node_size);
+ if (t <= mu_val) /*a skip or muskip register*/
+ {@+sa_ptr(cur_ptr)=zero_glue;add_glue_ref(zero_glue);
+ }
+ else sa_ptr(cur_ptr)=null; /*a box or token list register*/
+ }
+ sa_ref(cur_ptr)=null; /*all registers have a reference count*/
+ }
+sa_index(cur_ptr)=16*t+i;sa_lev(cur_ptr)=level_one
+
+@ The |delete_sa_ref| procedure is called when a pointer to an array
+element representing a register is being removed; this means that the
+reference count should be decreased by one. If the reduced reference
+count is |null| and the register has been (globally) assigned its
+default value the array element should disappear, possibly together with
+some index nodes. This procedure will never be used for mark class
+nodes.
+@^reference counts@>
+
+@d add_sa_ref(A) incr(sa_ref(A)) /*increase reference count*/
+@#
+@d change_box(A) /*change |box(cur_val)|, the |eq_level| stays the same*/
+ if (cur_val < 256) box(cur_val)=A;@+else set_sa_box(A)
+@#
+@d set_sa_box(X) {@+find_sa_element(box_val, cur_val, false);
+ if (cur_ptr!=null)
+ {@+sa_ptr(cur_ptr)=X;add_sa_ref(cur_ptr);delete_sa_ref(cur_ptr);
+ }
+ }
+
+@<Declare \eTeX\ procedures for tr...@>=
+static void delete_sa_ref(pointer @!q) /*reduce reference count*/
+{@+
+pointer p; /*for list manipulations*/
+small_number @!i; /*a four bit index*/
+small_number @!s; /*size of a node*/
+decr(sa_ref(q));
+if (sa_ref(q)!=null) return;
+if (sa_index(q) < dimen_val_limit)
+ if (sa_int(q)==0) s=word_node_size;
+ else return;
+else{@+if (sa_index(q) < mu_val_limit)
+ if (sa_ptr(q)==zero_glue) delete_glue_ref(zero_glue);
+ else return;
+ else if (sa_ptr(q)!=null) return;
+ s=pointer_node_size;
+ }
+@/do@+{i=hex_dig4(sa_index(q));p=q;q=link(p);free_node(p, s);
+if (q==null) /*the whole tree has been freed*/
+ {@+sa_root[i]=null;return;
+ }
+delete_sa_ptr;s=index_node_size; /*node |q| is an index node*/
+}@+ while (!(sa_used(q) > 0));
+}
+
+@ The |print_sa_num| procedure prints the register number corresponding
+to an array element.
+
+@<Basic print...@>=
+static void print_sa_num(pointer @!q) /*print register number*/
+{@+halfword @!n; /*the register number*/
+if (sa_index(q) < dimen_val_limit) n=sa_num(q); /*the easy case*/
+else{@+n=hex_dig4(sa_index(q));q=link(q);n=n+16*sa_index(q);
+ q=link(q);n=n+256*(sa_index(q)+16*sa_index(link(q)));
+ }
+print_int(n);
+}
+
+@ Here is a procedure that displays the contents of an array element
+symbolically. It is used under similar circumstances as is
+|restore_trace| (together with |show_eqtb|) for the quantities kept in
+the |eqtb| array.
+
+@<Declare \eTeX\ procedures for tr...@>=
+#ifdef @!STAT
+static void show_sa(pointer @!p, char *@!s)
+{@+small_number t; /*the type of element*/
+begin_diagnostic();print_char('{');print(s);print_char(' ');
+if (p==null) print_char('?'); /*this can't happen*/
+else{@+t=sa_type(p);
+ if (t < box_val) print_cmd_chr(internal_register, p);
+ else if (t==box_val)
+ {@+print_esc("box");print_sa_num(p);
+ }
+ else if (t==tok_val) print_cmd_chr(toks_register, p);
+ else print_char('?'); /*this can't happen either*/
+ print_char('=');
+ if (t==int_val) print_int(sa_int(p));
+ else if (t==dimen_val)
+ {@+print_scaled(sa_dim(p));print("pt");
+ }
+ else{@+p=sa_ptr(p);
+ if (t==glue_val) print_spec(p,"pt");
+ else if (t==mu_val) print_spec(p,"mu");
+ else if (t==box_val)
+ if (p==null) print("void");
+ else{@+depth_threshold=0;breadth_max=1;show_node_list(p);
+ }
+ else if (t==tok_val)
+ {@+if (p!=null) show_token_list(link(p), null, 32);
+ }
+ else print_char('?'); /*this can't happen either*/
+ }
+ }
+print_char('}');end_diagnostic(false);
+}
+#endif
+
+@ Here we compute the pointer to the current mark of type |t| and mark
+class |cur_val|.
+
+@<Compute the mark pointer...@>=
+{@+find_sa_element(mark_val, cur_val, false);
+if (cur_ptr!=null)
+ if (odd(t)) cur_ptr=link(cur_ptr+(t/2)+1);
+ else cur_ptr=info(cur_ptr+(t/2)+1);
+}
+
+@ The current marks for all mark classes are maintained by the |vsplit|
+and |fire_up| routines and are finally destroyed (for \.{INITEX} only)
+@.INITEX@>
+by the |final_cleanup| routine. Apart from updating the current marks
+when mark nodes are encountered, these routines perform certain actions
+on all existing mark classes. The recursive |do_marks| procedure walks
+through the whole tree or a subtree of existing mark class nodes and
+preforms certain actions indicted by its first parameter |a|, the action
+code. The second parameter |l| indicates the level of recursion (at
+most four); the third parameter points to a nonempty tree or subtree.
+The result is |true| if the complete tree or subtree has been deleted.
+
+@d vsplit_init 0 /*action code for |vsplit| initialization*/
+@d fire_up_init 1 /*action code for |fire_up| initialization*/
+@d fire_up_done 2 /*action code for |fire_up| completion*/
+@d destroy_marks 3 /*action code for |final_cleanup|*/
+@#
+@d sa_top_mark(A) info(A+1) /*\.{\\topmarks}|n|*/
+@d sa_first_mark(A) link(A+1) /*\.{\\firstmarks}|n|*/
+@d sa_bot_mark(A) info(A+2) /*\.{\\botmarks}|n|*/
+@d sa_split_first_mark(A) link(A+2) /*\.{\\splitfirstmarks}|n|*/
+@d sa_split_bot_mark(A) info(A+3) /*\.{\\splitbotmarks}|n|*/
+
+@<Declare the function called |do_marks|@>=
+static bool do_marks(small_number @!a, small_number @!l, pointer @!q)
+{@+int i; /*a four bit index*/
+if (l < 4) /*|q| is an index node*/
+ {@+for (i=0; i<=15; i++)
+ {@+get_sa_ptr;
+ if (cur_ptr!=null) if (do_marks(a, l+1, cur_ptr)) delete_sa_ptr;
+ }
+ if (sa_used(q)==0)
+ {@+free_node(q, index_node_size);q=null;
+ }
+ }
+else /*|q| is the node for a mark class*/
+ {@+switch (a) {
+ @<Cases for |do_marks|@>@;
+ } /*there are no other cases*/
+ if (sa_bot_mark(q)==null) if (sa_split_bot_mark(q)==null)
+ {@+free_node(q, mark_class_node_size);q=null;
+ }
+ }
+return(q==null);
+}
+
+@ At the start of the |vsplit| routine the existing |split_fist_mark|
+and |split_bot_mark| are discarded.
+
+@<Cases for |do_marks|@>=
+case vsplit_init: if (sa_split_first_mark(q)!=null)
+ {@+delete_token_ref(sa_split_first_mark(q));sa_split_first_mark(q)=null;
+ delete_token_ref(sa_split_bot_mark(q));sa_split_bot_mark(q)=null;
+ } @+break;
+
+@ We use again the fact that |split_first_mark==null| if and only if
+|split_bot_mark==null|.
+
+@<Update the current marks for |vsplit|@>=
+{@+find_sa_element(mark_val, mark_class(p), true);
+if (sa_split_first_mark(cur_ptr)==null)
+ {@+sa_split_first_mark(cur_ptr)=mark_ptr(p);
+ add_token_ref(mark_ptr(p));
+ }
+else delete_token_ref(sa_split_bot_mark(cur_ptr));
+sa_split_bot_mark(cur_ptr)=mark_ptr(p);
+add_token_ref(mark_ptr(p));
+}
+
+@ At the start of the |fire_up| routine the old |top_mark| and
+|first_mark| are discarded, whereas the old |bot_mark| becomes the new
+|top_mark|. An empty new |top_mark| token list is, however, discarded
+as well in order that mark class nodes can eventually be released. We
+use again the fact that |bot_mark!=null| implies |first_mark!=null|; it
+also knows that |bot_mark==null| implies |top_mark==first_mark==null|.
+
+@<Cases for |do_marks|@>=
+case fire_up_init: if (sa_bot_mark(q)!=null)
+ {@+if (sa_top_mark(q)!=null) delete_token_ref(sa_top_mark(q));
+ delete_token_ref(sa_first_mark(q));sa_first_mark(q)=null;
+ if (link(sa_bot_mark(q))==null) /*an empty token list*/
+ {@+delete_token_ref(sa_bot_mark(q));sa_bot_mark(q)=null;
+ }
+ else add_token_ref(sa_bot_mark(q));
+ sa_top_mark(q)=sa_bot_mark(q);
+ } @+break;
+
+@ @<Cases for |do_marks|@>=
+case fire_up_done: if ((sa_top_mark(q)!=null)&&(sa_first_mark(q)==null))
+ {@+sa_first_mark(q)=sa_top_mark(q);add_token_ref(sa_top_mark(q));
+ } @+break;
+
+@ @<Update the current marks for |fire_up|@>=
+{@+find_sa_element(mark_val, mark_class(p), true);
+if (sa_first_mark(cur_ptr)==null)
+ {@+sa_first_mark(cur_ptr)=mark_ptr(p);
+ add_token_ref(mark_ptr(p));
+ }
+if (sa_bot_mark(cur_ptr)!=null) delete_token_ref(sa_bot_mark(cur_ptr));
+sa_bot_mark(cur_ptr)=mark_ptr(p);add_token_ref(mark_ptr(p));
+}
+
+@ Here we use the fact that the five current mark pointers in a mark
+class node occupy the same locations as the the first five pointers of
+an index node. For systems using a run-time switch to distinguish
+between \.{VIRTEX} and \.{INITEX}, the codewords `$|@t\#\&{ifdef} \.{INIT}@>|\ldots|@t\#\&{endif}@>|$'
+surrounding the following piece of code should be removed.
+@.INITEX@>
+@^system dependencies@>
+
+@<Cases for |do_marks|@>=
+#ifdef @!INIT
+case destroy_marks: for (i=top_mark_code; i<=split_bot_mark_code; i++)
+ {@+get_sa_ptr;
+ if (cur_ptr!=null)
+ {@+delete_token_ref(cur_ptr);put_sa_ptr(null);
+ }
+ }
+#endif
+
+@ The command code |internal_register| is used for `\.{\\count}', `\.{\\dimen}',
+etc., as well as for references to sparse array elements defined by
+`\.{\\countdef}', etc.
+
+@<Cases of |register| for |print_cmd_chr|@>=
+{@+if ((chr_code < mem_bot)||(chr_code > lo_mem_stat_max))
+ cmd=sa_type(chr_code);
+else{@+cmd=chr_code-mem_bot;chr_code=null;
+ }
+if (cmd==int_val) print_esc("count");
+else if (cmd==dimen_val) print_esc("dimen");
+else if (cmd==glue_val) print_esc("skip");
+else print_esc("muskip");
+if (chr_code!=null) print_sa_num(chr_code);
+}
+
+@ Similarly the command code |toks_register| is used for `\.{\\toks}' as
+well as for references to sparse array elements defined by
+`\.{\\toksdef}'.
+
+@<Cases of |toks_register| for |print_cmd_chr|@>=
+{@+print_esc("toks");
+if (chr_code!=mem_bot) print_sa_num(chr_code);
+}
+
+@ When a shorthand definition for an element of one of the sparse arrays
+is destroyed, we must reduce the reference count.
+
+@<Cases for |eq_destroy|@>=
+case toks_register: case internal_register:
+ if ((equiv_field(w) < mem_bot)||(equiv_field(w) > lo_mem_stat_max))
+ delete_sa_ref(equiv_field(w));@+break;
+
+@ The task to maintain (change, save, and restore) register values is
+essentially the same when the register is realized as sparse array
+element or entry in |eqtb|. The global variable |sa_chain| is the head
+of a linked list of entries saved at the topmost level |sa_level|; the
+lists for lowel levels are kept in special save stack entries.
+
+@<Glob...@>=
+static pointer @!sa_chain; /*chain of saved sparse array entries*/
+static quarterword @!sa_level; /*group level for |sa_chain|*/
+
+@ @<Set init...@>=
+sa_chain=null;sa_level=level_zero;
+
+@ The individual saved items are kept in pointer or word nodes similar
+to those used for the array elements: a word node with value zero is,
+however, saved as pointer node with the otherwise impossible |sa_index|
+value |tok_val_limit|.
+
+@d sa_loc(A) sa_ref(A) /*location of saved item*/
+
+@<Declare \eTeX\ procedures for tr...@>=
+static void sa_save(pointer @!p) /*saves value of |p|*/
+{@+pointer q; /*the new save node*/
+quarterword @!i; /*index field of node*/
+if (cur_level!=sa_level)
+ {@+check_full_save_stack;save_type(save_ptr)=restore_sa;
+ save_level(save_ptr)=sa_level;save_index(save_ptr)=sa_chain;
+ incr(save_ptr);sa_chain=null;sa_level=cur_level;
+ }
+i=sa_index(p);
+if (i < dimen_val_limit)
+ {@+if (sa_int(p)==0)
+ {@+q=get_node(pointer_node_size);i=tok_val_limit;
+ }
+ else{@+q=get_node(word_node_size);sa_int(q)=sa_int(p);
+ }
+ sa_ptr(q)=null;
+ }
+else{@+q=get_node(pointer_node_size);sa_ptr(q)=sa_ptr(p);
+ }
+sa_loc(q)=p;sa_index(q)=i;sa_lev(q)=sa_lev(p);
+link(q)=sa_chain;sa_chain=q;add_sa_ref(p);
+}
+
+@ @<Declare \eTeX\ procedures for tr...@>=
+static void sa_destroy(pointer @!p) /*destroy value of |p|*/
+{@+if (sa_index(p) < mu_val_limit) delete_glue_ref(sa_ptr(p));
+else if (sa_ptr(p)!=null)
+ if (sa_index(p) < box_val_limit) flush_node_list(sa_ptr(p));
+ else delete_token_ref(sa_ptr(p));
+}
+
+@ The procedure |sa_def| assigns a new value to sparse array elements,
+and saves the former value if appropriate. This procedure is used only
+for skip, muskip, box, and token list registers. The counterpart of
+|sa_def| for count and dimen registers is called |sa_w_def|.
+
+@d sa_define(A, B, C, D, E) if (e)
+ if (global) gsa_def(A, B);@+else sa_def(A, B);
+ else if (global) geq_define(C, D, E);@+else eq_define(C, D, E)
+@#
+@d sa_def_box /*assign |cur_box| to |box(cur_val)|*/
+ {@+find_sa_element(box_val, cur_val, true);
+ if (global) gsa_def(cur_ptr, cur_box);@+else sa_def(cur_ptr, cur_box);
+ }
+@#
+@d sa_word_define(A, B) if (e)
+ if (global) gsa_w_def(A, B);@+else sa_w_def(A, B);
+ else word_define(A, B)
+
+@<Declare \eTeX\ procedures for tr...@>=
+static void sa_def(pointer @!p, halfword @!e)
+ /*new data for sparse array elements*/
+{@+add_sa_ref(p);
+if (sa_ptr(p)==e)
+ {
+#ifdef @!STAT
+if (tracing_assigns > 0) show_sa(p,"reassigning");
+#endif
+@;@/
+ sa_destroy(p);
+ }
+else{
+#ifdef @!STAT
+if (tracing_assigns > 0) show_sa(p,"changing");
+#endif
+@;@/
+ if (sa_lev(p)==cur_level) sa_destroy(p);@+else sa_save(p);
+ sa_lev(p)=cur_level;sa_ptr(p)=e;
+#ifdef @!STAT
+ if (tracing_assigns > 0) show_sa(p,"into");
+#endif
+@;@/
+ }
+delete_sa_ref(p);
+}
+@#
+static void sa_w_def(pointer @!p, int @!w)
+{@+add_sa_ref(p);
+if (sa_int(p)==w)
+ {
+#ifdef @!STAT
+if (tracing_assigns > 0) show_sa(p,"reassigning");
+#endif
+@;@/
+ }
+else{
+#ifdef @!STAT
+if (tracing_assigns > 0) show_sa(p,"changing");
+#endif
+@;@/
+ if (sa_lev(p)!=cur_level) sa_save(p);
+ sa_lev(p)=cur_level;sa_int(p)=w;
+#ifdef @!STAT
+ if (tracing_assigns > 0) show_sa(p,"into");
+#endif
+@;@/
+ }
+delete_sa_ref(p);
+}
+
+@ The |sa_def| and |sa_w_def| routines take care of local definitions.
+@^global definitions@>
+Global definitions are done in almost the same way, but there is no need
+to save old values, and the new value is associated with |level_one|.
+
+@<Declare \eTeX\ procedures for tr...@>=
+static void gsa_def(pointer @!p, halfword @!e) /*global |sa_def|*/
+{@+add_sa_ref(p);
+#ifdef @!STAT
+if (tracing_assigns > 0) show_sa(p,"globally changing");
+#endif
+@;@/
+sa_destroy(p);sa_lev(p)=level_one;sa_ptr(p)=e;
+#ifdef @!STAT
+if (tracing_assigns > 0) show_sa(p,"into");
+#endif
+@;@/
+delete_sa_ref(p);
+}
+@#
+static void gsa_w_def(pointer @!p, int @!w) /*global |sa_w_def|*/
+{@+add_sa_ref(p);
+#ifdef @!STAT
+if (tracing_assigns > 0) show_sa(p,"globally changing");
+#endif
+@;@/
+sa_lev(p)=level_one;sa_int(p)=w;
+#ifdef @!STAT
+if (tracing_assigns > 0) show_sa(p,"into");
+#endif
+@;@/
+delete_sa_ref(p);
+}
+
+@ The |sa_restore| procedure restores the sparse array entries pointed
+at by |sa_chain|
+
+@<Declare \eTeX\ procedures for tr...@>=
+static void sa_restore(void)
+{@+pointer p; /*sparse array element*/
+@/do@+{p=sa_loc(sa_chain);
+if (sa_lev(p)==level_one)
+ {@+if (sa_index(p) >= dimen_val_limit) sa_destroy(sa_chain);
+#ifdef @!STAT
+ if (tracing_restores > 0) show_sa(p,"retaining");
+#endif
+@;@/
+ }
+else{@+if (sa_index(p) < dimen_val_limit)
+ if (sa_index(sa_chain) < dimen_val_limit) sa_int(p)=sa_int(sa_chain);
+ else sa_int(p)=0;
+ else{@+sa_destroy(p);sa_ptr(p)=sa_ptr(sa_chain);
+ }
+ sa_lev(p)=sa_lev(sa_chain);
+#ifdef @!STAT
+ if (tracing_restores > 0) show_sa(p,"restoring");
+#endif
+@;@/
+ }
+delete_sa_ref(p);
+p=sa_chain;sa_chain=link(p);
+if (sa_index(p) < dimen_val_limit) free_node(p, word_node_size);
+else free_node(p, pointer_node_size);
+}@+ while (!(sa_chain==null));
+}
+
+@ When reading \.{\\patterns} while \.{\\savinghyphcodes} is positive
+the current |lc_code| values are stored together with the hyphenation
+patterns for the current language. They will later be used instead of
+the |lc_code| values for hyphenation purposes.
+
+The |lc_code| values are stored in the linked trie analogous to patterns
+$p_1$ of length~1, with |hyph_root==trie_r[0]| replacing |trie_root| and
+|lc_code(p_1)| replacing the |trie_op| code. This allows to compress
+and pack them together with the patterns with minimal changes to the
+existing code.
+
+@d hyph_root trie_r[0] /*root of the linked trie for |hyph_codes|*/
+
+@<Initialize table entries...@>=
+hyph_root=0;hyph_start=0;
+
+@ @<Store hyphenation codes for current language@>=
+{@+c=cur_lang;first_child=false;p=0;
+@/do@+{q=p;p=trie_r[q];
+}@+ while (!((p==0)||(c <= so(trie_c[p]))));
+if ((p==0)||(c < so(trie_c[p])))
+ @<Insert a new trie node between |q| and |p|, and make |p| point to it@>;
+q=p; /*now node |q| represents |cur_lang|*/
+@<Store all current |lc_code| values@>;
+}
+
+@ We store all nonzero |lc_code| values, overwriting any previously
+stored values (and possibly wasting a few trie nodes that were used
+previously and are not needed now). We always store at least one
+|lc_code| value such that |hyph_index| (defined below) will not be zero.
+
+@<Store all current |lc_code| values@>=
+p=trie_l[q];first_child=true;
+for (c=0; c<=255; c++)
+ if ((lc_code(c) > 0)||((c==255)&&first_child))
+ {@+if (p==0)
+ @<Insert a new trie node between |q| and |p|, and make |p| point to
+it@>@;
+ else trie_c[p]=si(c);
+ trie_o[p]=qi(lc_code(c));
+ q=p;p=trie_r[q];first_child=false;
+ }
+if (first_child) trie_l[q]=0;@+else trie_r[q]=0
+
+@ We must avoid to ``take'' location~1, in order to distinguish between
+|lc_code| values and patterns.
+
+@<Pack all stored |hyph_codes|@>=
+{@+if (trie_root==0) for (p=0; p<=255; p++) trie_min[p]=p+2;
+first_fit(hyph_root);trie_pack(hyph_root);
+hyph_start=trie_ref[hyph_root];
+}
+
+@ The global variable |hyph_index| will point to the hyphenation codes
+for the current language.
+
+@d set_hyph_index /*set |hyph_index| for current language*/
+ if (trie_char(hyph_start+cur_lang)!=qi(cur_lang)
+ ) hyph_index=0; /*no hyphenation codes for |cur_lang|*/
+ else hyph_index=trie_link(hyph_start+cur_lang)
+@#
+@d set_lc_code(A) /*set |hc[0]| to hyphenation or lc code for |A|*/
+ if (hyph_index==0) hc[0]=lc_code(A);
+ else if (trie_char(hyph_index+A)!=qi(A)) hc[0]=0;
+ else hc[0]=qo(trie_op(hyph_index+A))
+
+@<Glob...@>=
+static trie_pointer @!hyph_start; /*root of the packed trie for |hyph_codes|*/
+static trie_pointer @!hyph_index; /*pointer to hyphenation codes for |cur_lang|*/
+
+@ When |saving_vdiscards| is positive then the glue, kern, and penalty
+nodes removed by the page builder or by \.{\\vsplit} from the top of a
+vertical list are saved in special lists instead of being discarded.
+
+@d tail_page_disc disc_ptr[copy_code] /*last item removed by page builder*/
+@d page_disc disc_ptr[last_box_code] /*first item removed by page builder*/
+@d split_disc disc_ptr[vsplit_code] /*first item removed by \.{\\vsplit}*/
+
+@<Glob...@>=
+static pointer @!disc_ptr0[vsplit_code-copy_code+1],
+ *const @!disc_ptr = @!disc_ptr0-copy_code; /*list pointers*/
+
+@ @<Set init...@>=
+page_disc=null;split_disc=null;
+
+@ The \.{\\pagediscards} and \.{\\splitdiscards} commands share the
+command code |un_vbox| with \.{\\unvbox} and \.{\\unvcopy}, they are
+distinguished by their |chr_code| values |last_box_code| and
+|vsplit_code|. These |chr_code| values are larger than |box_code| and
+|copy_code|.
+
+@<Generate all \eTeX...@>=
+primitive("pagediscards", un_vbox, last_box_code);@/
+@!@:page\_discards\_}{\.{\\pagediscards} primitive@>
+primitive("splitdiscards", un_vbox, vsplit_code);@/
+@!@:split\_discards\_}{\.{\\splitdiscards} primitive@>
+
+@ @<Cases of |un_vbox| for |print_cmd_chr|@>=;
+else if (chr_code==last_box_code) print_esc("pagediscards");
+else if (chr_code==vsplit_code) print_esc("splitdiscards")
+
+@ @<Handle saved items and |goto done|@>=
+{@+link(tail)=disc_ptr[cur_chr];disc_ptr[cur_chr]=null;
+goto done;
+}
+
+@ The \.{\\interlinepenalties}, \.{\\clubpenalties}, \.{\\widowpenalties},
+and \.{\\displaywidowpenalties} commands allow to define arrays of
+penalty values to be used instead of the corresponding single values.
+
+@d inter_line_penalties_ptr equiv(inter_line_penalties_loc)
+@<Generate all \eTeX...@>=
+primitive("interlinepenalties", set_shape, inter_line_penalties_loc);@/
+@!@:inter\_line\_penalties\_}{\.{\\interlinepenalties} primitive@>
+primitive("clubpenalties", set_shape, club_penalties_loc);@/
+@!@:club\_penalties\_}{\.{\\clubpenalties} primitive@>
+primitive("widowpenalties", set_shape, widow_penalties_loc);@/
+@!@:widow\_penalties\_}{\.{\\widowpenalties} primitive@>
+primitive("displaywidowpenalties", set_shape, display_widow_penalties_loc);@/
+@!@:display\_widow\_penalties\_}{\.{\\displaywidowpenalties} primitive@>
+
+@ @<Cases of |set_shape| for |print_cmd_chr|@>=
+case inter_line_penalties_loc: print_esc("interlinepenalties");@+break;
+case club_penalties_loc: print_esc("clubpenalties");@+break;
+case widow_penalties_loc: print_esc("widowpenalties");@+break;
+case display_widow_penalties_loc: print_esc("displaywidowpenalties");
+
+@ @<Fetch a penalties array element@>=
+{@+scan_int();
+if ((equiv(m)==null)||(cur_val < 0)) cur_val=0;
+else{@+if (cur_val > penalty(equiv(m))) cur_val=penalty(equiv(m));
+ cur_val=penalty(equiv(m)+cur_val);
+ }
+}
+
+@ |expand_depth| and |expand_depth_count| are used in the \eTeX\ code
+above, but not defined. So we correct this in the following modules,
+|expand_depth| having been defined by us as an integer paramater (hence
+there is a new primitive to create in \eTeX\ mode), and
+|expand_depth_count| needing to be a global. Both have to be defined to
+some sensible value.
+
+@<Glob...@>=
+static int @!expand_depth_count; /*current expansion depth*/
+
+@ @<Generate all \eTeX...@>=
+primitive("expanddepth", assign_int, int_base+expand_depth_code);@/
+@!@:expand\_depth\_}{\.{\\expanddepth} primitive@>
+
+@ @<Cases for |print_param|@>=
+case expand_depth_code: print_esc("expanddepth");@+break;
+
+@ @<Initialize variables for \eTeX\ extended mode@>=
+expand_depth=10000; /*value taken for compatibility with Web2C*/
+expand_depth_count=0;
+
+@* The extended features of \Prote.
+\Prote\ extends furthermore \eTeX\ i.e. \eTeX\ is thus required
+before adding \Prote\ own extensions. But if \eTeX\ mode has not
+be enabled, the engine is still compatible with \TeX with no added
+primitive commands and with a modification of code---from
+\eTeX\ exclusively for now---that is sufficiently minor so that
+the engine still deserves the name \TeX.
+
+@d Prote_ex (Prote_mode==1) /*is this prote mode?*/
+
+@<Glob...@>=
+static int @!Prote_mode; /*to be or not to be; but an int to dump*/
+
+@ We begin in \TeX\ compatibility mode. The state |Prote_mode| will be
+set to $1$ only if activated by the supplementary `*' added to the one
+activating the \eTeX\ extensions (in fact, this means for the user two
+initial `*' in a row).
+
+@<Initialize table entries...@>=
+Prote_mode=0; /*initially we are in compatibility mode*/
+
+@ @<Dump the \Prote\ state@>=
+dump_int(Prote_mode);
+
+@ @<Undump the \Prote\ state@>=
+undump(0, 1, Prote_mode);
+
+@ In order to not clobber the global scope with variables that are
+locally used, the initializations for \Prote, if the mode is
+activated, are done in a dedicated procedure. These are not part of
+what is dumped.
+
+@<Last-minute procedures@>=
+static void Prote_initialize(void)
+{@+int k; /*all-purpose index*/
+@<\Prote\ initializations@>;
+}
+
+@ There are commands and command modifiers, these command modifiers
+maybe encoding too a type. So we must not step on each other toes.
+
+@ When we are adding primitives that deal intimately with the variables
+of \TeX, in the |eqtb| regions (in our case regions 5 for integers, and
+6 for dimensions), the command modifier to the various
+\.{assign\_*} classes is simply the address. So we have interpolated
+our added variables above since this is done by the way of WEB
+pre-processing.
+
+@ For the conditional primitives, the way is straightforward.
+
+@d if_incsname_code (eTeX_last_if_test_cmd_mod+1) /* `\.{\\ifincsname}' */
+@d if_primitive_code (eTeX_last_if_test_cmd_mod+2) /* `\.{\\ifprimitive}' */
+@ The |last_item| class is for secondary internal values, that can be
+dereferenced by \.{\\the} but are read-only and are mainly related to
+the value of a current state or are such values but their assignation
+shall trigger an action, and we shall not hook in the \.{assign\_*}
+processing.
+
+The command modifiers for the |last_item| class were, originally,
+encoding too the type of the item (see m.410). But \eTeX\ has added its
+extensions and we won't try to be smart: the type |cur_val_level| will
+be set by switching between contiguous ranges of values of the same
+type.
+
+And we will define here all the instances of |last_item| that we add
+in order to keep our number assignations gathered.
+
+@d Prote_version_code (eTeX_last_last_item_cmd_mod+1)
+ /*code for \.{\\Proteversion}*/
+@d random_seed_code (eTeX_last_last_item_cmd_mod+2) /* \.{\\randomseed} */
+@d elapsed_time_code (eTeX_last_last_item_cmd_mod+3) /* \.{\\elapsedtime} */
+@d shell_escape_code (eTeX_last_last_item_cmd_mod+4) /* \.{\\shellescape} */
+@d last_xpos_code (eTeX_last_last_item_cmd_mod+5) /* \.{\\lastxpos} */
+@d last_ypos_code (eTeX_last_last_item_cmd_mod+6) /* \.{\\lastypos} */
+
+@ The \.{convert} class is for conversion of some external stuff to put
+it, as a token list, into the scanner. It is not an internal value that
+could be dereferenced by \.{\\the} and it is obviously not settable: it
+expands to the token list.
+
+@d Prote_revision_code (eTeX_last_convert_cmd_mod+1) /* \.{\\Proterevision}*/
+@d strcmp_code (eTeX_last_convert_cmd_mod+2) /* \.{\\strcmp} */
+@d set_random_seed_code (eTeX_last_convert_cmd_mod+3) /* \.{\\setrandomseed} */
+@d normal_deviate_code (eTeX_last_convert_cmd_mod+4) /* \.{\\normaldeviate} */
+@d uniform_deviate_code (eTeX_last_convert_cmd_mod+5) /* \.{\\uniformdeviate} */
+@d creation_date_code (eTeX_last_convert_cmd_mod+6) /* \.{\\creationdate} */
+@d file_size_code (eTeX_last_convert_cmd_mod+7) /* \.{\\filesize} */
+@d file_mod_date_code (eTeX_last_convert_cmd_mod+8) /* \.{\\filemodedate} */
+@d file_dump_code (eTeX_last_convert_cmd_mod+9) /* \.{\\filedump} */
+@d mdfive_sum_code (eTeX_last_convert_cmd_mod+10) /* \.{\\mdfivesum} */
+@d Prote_last_convert_code mdfive_sum_code
+
+@ When modifying the meaning of something---in this case, for now,
+switching to the primitive meaning if it exists---or modifying the
+way expansion is done, it seems that it can be thought as a special
+case of expansion, hence a variant of |expand_after|.
+
+@d primitive_code (eTeX_last_expand_after_cmd_mod+1) /* `\.{\\primitive}' */
+@d expanded_code (eTeX_last_expand_after_cmd_mod+2) /* `\.{\\expanded}' */
+@ When the primitive manipulate something really external, whether
+trying to insert something in the output format---\.{DVI} for
+us---or dealing with the system, it doesn't fit in any cmd group and
+could be called an exception. So it will be a variant of the |extension|
+cmd group.
+
+\eTeX\ didn't add new primitives to the extension command group, so we
+add a related macro, equal to |TeX_last_extension_cmd_mod|, simply so
+that it is locally obvious.
+
+@d eTeX_last_extension_cmd_mod TeX_last_extension_cmd_mod /*none added*/
+@d reset_timer_code (eTeX_last_extension_cmd_mod+1) /* `\.{\\resettimer}' */
+@d save_pos_code (eTeX_last_extension_cmd_mod+2) /* `\.{\\savepos}' */
+
+@*1 Identifying \Prote.
+
+We will start by giving a mean to test that \Prote\ is activated and
+to identify the version.
+
+@<Generate all \Prote\ primitives@>=
+if (!pdf_on) {
+primitive("Proteversion", last_item, Prote_version_code);
+@!@:Prote\_version\_}{\.{\\Proteversion} primitive@>
+primitive("Proterevision", convert, Prote_revision_code);@/
+@!@:Prote\_revision\_}{\.{\\Proterevision} primitive@>
+}
+@ We use the different hooks added to insert our cases.
+
+@<Cases of |last_item| for |print_cmd_chr|@>=
+case Prote_version_code: print_esc("Proteversion");@+break;
+
+@ @<Cases for fetching a \Prote\ int value@>=
+case Prote_version_code: cur_val=Prote_version;@+break;
+
+@ @<Cases of |convert| for |print_cmd_chr|@>=
+case Prote_revision_code: print_esc("Proterevision");@+break;
+
+@ @<Cases of `Scan the argument for command |c|'@>=
+case Prote_revision_code: do_nothing;@+break;
+
+@ @<Cases of `Print the result of command |c|'@>=
+case Prote_revision_code: print(Prote_revision);@+break;
+
+@*1 \Prote\ added token lists routines.
+
+We will, more than once, convert a general normally expanded text
+to a string. Due to the unfelicity of \PASCAL\ about forward
+declarations of functions, we declare procedures that do their task
+by defining global variables. In this case, |garbage| is used.
+
+|link(garbage)| will hold the pointer to the head of the token list,
+|info(garbage)| to the tail. If the two are equals, then the list is
+empty. The routine making a string will take |link(garbage)| and
+put the number in |info(garbage)|.
+
+@ The first procedure scan a general text (normally) expanded. The
+head of the reference count is returned in |link(garbage)|, the
+tail in |info(garbage)| and if the two are equals, the list is empty.
+User must keep in mind that this has to be flushed when done with!
+
+@<Forward declarations@>=
+static void scan_general_x_text(void);
+
+@ @<Declare \Prote\ procedures for token lists@>=
+static void scan_general_x_text(void)
+{@+pointer d; /*to save |def_ref|*/
+d=def_ref;info(garbage)=scan_toks(false, true);
+link(garbage)=def_ref;
+def_ref=d; /*restore whatever*/
+}
+
+@ The second procedure takes a token list defined in |link(garbage)|
+and converts it to a string number that is returned in |info(garbage)|.
+Neither the token list nor the string (obviously) are flushed.
+
+@<Forward declarations@>=
+static void toks_to_str(void);
+
+@ Here we are using |token_show| that has to take a reference count.
+
+@<Declare \Prote\ procedures for token lists@>=
+static void toks_to_str(void)
+{@+int old_setting; /*holds |selector| setting*/
+old_setting=selector;selector=new_string;
+ token_show(link(garbage));selector=old_setting;
+ str_room(1); /*flirting with the limit means probably truncation*/
+ info(garbage)=make_string();
+}
+
+@*1 \Prote\ added strings routines.
+
+The next procedure sets |name_of_file| from the string given as an
+argument, mimicking the |input| primitive by adding an |.tex| extension
+if there is none. It silently truncates if the length of the string
+exceeds the size of the name buffer and doesn't use |cur_area| and
+|cur_ext|, but |name_length| is set to the real name length (without
+truncating) so a test about |k <= file_name_size| allows to detect the
+impossibility of opening the file without having to call external code.
+The string is not flushed: it is the responsability of the code calling
+the procedure to flush it if wanted.
+
+@<Declare \Prote\ procedures for strings@>=
+static void str_to_name(str_number @!s)
+{@+int k; /*number of positions filled in |name_of_file|*/
+ASCII_code @!c; /*character being packed*/
+int @!j; /*index into |str_pool|*/
+k=0;for (j=str_start[s]; j<=str_start[s+1]-1; j++) {@+
+ c=so(str_pool[j]);incr(k);
+ if (k <= file_name_size) name_of_file[k]=xchr[c];
+ }
+name_length=k;
+name_of_file[name_length+1]=0;
+}
+
+@*1 Exchanging data with external routines.
+
+In order to try to sever external handling from our core, we
+introduce an all purpose exchange buffer |xchg_buffer|, that will
+be an array of bytes (these can be interpreted as \.{text\_char}
+or \.{ASCII\_char} or \.{eight\_bits}).
+
+The data to be used starts at index $1$ and ends at index
+|xchg_buffer_length|.
+
+For the moment, this buffer must accommodate a numerical MD5
+hash value, i.e. $16$ bytes long; will also be used to exchange $64$
+bytes chunks to feed MD5 hash generation, and will have to accommodate
+too the maximal size of the date returned by \.{\\creationdate} or
+\.{\\filemoddate} that is $23$ \.{text\_char}. So at least $64$ for now.
+
+@<Global...@>=
+static eight_bits @!xchg_buffer0[xchg_buffer_size],
+ *const @!xchg_buffer = @!xchg_buffer0-1;
+ /*exchange buffer for interaction with system routines*/
+static int @!xchg_buffer_length; /*last valid index in this buf; 0 means no data*/
+@ @<Check \Prote\ ``constant'' values for consistency@>=
+if (xchg_buffer_size < 64) bad=51;
+
+@ When there is data in the exchange buffer, the length of the data has
+to be set. When an external routine has consumed the data, it shall
+reset the length to $0$.
+
+@<\Prote\ initializations@>=
+xchg_buffer_length=0;
+
+@*1 \Prote\ states.
+
+\.{\\shellescape} depends on a pdf\TeX\ feature, namely the ability to
+escape to shell. There is no such thing in \Prote. So it expands to $0$.
+Note: this a status primitive; it does not allow to set the status but
+simply expands to a read-only integer reflecting it. In \Prote, it is
+always $0$.
+
+@<Generate all \Prote\ primitives@>=
+if (pdf_on) {
+primitive("pdfshellescape", last_item, shell_escape_code);
+@!@:pdfshellescape\_}{\.{\\pdfshellescape} primitive@>
+} else { primitive("shellescape", last_item, shell_escape_code);
+@!@:shellescape\_}{\.{\\shellescape} primitive@>
+}
+
+@ @<Cases of |last_item| for |print_cmd_chr|@>=
+case shell_escape_code:
+ if (pdf_on) print_esc("pdfshellescape");
+ else print_esc("shellescape");
+ @+break;
+
+@ @<Cases for fetching a \Prote\ int value@>=
+case shell_escape_code: cur_val=0;@+break;
+
+@*1 \Prote\ conditionals.
+
+We add the following conditionals, that are susceptible of the same
+expansion rules as the other |if_test| ones.
+
+@<Generate all \Prote\ primitives@>=
+primitive("ifincsname", if_test, if_incsname_code);
+@!@:if\_incsname\_}{\.{\\if} primitive@>
+if (pdf_on)
+primitive("ifpdfprimitive", if_test, if_primitive_code);
+@!@:if\_pdfprimitive\_}{\.{\\if} pdfprimitive@>
+else primitive("ifprimitive", if_test, if_primitive_code);
+@!@:if\_primitive\_}{\.{\\if} primitive@>
+
+
+@ @<Cases of |if_test| for |print_cmd_chr|@>=
+case if_incsname_code: print_esc("ifincsname");@+break;
+case if_primitive_code:
+ if (pdf_on) print_esc("ifpdfprimitive");
+ else print_esc("ifprimitive");
+ @+break;
+
+@ The conditional \.{\\ifincsname} is simple since we increment a global
+variable |incsname_state| when we enter the \.{\\csname} command and
+decrement it when we have reached and passed the \.{\\endcsname}---a
+scope depth index.
+
+@<\Prote\ initializations@>=
+incsname_state=0;
+
+@ @<Cases for |conditional|@>=
+case if_incsname_code: b=(incsname_state > 0);@+break;
+
+@ The conditional \.{\\ifprimitive} is true when the following control
+sequence is a primitive; false otherwise. |id_lookup| can return
+|undefined_control_sequence| (for a control sequence not entered in the
+hash since |no_new_control_sequence| is |true|), but since it has the
+\.{eq\_type} set to |undefined_cs|, the test of this latter works as
+for a control sequence entered but never defined.
+
+@ @<Cases for |conditional|@>=
+case if_primitive_code: {@+@/do@+{get_token();}@+ while (!(cur_tok!=space_token));
+if ((cur_cs!=0)&&(cur_cmd!=undefined_cs)&&(cur_cmd < call)) b=true;else b=false;
+} @+break;
+
+@*1 \Prote\ primitives changing definition or expansion.
+
+The next primitives, here, are more involved since they are whether
+changing the definition of a control sequence, or modifying how the
+tokens will be treated.
+
+@ Since a user level control sequence can give a new definition to a
+primitive, the \.{primitive}... primitive, if the argument is a control
+sequence whose name is the name of a primitive, will make this primitive
+meaning the meaning of the control sequence {\it hic et nunc}. If there
+was no primitive meaning, no error is raised and nothing is changed.
+It can be seen as a kind of \.{expand\_after} command since it is in the
+external handling of the token list creation.
+
+Since we need to redefine the token and hence give a valid control
+sequence in the |eqtb|, we have defined |frozen_primitive|. This
+``frozen'' is, actually, not quite frozen by itself since we will
+redefine its values according to the primitive definition we have
+to restablish momentarily. But it is indeed ``permanent'' since
+it only refers to the permanently defined meanings. Hence, the
+initialization of the |frozen_primitive| address is just to document
+the code: these values will be overwritten on each actual call.
+
+@<Generate all \Prote\ primitives@>=
+if (pdf_on)
+primitive("pdfprimitive", expand_after, primitive_code);
+@!@:pdfprimitive\_}{\.{\\pdfprimitive} primitive@>
+else
+primitive("primitive", expand_after, primitive_code);
+@!@:primitive\_}{\.{\\primitive} primitive@>
+text(frozen_primitive)=text(cur_val);eqtb[frozen_primitive]=eqtb[cur_val];
+
+@ @<Cases of |expandafter| for |print_cmd_chr|@>=
+case primitive_code:
+ if (pdf_on) print_esc("pdfprimitive");
+ else print_esc("primitive");
+ @+break;
+
+@ The problem is that the primitives are added at |level_one| and that a
+redefinition as a macro at this same level by a user simply overwrites
+the definition. We need then to keep these definitions.
+
+Primitives are only added by \.{INITEX}. So we can consider what we will
+call a |ROM|, since it can be only ``flashed'' by \.{INITEX} and is
+read-only afterwards, a kind of BIOS table holding initial system calls
+(primitives).
+
+Since primitives are not macros (they don't need to expand or to
+evaluate parameters since their definition is directly in the code),
+the definition of a primitive is a couple: the command class (|cur_cmd|)
+and the modifier (|cur_chr|) to distinguish between the cases---the
+instances. But since, at the user level, a primitive is identified by
+its name, and that a redefinition is, mandatorily, a homonym, the
+location of the macro shadowing the primitive is at the same address as
+was the primitive in the |eqtb|. So in order to speed-up the check, we
+should organize things so that the address in the |eqtb| of a control
+sequence (one character or multiletter) can be readily
+converted in an address in the |ROM| array.
+
+This array will be an array of memory word, of type |two_halves|, in
+order to re-use the macro definitions set for the table of equivalents.
+
+The one character primitives are added by direct addressing relative to
+|single_base|. The multiletter primitives are added starting at
+|frozen_control_sequence-1|, downwards; but there are only, at the
+moment, $322$ multileter primitives defined by \TeX, $78$ such
+primitives defined by \eTeX, and we are adding $24$ more. It is clear
+that, looking at primitives, region 2 of |eqtb| is really a sparse array
+and that, when |hash_size| is increased for format needs, there will be
+a fair amount of space wasted if we simply copy, in fact, second part
+of region 1 and region 2 in the |ROM|.
+
+Yes, but it is simpler as a first approach---premature optimization is
+the root of all evil. So a simple translation scheme will be enough.
+
+The index in |ROM| will start at $1$ and will go up to $256 + 1 +
+hash\_size$, that is a simple translation from |single_base| to
+|ROM_base|, but only for addresses of interest, the other pointing to
+an |ROM_undefined_primitive| that will allow an easy test.
+
+@d ROM_base 1
+@d ROM_size (256+1+hash_size) /*256 oc, undefined and ml*/
+@d ROM_undefined_primitive 257
+@d ROM_type_field(A) A.hh.b0
+@d ROM_equiv_field(X) X.hh.rh
+@d ROM_type(A) ROM_type_field(ROM[A]) /*command code for equivalent*/
+@d set_ROM_p_from_cs(A) if ((A >= single_base)
+ &&(A < frozen_control_sequence))
+ p=A-single_base+ROM_base;
+ else p=ROM_undefined_primitive
+
+@<Global...@>=
+static memory_word @!ROM0[ROM_size-ROM_base+1],
+ *const @!ROM = @!ROM0-ROM_base;
+
+@ Even if it will be unused in \TeX\ or \eTeX\ modes, we will initialize
+it since we add code to the |primitive| procedure and we need \TeX\
+and \eTeX\ ones to be registered as well, whether \.{INITEX} switches to
+\Prote\ mode later or not.
+
+@<Initialize table entries...@>=
+ROM[ROM_undefined_primitive]=eqtb[undefined_control_sequence];
+for (k=ROM_base; k<=256; k++) ROM[k]=ROM[ROM_undefined_primitive];
+for (k=ROM_undefined_primitive+1; k<=ROM_size; k++)
+ ROM[k]=ROM[ROM_undefined_primitive];
+
+@ When a primitive is added---and this only happens in \.{INITEX}---we
+have to define the corresponding address in the \.{ROM}.
+
+@ |cur_val| has the pointer in second part of region 1 or in region 2
+of |eqtb|.
+
+@<Add primitive definition to the |ROM| array@>=
+set_ROM_p_from_cs(cur_val);
+ROM[p]=eqtb[cur_val];
+
+@ This array has to be dumped since it is only defined by \.{INITEX}. It
+is always dumped even if it is unused unless in \Prote\ mode.
+
+@<Dump the |ROM| array@>=
+for (k=ROM_base; k<=ROM_size; k++) dump_wd(ROM[k]);
+
+@ And what has been dumped shall be undumped.
+
+@<Undump the |ROM| array@>=
+for (k=ROM_base; k<=ROM_size; k++) undump_wd(ROM[k]);
+
+@ Once all this is done, the processing of \.{\\primitive} is simple:
+we read the next token that has to be a control sequence. If this
+control sequence belongs to region 1 or 2 and is defined in |ROM|, we
+redefine the token to be the |frozen_primitive| control sequence,
+redefining its codes from the |ROM| and setting the text associated for
+printing purposes. If not, the token is unchanged. Then we put back the
+token so that it will be processed again, maybe redefined.
+
+@<Cases for |expandafter|@>=
+case primitive_code: {@+get_token();
+set_ROM_p_from_cs(cur_cs);
+if ((p!=ROM_undefined_primitive)&&(ROM_type(p)!=undefined_cs)) {@+
+ eqtb[frozen_primitive]=ROM[p];
+ text(frozen_primitive)=text(cur_cs);
+ cur_tok=cs_token_flag+frozen_primitive;
+ }
+back_input();
+} @+break;
+
+@ The next primitive changes the expansion of its argument that is like
+a general text expanded, except that protected macros (an \eTeX\
+extension) are not extanded.
+
+@ @<Generate all \Prote\ primitives@>=
+primitive("expanded", expand_after, expanded_code);@/
+@!@:expanded\_}{\.{\\expanded} primitive@>
+
+@ @<Cases of |expandafter| for |print_cmd_chr|@>=
+case expanded_code: print_esc("expanded");
+
+@ This intervenes in |expand| and we must substitute a token list to our
+current token, putting it back for further reprocessing.
+
+@<Cases for |expandafter|@>=
+case expanded_code: {@+scan_general_x_text();
+ back_list(link(link(garbage)));
+ free_avail(link(garbage)); /*drop reference count*/
+ }
+
+@*1 \Prote\ strings related primitives.
+
+The primitive \.{\\strcmp} text two parameters that are general text
+without expansion. The two token lists created are converted to strings
+and this couple of strings is then compared, character by character. If
+the first string is lexicographically sorted before the second, the
+expansion is $-1$; if the two strings are equal, the expansion is $0$;
+if the first string is lexicographically sorted after the second, the
+expansion is $1$.
+
+@<Generate all \Prote\ primitives@>=
+if (pdf_on)
+primitive("pdfstrcmp", convert, strcmp_code);@/
+@!@:pdf\_strcmp\_}{\.{\\pdfstrcmp} primitive@>
+else
+primitive("strcmp", convert, strcmp_code);
+@!@:strcmp\_}{\.{\\strcmp} primitive@>
+
+
+@ @<Cases of |convert| for |print_cmd_chr|@>=
+case strcmp_code:
+ if (pdf_on) print_esc("pdfstrcmp");
+ else print_esc("strcmp");
+ @+break;
+
+@ It should be noted that the strings comparison is \TeX\ strings
+comparison: the arguments are subject to the manipulation done when
+scanning a general text (squeezing non escaped blanks), and the
+characters are converted according to the |xord| array. Thus it is
+an |ASCII_code|---in the \TeX\ sense explained at the very beginning
+of the web file, part 2---comparison and the result is the same,
+as long as relative characters are mapped to the same value, whatever
+the system. Nul strings are valid.
+
+@<Cases of `Scan the argument for command |c|'@>=
+case strcmp_code: @<Scan the argument for |strcmp_code|@>@;@+break;
+
+@ @<Scan the argument for |strcmp_code|@>=
+{@+scan_general_x_text();toks_to_str();
+ s=info(garbage);flush_list(link(garbage));
+ scan_general_x_text();toks_to_str();
+ t=info(garbage);flush_list(link(garbage));
+ if ((length(s)==0)&&(length(t)==0)) cur_val=0;
+ else if (length(s)==0) cur_val=-1;
+ else if (length(t)==0) cur_val=1;
+ else{@+m=str_start[s];n=str_start[t];r=false;
+ while ((!r)&&(m < str_start[s+1])&&(n < str_start[t+1])) {@+
+ cur_val=str_pool[m]-str_pool[n];if (cur_val!=0) r=true;
+ incr(m);incr(n);
+ }
+ if (cur_val==0) {@+if (length(s)!=length(t))
+ if (m!=str_start[s+1]) cur_val=1;else cur_val=-1;
+ }
+ else cur_val=cur_val/(double)abs(cur_val);
+ }
+ flush_string;flush_string;
+ }
+
+@ @<Cases of `Print the result of command |c|'@>=
+case strcmp_code: print_int(cur_val);@+break;
+
+@*1 \Prote\ date and time related primitives.
+
+The following primitives are related to the time elapsed since a
+defined moment in time. The creation date is fixed at the moment when
+|fix_date_and_time| has been called and stays fixed afterwards. This
+moment is also, by default, the reference moment for computing the time
+elapsed.
+
+@ The creation date is retrieved by the \.{\\creationdate} primitive. As
+explained above, the date corresponds to the moment when
+|fix_date_and_time| was called taking into account |FORCE_SOURCE_DATE|
+and |SOURCE_DATE_EPOCH| (see above, m.241). If the creation date is
+forced, the string will be UTC related.
+
+The format of the string is |D: YYYYMMDDHHmmSSOHH"mm"|, `O' being the
+relationship of local time to UT, that is `-' (minus), `+' or `Z';
+HH followed by a single quote being the absolute value of the offset
+from UT in hours (00--23), mm followed by a single quote being the
+absolute value of the offset from UT in minutes (00--59). All fields
+after the year are optional and default to zero values.
+
+@ @<Generate all \Prote\ primitives@>=
+if (!pdf_on)
+primitive("pdfcreationdate", convert, creation_date_code);@/
+@!@:pdf\_creation\_date\_}{\.{\\pdfcreationdate} primitive@>
+else
+primitive("creationdate", convert, creation_date_code);@/
+@!@:creationdate\_}{\.{\\creationdate} primitive@>
+
+
+@ @<Cases of |convert| for |print_cmd_chr|@>=
+case creation_date_code:
+ if (pdf_on) print_esc("pdfcreationdate");
+ else print_esc("creationdate");
+ @+break;
+
+@ |get_creation_date| has to be provided by the system.
+@^system dependencies@>
+
+@<Cases of `Scan the argument for command |c|'@>=
+case creation_date_code: get_creation_date();@+break;
+
+@ The date is in the |time_str| so we have simply to convert the
+characters.
+
+@<Cases of `Print the result of command |c|'@>=
+case creation_date_code: for (k=0; time_str[k]!='\0'; k++)
+ print_char(time_str[k]);@+break;
+
+@ The time elapsed is a scaled integer the unit being scaled seconds,
+i.e. $1/65536$ of a second. Since our scaled integers have a
+defined range, the value can not reach or pass, in plain seconds,
+$32767$.
+
+The elapsed time returned is relative to some defined moment. At
+start, the reference moment is the time the date was set for
+|fix_date_and_time|. This requires system support and the default
+implementation here will then fix this moment at noon on 4 July 1776
+and what would be returned by the function is here simply defined by
+a macro: with this reference time and this basic code, |infinity| is
+the permanent answer.
+@^system dependencies@>
+
+@d get_elapsed_time infinity /*a function should be implemented*/
+
+@ @<Generate all \Prote\ primitives@>=
+if (pdf_on) {
+primitive("pdfresettimer", extension, reset_timer_code);@/
+@!@:reset\_timer\_}{\.{\\pdfresettimer} primitive@>
+primitive("pdfelapsedtime", last_item, elapsed_time_code);
+@!@:elapsed\_time\_}{\.{\\pdfelapsedtime} primitive@>
+} else {
+primitive("resettimer", extension, reset_timer_code);@/
+@!@:resettimer\_}{\.{\\resettimer} primitive@>
+primitive("elapsedtime", last_item, elapsed_time_code);@/
+@!@:elapsedtime\_}{\.{\\elapsedtime} primitive@>
+}
+
+@ @<Cases of |last_item| for |print_cmd_chr|@>=
+case elapsed_time_code:
+ if (pdf_on) print_esc("pdfelapsedtime");
+ else print_esc("elapsedtime");
+ @+break;
+
+@ @<Cases of |extension| for |print_cmd_chr|@>=
+case reset_timer_code:
+ if (pdf_on) print_esc("pdfresettimer");
+ else print_esc("resettimer");
+ @+break;
+
+@ @<Cases for fetching a \Prote\ int value@>=
+case elapsed_time_code:
+ { double t=(start_sec*1.0+start_nsec/1000000000.0-reset_timer)*0x10000;
+ if (t>=infinity) cur_val=infinity;
+ else cur_val=(scaled)t;
+ @+break;
+ }
+
+@ The reference moment can be reset by a call to the primitive
+\.{\\resettimer}. It simply resets the reference moment to the moment
+the primitive was called. The counter is not regularily incremented.
+When asked about the time elapsed what is returned is the difference, in
+scaled seconds, from the moment of the call to the moment of reference.
+So there is no persistent variable neither a kind of clock implemented.
+@^system dependencies@>
+
+Since we maintain the time anyway, we implement the timing function.
+@<Glob...@>=
+static double reset_timer=0.0; /*time of the last reset in seconds*/
+
+
+@ Since to reset the timer a simple call to the routine is necessary,
+we simply add it to \.{main\_control} by adding it to the cases handled
+by \.{do\_extension}. It contributes nothing to the token list: it is a
+``fire and forget'', so no need to handle the special \.{subtype} in the
+other hooks.
+
+@<Cases for |do_extension|@>=
+case reset_timer_code: reset_timer=start_sec*1.0+start_nsec/1000000000.0;@+break;
+
+@*1 \Prote\ file related primitives.
+
+The presence of the following primitives in the engine can be
+questioned. Since they are very external, and their implementation, for
+example in C, requires things that are not in the C standard (the
+date of modification of the file, for example). So these should not be
+multiplied.
+
+@ The \.{\\filesize} primitive expands to the size, in bytes, of the
+file.
+
+@<Generate all \Prote\ primitives@>=
+if (pdf_on)
+primitive("pdffilesize", convert, file_size_code);@/
+@!@:pdf\_file\_size\_}{\.{\\pdffilesize} primitive@>
+else
+primitive("filesize", convert, file_size_code);
+@!@:filesize\_}{\.{\\filesize} primitive@>
+
+
+@ @<Cases of |convert| for |print_cmd_chr|@>=
+case file_size_code:
+ if (pdf_on) print_esc("pdffilesize");
+ else print_esc("filesize");
+ @+break;
+
+@ In order to be able to treat the problem when trying to open the file,
+we open here and pass the file pointer, if success, to a dedicated
+function in order to get its size. In case of problem, nothing is
+returned.
+@^system dependencies@>
+
+@<Cases of `Scan the argument for command |c|'@>=
+case file_size_code: @<Scan the argument for |file_size_code|@>@;@+break;
+
+@ @<Scan the argument for |file_size_code|@>=
+{@+scan_general_x_text();toks_to_str();
+ s=info(garbage);flush_list(link(garbage));str_to_name(s);
+ cur_val=-1; /*invalid value if error*/
+ cur_val=get_file_size();
+ flush_string;
+}
+
+@ @<Cases of `Print the result of command |c|'@>=
+case file_size_code: if (cur_val!=-1) print_int(cur_val);@+break;
+
+@ The \.{\\filemoddate} expands to a date with the same format as the
+creation date (see \.{\\creationdate}).
+
+@<Generate all \Prote\ primitives@>=
+if (pdf_on)
+primitive("pdffilemoddate", convert, file_mod_date_code);@/
+@!@:pdf\_file\_mod\_date\_}{\.{\\pdffilemoddate} primitive@>
+else
+primitive("filemoddate", convert, file_mod_date_code);
+@!@:filemoddate\_}{\.{\\filemoddate} primitive@>
+
+
+@ @<Cases of |convert| for |print_cmd_chr|@>=
+case file_mod_date_code:
+ if (pdf_on) print_esc("pdffilemoddate");
+ else print_esc("filemoddate");
+ @+break;
+
+@ For getting the argument, the treatment resembles that of
+\.{\\filesize} obviously, since it is only the type of information
+returned that changes. The availability of this information in system
+dependent. The information shall be set in |xchg_buffer|.
+@^system dependencies@>
+
+In this basic implementation, we set the string to the empty one by
+simply setting |xchg_buffer_length| to $0$.
+
+@d get_file_mtime xchg_buffer_length=0
+
+@<Cases of `Scan the argument for command |c|'@>=
+case file_mod_date_code: @<Scan the argument for |file_mod_date_code|@>@;@+break;
+
+@ @<Scan the argument for |file_mod_date_code|@>=
+{@+scan_general_x_text();toks_to_str();
+ s=info(garbage);flush_list(link(garbage));str_to_name(s);
+ get_file_mod_date();
+ flush_string;
+} @+break;
+
+@ Printing the result consists simply in printing every
+|text_char| in |time_str|.
+If the length is $0$, nothing is printed.
+
+@<Cases of `Print the result of command |c|'@>=
+case file_mod_date_code: for (k=0; time_str[k]!='\0'; k++)
+ print_char(time_str[k]);@+break;
+
+@ The primitive \.{\\filedump} expands to the dump of the first
+ \.{length} bytes of the file, starting from \.{offset}. Offset and
+length are optional integers given, in that order, introduced resp.
+by the keywords ``offset'' and ``length''. If not specified, they
+default to $0$. A length of $0$ expands to nothing (it is not an error).
+The file name is given as a |general text|.
+
+@<Generate all \Prote\ primitives@>=
+if (pdf_on)
+primitive("pdffiledump", convert, file_dump_code);@/
+@!@:pdf\_file\_dump\_}{\.{\\pdffiledump} primitive@>
+else
+primitive("filedump", convert, file_dump_code);
+@!@:filedump\_}{\.{\\filedump} primitive@>
+
+
+@ @<Cases of |convert| for |print_cmd_chr|@>=
+case file_dump_code:
+ if (pdf_on) print_esc("pdffiledump");
+ else print_esc("filedump");
+ @+break;
+
+@ The scanning of the arguments is obvious from the syntax above.
+
+Since ``offset'' and ``length'' may be given in that order, we assign
+the variables \.{k} and \.{l}, in alphabetical order. These have to be
+positive or nul values.
+
+Contrary to other blocks, and for optimization purposes (in order not to
+clobber the string pool with data that we can read, when necessary, one
+byte at a time), \.{k}, \.{l} and \.{f} will be defined here and used
+when printing.
+
+@<Cases of `Scan the argument for command |c|'@>=
+case file_dump_code: @<Scan the argument for |file_dump_code|@>@;@+break;
+
+@ @<Scan the argument for |file_dump_code|@>=
+{@+k=0;l=0; /*defaults*/
+ if (scan_keyword("offset")) {@+scan_int();
+ if (cur_val < 0) {@+print_err("Bad ");print_esc("filedump");
+@.Bad \\filedump@>
+ help2("I allow only nonnegative values here.",@/
+ "I changed this one to zero.");int_error(cur_val);
+ }
+ else k=cur_val;
+ }
+ if (scan_keyword("length")) {@+scan_int();
+ if (cur_val < 0) {@+print_err("Bad ");print_esc("filedump");
+@.Bad \\filedump@>
+ help2("I allow only nonnegative values here.",@/
+ "I changed this one to zero.");int_error(cur_val);
+ }
+ else l=cur_val;
+ }
+ scan_general_x_text();toks_to_str();s=info(garbage);
+ flush_list(link(garbage));str_to_name(s);
+ flush_string; /*this one was the filename argument*/
+ }
+
+@ The variables have been set, and the file name has been defined. We
+simply print the uppercase hexadecimal transcription of every byte
+requested before closing the file. Here we deal with bytes
+(\.{eight\_bits} values) so there is no transcription.
+
+@<Cases of `Print the result of command |c|'@>=
+case file_dump_code:
+ { FILE *f=fopen((char*)name_of_file0,"rb");
+ if (f!=NULL) {@+
+ fseek(f,k,SEEK_SET);
+ do@+{i=fgetc(f); if (i==EOF) break;
+ dig[0]=i%16;dig[1]=i/16;
+ print_the_digs(2);decr(l);
+ }@+ while (!(feof(f)||(l==0)));
+ fclose(f);
+ }
+ } @+break;
+
+@ The \.{\\mdfivesum} is obviously a variant of the \.{convert} class since
+it takes values from external and put them as a token list in the
+stream.
+
+@<Generate all \Prote\ primitives@>=
+if (pdf_on)
+primitive("pdfmdfivesum", convert, mdfive_sum_code);@/
+@!@:pdf\_mdfive\_sum\_}{\.{\\pdfmdfivesum} primitive@>
+else
+primitive("mdfivesum", convert, mdfive_sum_code);
+@!@:mdfivesum\_}{\.{\\mdfivesum} primitive@>
+
+
+@ @<Cases of |convert| for |print_cmd_chr|@>=
+case mdfive_sum_code:
+ if (pdf_on) print_esc("pdfmdfivesum");
+ else print_esc("mdfivesum");
+ @+break;
+
+@ There is an optional keyword "file" that will tell us if the | < general
+text > | is to be taken as a filename or just as the string to hash. The
+| < balanced text > | is expanded in both cases.
+
+Once this is done, we ask to init the MD5 state; then fill the exchange
+buffer with chunks of data and update the MD5 hash with every chunk
+until source is exhausted and ask for the final ($16$ bytes numerical
+value) result that will be put in the |xchg_buffer|.
+
+Since we are looking for a ``general text'', that must be enclosed (at
+least: ended; the opening brace can be implicit) by a |right_brace|,
+an error will be caught with runaways.
+
+The general text is converted to a string. It is legal to have an empty
+string if the argument is not a file.
+
+@<Cases of `Scan the argument for command |c|'@>=
+case mdfive_sum_code: @<Scan the argument for |mdfive_sum_code|@>@; @+break;
+
+@ @<Scan the argument for |mdfive_sum_code|@>=
+{@+r=scan_keyword("file");scan_general_x_text();
+toks_to_str();s=info(garbage);flush_list(link(garbage));
+l=get_md5_sum(s,r);
+flush_string; /*done with the filename or string to hash*/
+}
+
+@ As a result, there is $16$ bytes in the |md5_digest| representing the
+MD5 hash. We simply print, byte by byte, the uppercase hexadecimal
+representation of this hash.
+
+@<Cases of `Print the result of command |c|'@>=
+case mdfive_sum_code: for (k=0; k<l; k++)
+{@+dig[0]=md5_digest[k]%16;@+dig[1]=md5_digest[k]/16;print_the_digs(2);
+} @+break;
+
+@*1 Pseudo-random number generation.
+
+These routines come from John Hobby's \MP\ and generate pseudo-random
+numbers with the additive scheme recommended in Section 3.6 of
+{\sl The Art of Computer Programming}; however, the results are
+random fractions between 0 and |mpfract_one-1|, inclusive.
+
+\MP\ uses 28~significant bits of precision and we have kept this in
+order for the routines to behave the same way as in \MP. So the name
+|mpfract| will be used instead of |scaled|, while the two are integers,
+in the range defined by \TeX.
+
+@d double(A) A=A+A /*multiply a variable by two*/
+@d halfp(A) (A)/2 /*when quantity is known to be positive or zero*/
+
+@ The subroutines for logarithm and exponential involve two tables.
+The first is simple: |two_to_the[k]| equals $2^k$. The second involves
+a bit more calculation, which the author claims to have done correctly:
+|spec_log[k]| is $2^{27}$ times $\ln\bigl(1/(1-2^{-k})\bigr)=
+2^{-k}+{1\over2}2^{-2k}+{1\over3}2^{-3k}+\cdots\,$, rounded to the
+nearest integer.
+
+@<Glob...@>=
+static int @!two_to_the[31]; /*powers of two*/
+static int @!spec_log0[28], *const @!spec_log = @!spec_log0-1; /*special logarithms*/
+
+@ @<\Prote\ initializations@>=
+two_to_the[0]=1;
+for (k=1; k<=30; k++) two_to_the[k]=2*two_to_the[k-1];
+spec_log[1]=93032640;
+spec_log[2]=38612034;
+spec_log[3]=17922280;
+spec_log[4]=8662214;
+spec_log[5]=4261238;
+spec_log[6]=2113709;
+spec_log[7]=1052693;
+spec_log[8]=525315;
+spec_log[9]=262400;
+spec_log[10]=131136;
+spec_log[11]=65552;
+spec_log[12]=32772;
+spec_log[13]=16385;
+for (k=14; k<=27; k++) spec_log[k]=two_to_the[27-k];
+spec_log[28]=1;
+
+@ Here is the routine that calculates $2^8$ times the natural logarithm
+of a |scaled| quantity; it is an integer approximation to $2^{24}\ln(x/2^{16})$,
+when |x| is a given positive integer.
+
+The method is based on exercise 1.2.2--25 in {\sl The Art of Computer
+Programming\/}: During the main iteration we have $1\L 2^{-30}x<1/(1-2^{1-k})$,
+and the logarithm of $2^{30}x$ remains to be added to an accumulator
+register called~$y$. Three auxiliary bits of accuracy are retained in~$y$
+during the calculation, and sixteen auxiliary bits to extend |y| are
+kept in~|z| during the initial argument reduction. (We add
+$100\cdot2^{16}=6553600$ to~|z| and subtract 100 from~|y| so that |z| will
+not become negative; also, the actual amount subtracted from~|y| is~96,
+not~100, because we want to add~4 for rounding before the final division by~8.)
+
+@<Declare \Prote\ arithmetic routines@>=
+static scaled m_log(scaled @!x)
+{@+int @!y, @!z; /*auxiliary registers*/
+int @!k; /*iteration counter*/
+if (x <= 0) @<Handle non-positive logarithm@>@;
+else{@+y=1302456956+4-100; /*$14\times2^{27}\ln2\approx1302456956.421063$*/
+ z=27595+6553600; /*and $2^{16}\times .421063\approx 27595$*/
+ while (x < mpfract_four)
+ {@+double(x);y=y-93032639;z=z-48782;
+ } /*$2^{27}\ln2\approx 93032639.74436163$
+ and $2^{16}\times.74436163\approx 48782$*/
+ y=y+(z/unity);k=2;
+ while (x > mpfract_four+4)
+ @<Increase |k| until |x| can be multiplied by a factor of $2^{-k}$, and
+adjust $y$ accordingly@>;
+ return y/8;
+ }
+}
+
+@ @<Increase |k| until |x| can...@>=
+{@+z=((x-1)/two_to_the[k])+1; /*$z=\lceil x/2^k\rceil$*/
+while (x < mpfract_four+z)
+ {@+z=halfp(z+1);k=k+1;
+ }
+y=y+spec_log[k];x=x-z;
+}
+
+@ @<Handle non-positive logarithm@>=
+{@+print_err("Logarithm of ");
+@.Logarithm...replaced by 0@>
+print_scaled(x);print(" has been replaced by 0");
+help2("Since I don't take logs of non-positive numbers,",@/
+ "I'm zeroing this one. Proceed, with fingers crossed.");
+error();return 0;
+}
+
+@ Here is introduced the special 28bits significand |mpfract|.
+
+@d el_gordo 017777777777 /*$2^{31}-1$, the largest value that \TeX\ likes*/
+@d mpfract_half 01000000000 /*$2^{27}$, represents 0.50000000*/
+@d mpfract_one 02000000000 /*$2^{28}$, represents 1.00000000*/
+@d mpfract_four 010000000000 /*$2^{30}$, represents 4.00000000*/
+
+@<Types...@>=
+typedef int mpfract; /*this type is used for pseudo-random numbers*/
+
+@ The |make_mpfract| routine produces the |mpfract| equivalent of
+|p/(double)q|, given integers |p| and~|q|; it computes the integer
+$f=\lfloor2^{28}p/q+{1\over2}\rfloor$, when $p$ and $q$ are
+positive. If |p| and |q| are both of the same scaled type |t|,
+the ``type relation'' |make_mpfract(t, t)==mpfract| is valid;
+and it's also possible to use the subroutine ``backwards,'' using
+the relation |make_mpfract(t, mpfract)==t| between scaled types.
+
+If the result would have magnitude $2^{31}$ or more, |make_mpfract|
+sets |arith_error=true|. Most of \TeX's internal computations have
+been designed to avoid this sort of error.
+
+If this subroutine were programmed in assembly language on a typical
+machine, we could simply compute |(@t$2^{28}$@>*p)/q|, since a
+double-precision product can often be input to a fixed-point division
+instruction. But when we are restricted to \PASCAL\ arithmetic it
+is necessary either to resort to multiple-precision maneuvering
+or to use a simple but slow iteration. The multiple-precision technique
+would be about three times faster than the code adopted here, but it
+would be comparatively long and tricky, involving about sixteen
+additional multiplications and divisions.
+
+The present implementation is highly portable, but slow; it avoids
+multiplication and division except in the initial stage. But since it is
+not part of \TeX\ inner loop, it doesn't matter.
+
+@<Declare \Prote\ arithmetic routines@>=
+static mpfract make_mpfract(int @!p, int @!q)
+{@+int @!f; /*the fraction bits, with a leading 1 bit*/
+int @!n; /*the integer part of $\vert p/q\vert$*/
+bool @!negative; /*should the result be negated?*/
+int @!be_careful; /*disables certain compiler optimizations*/
+if (p >= 0) negative=false;
+else{@+negate(p);negative=true;
+ }
+if (q <= 0)
+ {
+#ifdef @!DEBUG
+if (q==0) confusion("/");@;
+#endif
+@;@/
+@:this can't happen /}{\quad \./@>
+ negate(q);negative=!negative;
+ }
+n=p/q;p=p%q;
+if (n >= 8)
+ {@+arith_error=true;
+ if (negative) return-el_gordo;@+else return el_gordo;
+ }
+else{@+n=(n-1)*mpfract_one;
+ @<Compute $f=\lfloor 2^{28}(1+p/q)+{1\over2}\rfloor$@>;
+ if (negative) return-(f+n);@+else return f+n;
+ }
+}
+
+@ The |@/do@+{| loop here preserves the following invariant relations
+between |f|, |p|, and~|q|:
+(i)~|0 <= p < q|; (ii)~$fq+p=2^k(q+p_0)$, where $k$ is an integer and
+$p_0$ is the original value of~$p$.
+
+Notice that the computation specifies
+|(p-q)+p| instead of |(p+p)-q|, because the latter could overflow.
+Let us hope that optimizing compilers do not miss this point; a
+special variable |be_careful| is used to emphasize the necessary
+order of computation. Optimizing compilers should keep |be_careful|
+in a register, not store it in memory.
+@^inner loop@>
+
+@<Compute $f=\lfloor 2^{28}(1+p/q)+{1\over2}\rfloor$@>=
+f=1;
+@/do@+{be_careful=p-q;p=be_careful+p;
+if (p >= 0) f=f+f+1;
+else{@+double(f);p=p+q;
+ }
+}@+ while (!(f >= mpfract_one));
+be_careful=p-q;
+if (be_careful+p >= 0) incr(f)
+
+@ The dual of |make_mpfract| is |take_mpfract|, which multiplies a
+given integer~|q| by a fraction~|f|. When the operands are positive, it
+computes $p=\lfloor qf/2^{28}+{1\over2}\rfloor$, a symmetric function
+of |q| and~|f|.
+
+@<Declare \Prote\ arithmetic routines@>=
+static int take_mpfract(int @!q, mpfract @!f)
+{@+int @!p; /*the fraction so far*/
+bool @!negative; /*should the result be negated?*/
+int @!n; /*additional multiple of $q$*/
+int @!be_careful; /*disables certain compiler optimizations*/
+@<Reduce to the case that |f>=0| and |q>0|@>;
+if (f < mpfract_one) n=0;
+else{@+n=f/mpfract_one;f=f%mpfract_one;
+ if (q <= el_gordo/n) n=n*q;
+ else{@+arith_error=true;n=el_gordo;
+ }
+ }
+f=f+mpfract_one;
+@<Compute $p=\lfloor qf/2^{28}+{1\over2}\rfloor-q$@>;
+be_careful=n-el_gordo;
+if (be_careful+p > 0)
+ {@+arith_error=true;n=el_gordo-p;
+ }
+if (negative) return-(n+p);
+else return n+p;
+}
+
+@ @<Reduce to the case that |f>=0| and |q>0|@>=
+if (f >= 0) negative=false;
+else{@+negate(f);negative=true;
+ }
+if (q < 0)
+ {@+negate(q);negative=!negative;
+ }
+
+@ The invariant relations in this case are (i)~$\lfloor(qf+p)/2^k\rfloor
+=\lfloor qf_0/2^{28}+{1\over2}\rfloor$, where $k$ is an integer and
+$f_0$ is the original value of~$f$; (ii)~$2^k\L f<2^{k+1}$.
+@^inner loop@>
+
+@<Compute $p=\lfloor qf/2^{28}+{1\over2}\rfloor-q$@>=
+p=mpfract_half; /*that's $2^{27}$; the invariants hold now with $k=28$*/
+if (q < mpfract_four)
+ @/do@+{if (odd(f)) p=halfp(p+q);@+else p=halfp(p);
+ f=halfp(f);
+ }@+ while (!(f==1));
+else@/do@+{if (odd(f)) p=p+halfp(q-p);@+else p=halfp(p);
+ f=halfp(f);
+ }@+ while (!(f==1))
+
+@ There's an auxiliary array |randoms| that contains 55 pseudo-random
+fractions. Using the recurrence $x_n=(x_{n-55}-x_{n-31})\bmod 2^{28}$,
+we generate batches of 55 new $x_n$'s at a time by calling |new_randoms|.
+The global variable |j_random| tells which element has most recently
+been consumed.
+
+@<Glob...@>=
+static mpfract @!randoms[55]; /*the last 55 random values generated*/
+static int @!j_random; /*the number of unused |randoms|*/
+
+@ This array of pseudo-random numbers is set starting from a seed value,
+that is kept in the global integer |random_seed|.
+
+@<Global...@>=
+static int @!random_seed; /*seed for pseudo-random number generation*/
+
+@ @<Generate all \Prote\ primitives@>=
+if (pdf_on)
+primitive("pdfrandomseed", last_item, random_seed_code);
+@!@:random\_seed\_}{\.{\\pdfrandomseed} primitive@>
+else
+primitive("randomseed", last_item, random_seed_code);@/
+@!@:randomseed\_}{\.{\\randomseed} primitive@>
+
+
+@ @<Cases of |last_item| for |print_cmd_chr|@>=
+case random_seed_code:
+ if (pdf_on) print_esc("pdfrandomseed");
+ else print_esc("randomseed");
+ @+break;
+
+@ @<Cases for fetching a \Prote\ int value@>=
+case random_seed_code: cur_val=random_seed;@+break;
+
+@ We set the initial value from the system time. System integrators
+could provide a better source of pseudo-randomness.
+
+Every time a new seed value is assigned, the array has to be
+regenerated for consumption by routines explained a little later.
+
+@<\Prote\ initializations@>=
+random_seed=sys_time;
+init_randoms();
+
+@ Since changing the value must trigger the redefinition of the array,
+a dedicated primitive is defined to take the new seed and call
+|init_randoms|.
+
+@<Generate all \Prote\ primitives@>=
+if (pdf_on)
+primitive("pdfsetrandomseed", convert, set_random_seed_code);@/
+@!@:set\_random\_seed\_code}{\.{\\pdfsetrandomseed} primitive@>
+else
+primitive("setrandomseed", convert, set_random_seed_code);@/
+@!@:setrandomseed\_}{\.{\\setrandomseed} primitive@>
+
+
+@ @<Cases of |convert| for |print_cmd_chr|@>=
+case set_random_seed_code:
+ if (pdf_on) print_esc("pdfsetrandomseed");
+ else print_esc("setrandomseed");
+ @+break;
+
+@ Once we have retrieved and redefined |random_seed|, we must regenerate
+the |randoms| array.
+
+@<Cases of `Scan the argument for command |c|'@>=
+case set_random_seed_code: {@+scan_int();random_seed=cur_val;
+ init_randoms();
+ } @+break;
+
+@ @<Cases of `Print the result of command |c|'@>=
+case set_random_seed_code: print_int(random_seed);@+break;
+
+@ To consume a random fraction, the program below will say `|next_random|'
+and then it will fetch |randoms[j_random]|.
+
+@d next_random if (j_random==0) new_randoms();
+ else decr(j_random)
+
+@<Declare \Prote\ arithmetic routines@>=
+static void new_randoms(void)
+{@+int @!k; /*index into |randoms|*/
+int @!x; /*accumulator*/
+for (k=0; k<=23; k++)
+ {@+x=randoms[k]-randoms[k+31];
+ if (x < 0) x=x+mpfract_one;
+ randoms[k]=x;
+ }
+for (k=24; k<=54; k++)
+ {@+x=randoms[k]-randoms[k-24];
+ if (x < 0) x=x+mpfract_one;
+ randoms[k]=x;
+ }
+j_random=54;
+}
+
+@ To initialize the |randoms| table, we call the following routine.
+
+@<Declare \Prote\ arithmetic routines@>=
+static void init_randoms(void)
+{@+mpfract @!j, @!jj, @!k; /*more or less random integers*/
+int @!i; /*index into |randoms|*/
+j=abs(random_seed);
+while (j >= mpfract_one) j=halfp(j);
+k=1;
+for (i=0; i<=54; i++)
+ {@+jj=k;k=j-k;j=jj;
+ if (k < 0) k=k+mpfract_one;
+ randoms[(i*21)%55]=j;
+ }
+new_randoms();new_randoms();new_randoms(); /*``warm up'' the array*/
+}
+
+@ To produce a uniform random number in the range |0 <= u < x| or |0 >= u > x|
+or |0==u==x|, given a |scaled| value~|x|, we proceed as shown here.
+
+Note that the call of |mult_integers| will produce the values 0 and~|x|
+with about half the probability that it will produce any other particular
+values between 0 and~|x|, because it rounds its answers.
+
+@<Declare \Prote\ arithmetic routines@>=
+static scaled unif_rand(scaled @!x)
+{@+scaled @!y; /*trial value*/
+next_random;y=take_mpfract(abs(x), randoms[j_random]);
+if (y==abs(x)) return 0;
+else if (x > 0) return y;
+else return-y;
+}
+
+@ This can be used by calling the following primitive.
+
+@<Generate all \Prote\ primitives@>=
+if (pdf_on)
+primitive("pdfuniformdeviate", convert, uniform_deviate_code);@/
+@!@:uniform\_deviate\_}{\.{\\pdfuniformdeviate} primitive@>
+else
+primitive("uniformdeviate", convert, uniform_deviate_code);@/
+@!@:uniformdeviate\_}{\.{\\uniformdeviate} primitive@>
+
+
+@ @<Cases of |convert| for |print_cmd_chr|@>=
+case uniform_deviate_code:
+ if (pdf_on) print_esc("pdfuniformdeviate");
+ else print_esc("uniformdeviate");
+ @+break;
+
+@ It takes one integer argument obviously that will be the argument
+to the function.
+
+@<Cases of `Scan the argument for command |c|'@>=
+case uniform_deviate_code: {@+scan_int();
+ cur_val=unif_rand(cur_val);
+ } @+break;
+
+@ @<Cases of `Print the result of command |c|'@>=
+case uniform_deviate_code: print_int(cur_val);@+break;
+
+@ The following somewhat different subroutine tests rigorously if $ab$ is
+greater than, equal to, or less than~$cd$,
+given integers $(a,b,c,d)$. In most cases a quick decision is reached.
+The result is $+1$, 0, or~$-1$ in the three respective cases.
+
+@d return_sign(A) {@+return A;
+ }
+
+@<Declare \Prote\ arithmetic routines@>=
+static int ab_vs_cd(int @!a, int b, int c, int d)
+{@+
+int @!q, @!r; /*temporary registers*/
+@<Reduce to the case that |a,c>=0|, |b,d>0|@>;
+loop@+{@+q=a/d;r=c/b;
+ if (q!=r)
+ if (q > r) return_sign(1)@;@+else return_sign(-1);
+ q=a%d;r=c%b;
+ if (r==0)
+ if (q==0) return_sign(0)@;@+else return_sign(1);
+ if (q==0) return_sign(-1);
+ a=b;b=q;c=d;d=r;
+ } /*now |a > d > 0| and |c > b > 0|*/
+}
+
+@ @<Reduce to the case that |a...@>=
+if (a < 0)
+ {@+negate(a);negate(b);
+ }
+if (c < 0)
+ {@+negate(c);negate(d);
+ }
+if (d <= 0)
+ {@+if (b >= 0)
+ if (((a==0)||(b==0))&&((c==0)||(d==0))) return_sign(0)@;
+ else return_sign(1);
+ if (d==0)
+ if (a==0) return_sign(0)@;@+else return_sign(-1);
+ q=a;a=c;c=q;q=-b;b=-d;d=q;
+ }
+else if (b <= 0)
+ {@+if (b < 0) if (a > 0) return_sign(-1);
+ if (c==0) return_sign(0)@;else return_sign(-1);
+ }
+
+@ Finally, a normal deviate with mean zero and unit standard deviation
+can readily be obtained with the ratio method (Algorithm 3.4.1R in
+{\sl The Art of Computer Programming\/}).
+
+@<Declare \Prote\ arithmetic routines@>=
+static scaled norm_rand(void)
+{@+int @!x, @!u, @!l; /*what the book would call $2^{16}X$, $2^{28}U$,
+ and $-2^{24}\ln U$*/
+@/do@+{
+ @/do@+{next_random;
+ x=take_mpfract(112429, randoms[j_random]-mpfract_half);
+ /*$2^{16}\sqrt{8/e}\approx 112428.82793$*/
+ next_random;u=randoms[j_random];
+ }@+ while (!(abs(x) < u));
+x=make_mpfract(x, u);
+l=139548960-m_log(u); /*$2^{24}\cdot12\ln2\approx139548959.6165$*/
+}@+ while (!(ab_vs_cd(1024, l, x, x) >= 0));
+return x;
+}
+
+@ This can be used by calling the following primitive.
+
+@<Generate all \Prote\ primitives@>=
+if (pdf_on)
+primitive("pdfnormaldeviate", convert, normal_deviate_code);@/
+@!@:normal\_deviate\_}{\.{\\pdfnormaldeviate} primitive@>
+else
+primitive("normaldeviate", convert, normal_deviate_code);@/
+@!@:normaldeviate\_}{\.{\\normaldeviate} primitive@>
+
+
+@ @<Cases of |convert| for |print_cmd_chr|@>=
+case normal_deviate_code:
+ if (pdf_on) print_esc("pdfnormaldeviate");
+ else print_esc("normaldeviate");
+ @+break;
+
+
+@ @<Cases of `Scan the argument for command |c|'@>=
+case normal_deviate_code: cur_val=norm_rand();@+break;
+
+@ @<Cases of `Print the result of command |c|'@>=
+case normal_deviate_code: print_int(cur_val);@+break;
+
+@*1 DVI related primitives.
+
+These primitives are related to positions in the DVI output.
+
+The \TeX\ and DVI system coordinates relate to an origin that is at the
+upper left corner. The \TeX\ coordinates are computed relative to an
+origin that has $(0,0)$ coordinates. Coordinates grow then rightward and
+downward. This is the {\sl page} coordinates relative to what is
+typeset (what \TeX\ is dealing with).
+
+But this typesetting material has to be put on what we will call {\sl
+paper}. The material put into shape by \TeX\ is put on the paper. On
+this paper, where will be put the \TeX\ origin? It is considered to be
+$1in$ at the right and $1in$ down from the upper left corner of the
+paper (see m.590, alinea 2).
+
+@d DVI_std_x_offset 4736286 /*1 inch in sp*/
+@d DVI_std_y_offset 4736286 /*1 inch in sp*/
+
+@ But the paper size is not specified in the DVI file and is not being
+dealt with by \TeX.
+
+In order to have a common reference point, and since the \.{\\lastxpos}
+and \.{\\lastypos} primitives originated in pdf\TeX, these two
+primitives give positions, in scaled points, relative to the lower left
+corner of the paper.
+
+Hence the need, for these primitive, to define the paper size, with the
+(misnamed) \.{\\pagewidth} and \.{\\pageheight}.
+
+\.{\\pagewidth} and \.{\\pageheight} are dimension parameters,
+initialized to $0$ by the generic \TeX\ code.
+
+@<Generate all \Prote\ primitives@>=
+if (!pdf_on) {
+primitive("pdfpagewidth", assign_dimen, dimen_base+page_width_code);@/
+@!@:pdf\_page\_width\_}{\.{\\pdfpagewidth} primitive@>
+primitive("pdfpageheight", assign_dimen, dimen_base+page_height_code);@/
+@!@:pdf\_page\_height\_}{\.{\\pdfpageheight} primitive@>
+} else {
+primitive("pagewidth", assign_dimen, dimen_base+page_width_code);@/
+@!@:pagewidth\_}{\.{\\pagewidth} primitive@>
+primitive("pageheight", assign_dimen, dimen_base+page_height_code);@/
+@!@:pageheight\_}{\.{\\pageheight} primitive@>
+}
+
+
+
+@ When instructed to, the \.{h} and \.{v} last values are transformed,
+in the coordinates system defined above and saved in the global
+variables |last_saved_xpos| and |last_saved_ypos|. They are
+initialized to $0$ and we do not make any verification that a
+call to the \.{\\savepos} primitive---to come---has been made before
+retrieving their values.
+
+@<Global...@>=
+static scaled @!last_saved_xpos, last_saved_ypos; /*last (x,y) DVI pos saved*/
+
+@ @<\Prote\ initializations@>=
+last_saved_xpos=0;last_saved_ypos=0;
+
+@ @<Set |last_saved_xpos| and |last_saved_ypos| with transformed coordinates@>=
+last_saved_xpos=cur_h+DVI_std_x_offset;
+last_saved_ypos=page_height-(cur_v+DVI_std_y_offset);
+
+@ @<Generate all \Prote\ primitives@>=
+if (!pdf_on) {
+primitive("pdflastxpos", last_item, last_xpos_code);@/
+@!@:pdflastxpos\_}{\.{\\pdflastxpos} primitive@>
+primitive("lastypos", last_item, last_ypos_code);@/
+@!@:pdflastypos\_}{\.{\\pdflastypos} primitive@>
+} else {
+primitive("lastxpos", last_item, last_xpos_code);@/
+@!@:lastxpos\_}{\.{\\lastxpos} primitive@>
+primitive("lastypos", last_item, last_ypos_code);@/
+@!@:lastypos\_}{\.{\\lastypos} primitive@>
+}
+
+@ @<Cases of |last_item| for |print_cmd_chr|@>=
+case last_xpos_code:
+ if (pdf_on) print_esc("pdflastxpos");
+ else print_esc("lastxpos");
+ @+break;
+case last_ypos_code:
+ if (pdf_on) print_esc("pdflastypos");
+ else print_esc("lastypos");
+ @+break;
+
+@ @<Cases for fetching a \Prote\ int value@>=
+case last_xpos_code: cur_val=last_saved_xpos;@+break;
+case last_ypos_code: cur_val=last_saved_ypos;
+
+@ |last_saved_xpos| and |last_saved_ypos| are only defined when
+instructed to by the call the the \.{\\savepos} primitive. Since the
+real work has to be done at \.{shipout} time, it is a case to be treated
+like the \.{\\special} primitive, that is it belongs to the
+\.{extension} class.
+
+We will add something more in the handling of the primitive: it will
+insert a \.{whatsit} in the DVI file so that one, using the program
+|dvitype|, could retrieve more than one {\it hic}. So there is a counter
+incremented whenever the primitive is called.
+
+@<Global...@>=
+static int last_save_pos_number; /*identifying the order of the call*/
+
+@ @<\Prote\ initializations@>=
+last_save_pos_number=0; /*i.e. none*/
+
+@ @<Generate all \Prote\ primitives@>=
+if (pdf_on)
+primitive("pdfsavepos", extension, save_pos_code);@/
+@!@:pdf\_save\_pos\_}{\.{\\pdfsavepos} primitive@>
+else
+primitive("savepos", extension, save_pos_code);@/
+@!@:savepos\_}{\.{\\savepos} primitive@>
+
+
+@ @<Cases of |extension| for |print_cmd_chr|@>=
+case save_pos_code:
+ if (pdf_on) print_esc("pdfsavepos");
+ else print_esc("savepos");
+ @+break;
+@ @<Cases for |do_extension|@>=
+case save_pos_code: @<Implement \.{\\savepos}@>@;@+break;
+
+@ We need the basic two words node, since we don't pass any parameter
+and it is just an instruction to do something. So the \.{whatsit} node
+is just the call.
+
+@<Implement \.{\\savepos}@>=
+{@+new_whatsit(save_pos_code, small_node_size);write_stream(tail)=null;
+ write_tokens(tail)=null;
+ }
+
+@ @<Cases for displaying the |whatsit| node@>=
+case save_pos_code:
+ if (pdf_on) print_esc("pdfsavepos");
+ else print_esc("savepos");
+ @+break;
+@ @<Cases for making a partial copy of the whatsit node@>=
+case save_pos_code: {@+r=get_node(small_node_size);
+ words=small_node_size;
+ } @+break;
+
+@ @<Cases for wiping out the whatsit node@>=
+case save_pos_code: free_node(p, small_node_size);@+break;
+
+@ So, after these trivial initializations, what will we effectively do?
+When the following procedure will be called, we define |last_saved_xpos|,
+|last_saved_ypos|, increment |last_save_pos_number|, and a |warning|
+followed by three
+|key==value| space separated definitions as a \.{\\special}, the first
+being prefixed by the string |__PROTE_| (shall be considered a reserved
+prefix) and the string |SAVEPOS_|, equal to the index of the call, and
+the |XPOS| and |YPOS| definitions.
+
+This is obviously, from the previous description, a variation around
+|special_out|.
+
+@<Declare procedures needed in |out_what|@>=
+static void save_pos_out(pointer @!p)
+{@+int old_setting; /*holds print |selector|*/
+int @!k; /*index into |str_pool|*/
+synch_h;synch_v;incr(last_save_pos_number);
+@<Set |last_saved_xpos| and |last_saved_ypos|...@>@;
+old_setting=selector;selector=new_string;
+print("warning __PROTE_");print("SAVEPOS");print_char('=');
+print_int(last_save_pos_number);print_char(' ');
+print("XPOS");print("=");print_int(last_saved_xpos);print_char(' ');
+print("YPOS");print("=");print_int(last_saved_ypos);
+selector=old_setting;
+str_room(1); /*abort if probably overflowed and truncated*/
+dvi_out(xxx1);dvi_out(cur_length); /*it's less than 256*/
+for (k=str_start[str_ptr]; k<=pool_ptr-1; k++) dvi_out(so(str_pool[k]));
+pool_ptr=str_start[str_ptr]; /*forget the not commited tentative string*/
+}
+
+@ @<Cases for |out_what|@>=
+case save_pos_code: save_pos_out(p);@+break;
+
+@* System-dependent changes.
+This section should be replaced, if necessary, by any special
+modifications of the program
+that are necessary to make \TeX\ work at a particular installation.
+It is usually best to design your change file so that all changes to
+previous sections preserve the section numbering; then everybody's version
+will be consistent with the published program. More extensive changes,
+which introduce new sections, can be inserted here; then only the index
+itself will get a new section number.
+@^system dependencies@>
+
+@* \TeX\ Live Integration.
+A \TeX\ engine that aspires
+to become a member of the \TeX\ Live family of programs
+must
+
+$-$ respect the \TeX\ Live conventions for command line parameters,\par
+$-$ find its input files using the \.{kpathsearch} library, and\par
+$-$ implement \TeX\ primitives to support
+\LaTeX.
+
+\noindent Naturally, the functions that follow here are taken, with small
+modifications, from the \TeX\ Live sources. What is
+added here, or rather subtracted here, are the parts that are specific
+to some of the \TeX\ engines included in \TeX\ Live. New is also
+that the code is presented in literate programming style.
+
+The code that follows is organized in three parts.
+Some code for \TeX\ Live must come before
+the definition of \TeX's macros because
+it uses include files containing identifiers that are in conflict
+with \TeX's macros or modify these macros. For example
+\TeX's |banner| is modified by adding the \TeX\ Live version.
+
+@<Header files and function declarations@>=
+#ifdef WEB2CVERSION
+#define TL_VERSION "(TeX Live "WEB2CVERSION")"
+#else
+#define TL_VERSION
+#endif
+
+@ The remaining two parts are first auxiliary variables and functions and then
+those functions that are called from the ``classic'' \TeX\ code.
+
+@p @<\TeX\ Live auxiliary variables@>@;
+ @<\TeX\ Live auxiliary functions@>@;
+ @<\TeX\ Live functions@>@;
+
+@
+Most of the code that we present next comes together in the
+function |main_init|
+which is the first function called in the main program of a \TeX\ engine
+belonging to \TeX\ Live. Before doing so, we make copies of
+argument count and argument vector putting them in global variables.
+@<Global...@>=
+static char **argv;
+static int argc;
+
+@ @<\TeX\ Live functions@>=
+static void main_init(int ac, char *av[])
+{@+
+ char* main_input_file;
+ argc = ac;
+ argv = av;
+ interaction = error_stop_mode;
+ kpse_record_input = recorder_record_input;
+ kpse_record_output = recorder_record_output;
+
+ @<parse options@>@;
+ @<set the program and engine name@>@;
+ @<activate configuration lines@>@;
+ @<set the input file name@>@;
+ @<set defaults from the {\tt texmf.cfg} file@>@;
+ @<set the format name@>@;
+ @<enable the generation of input files@>@;
+}
+
+@ @<Forward declarations@>=
+static void main_init(int ac, char *av[]);
+
+
+@*1 Command Line.
+ Let's begin with the beginning: the command line.
+To see how a command line is structured, we first look at the
+help text that is displayed if the user asks for it (or if \TeX\
+decides that the user needs it). The help text is produced by the
+function |usage_help|.
+\def\SP{ }% disable visible spaces in strings.
+
+@<\TeX\ Live auxiliary functions@>=
+static void usage_help(void)
+{@+@<explain the command line@>@;
+ fprintf(stdout,"Options:\n"@,@[@<explain the options@>@]);
+ fprintf(stdout,"\nFor further information and reporting bugs see https://hint.userweb.mwn.de/\n");
+ exit(0);
+}
+
+@ The command line comes in three slightly different versions:
+
+@<explain the command line@>=
+ fprintf(stdout,@/
+ "Usage: %s [OPTION]... [TEXNAME[.tex]] [COMMANDS]\n"@/
+ " or: %s [OPTION]... \\FIRST-LINE\n"@/
+ " or: %s [OPTION]... &FMT ARGS\n\n",@/
+ argv[0],argv[0],argv[0]);@/
+ fprintf(stdout,@/
+ " Run TeX on TEXNAME, creating TEXNAME.dvi.\n"@/
+ " Any remaining COMMANDS are processed\n"@/
+ " as TeX input after TEXNAME is read.\n"@/
+ " If the first line of TEXNAME starts with %%&FMT, and FMT is\n"@/
+ " an existing .fmt file, use it. Else use `NAME.fmt', where\n"@/
+ " NAME is the program invocation name.\n"@/
+ "\n"@/
+ " Alternatively, if the first non-option argument begins\n"@/
+ " with a backslash, interpret all non-option arguments as\n"@/
+ " a line of TeX input.\n"@/
+ "\n"@/
+ " Alternatively, if the first non-option argument begins\n"@/
+ " with a &, the next word is taken as the FMT to read,\n"@/
+ " overriding all else. Any remaining arguments are\n"@/
+ " processed as above.\n"@/
+ "\n"@/
+ " If no arguments or options are specified, prompt for input.\n"@/
+ "\n");
+
+
+@*1 Options.
+Here is the list of possible options and their explanation:
+
+@<explain the options@>=
+ " -help "@/
+ @t\qquad@>"\t display this help and exit\n"@/
+ " -version "@/
+ @t\qquad@>"\t output version information and exit\n"@/
+ " -etex "@/
+ @t\qquad@>"\t enable e-TeX extensions\n"@/
+ " -ltx "@/
+ @t\qquad@>"\t enable LaTeX extensions, implies -etex\n"@/
+ " -ini "@/
+ @t\qquad@>"\t be initex for dumping formats; this is\n"@/
+ @t\qquad@>"\t\t\t also true if the program name is `kinitex'\n"@/
+ " -progname=STRING "@/
+ @t\qquad@>"\t set program (and fmt) name to STRING\n"@/
+ " -fmt=FMTNAME "@/
+ @t\qquad@>"\t use FMTNAME instead of program name or a %%& line\n"@/
+ " -output-directory=DIR "@/
+ @t\qquad@>"\t use existing DIR as the directory to write files to\n"@/
+ " -jobname=STRING "@/
+ @t\qquad@>"\t set the TeX \\jobname to STRING\n"@/
+ " [-no]-mktex=FMT "@/
+ @t\qquad@>"\t disable/enable mktexFMT generation (FMT=tex/tfm/fmt)\n"@/
+ " -interaction=STRING "@/
+ @t\qquad@>"\t set interaction mode (STRING=batchmode/\n"@/
+ @t\qquad@>"\t\t\t nonstopmode/scrollmode/errorstopmode)\n"@/
+ " -kpathsea-debug=NUMBER"@/
+ @t\qquad@>"\t set path searching debugging flags according\n"@/
+ @t\qquad@>"\t\t\t to the bits of NUMBER\n"@/
+ " -recorder"@/
+ @t\qquad@>"\t\t enable filename recorder\n"@/
+ " [-no]-parse-first-line"@/
+ @t\qquad@>"\t disable/enable parsing of the first line of\n"@/
+ @t\qquad@>"\t\t\t the input file\n"@/
+ " [-no]-file-line-error"@/
+ @t\qquad@>"\t disable/enable file:line:error style\n"@/
+ " -cnf-line=STRING"@/
+ @t\qquad@>"\t process STRING like a line in texmf.cnf\n"@/
+
+
+@ The processing of command line options is controlled by the |long_options|
+\def\SP{{\tt\char`\ }}% restore the visible space in a string
+array. Each entry in this array contains first the name of the option,
+then a flag that tells whether the option takes an argument or not.
+If next the (optional) address of a flag variable is given,
+it is followed by the value to store in the flag variable.
+In this case, setting the flag variable is handled by
+the |getopt_long_only| function.
+
+Besides the flag variables that occur in the table,
+a few string variables may be set using the options.
+The following is a complete list of these variables.
+Variables are initialized with |-1| to indicate an undefined value;
+string variables are initialized with |NULL|.
+
+@<Global variables@>=
+static int iniversion=false, etexp=false, ltxp=false, recorder_enabled=false;
+static int parsefirstlinep=-1, filelineerrorstylep=-1, interaction_option=-1;
+static const char *user_progname=NULL, *output_directory=NULL, *c_job_name=NULL,
+ *dump_name=NULL;@#
+
+@ @<\TeX\ Live auxiliary variables@>=
+static struct option long_options[] = {@/
+ { "help", 0, 0, 0 },@/
+ { "version", 0, 0, 0 },@/
+ { "interaction", 1, 0, 0 },@/
+ { "mktex", 1, 0, 0 },@/
+ { "no-mktex", 1, 0, 0 },@/
+ { "kpathsea-debug", 1, 0, 0 },@/
+ { "progname", 1, 0, 0 },@/
+ { "fmt", 1, 0, 0 },@/
+ { "output-directory", 1, 0, 0 },@/
+ { "jobname", 1, 0, 0 },@/
+ { "cnf-line", 1, 0, 0 },@/
+ { "ini", 0, &iniversion, 1 },@/
+ { "etex", 0, &etexp, 1 },@/
+ { "ltx", 0, &ltxp, 1 },@/
+ { "recorder", 0, &recorder_enabled, 1 },@/
+ { "parse-first-line", 0, &parsefirstlinep, 1 },@/
+ { "no-parse-first-line", 0, &parsefirstlinep, 0 },@/
+ { "file-line-error", 0, &filelineerrorstylep, 1 },@/
+ { "no-file-line-error", 0, &filelineerrorstylep, 0 },@/
+ @<more options@>@/
+ { 0, 0, 0, 0 }@+}@+;
+
+
+@ Parsing the command line options is accomplished with the
+|parse_options| function which in turn uses the |getopt_long_only|
+function from the \CEE/ library. This function returns 0 and sets the
+|option_index| parameter to the option found, or it returns $-1$ if
+the end of all options is reached.
+@<\TeX\ Live functions@>=
+static void parse_options (int argc, char *argv[])
+{@+ while (true) {
+ int option_index;
+ int g = getopt_long_only (argc, argv, "+", long_options, &option_index);
+ if (g==0)
+ { @<handle the options@>@;@+ }
+ else if (g == '?')
+ { fprintf(stderr,"Try '%s --help' for more information\n",argv[0]);
+ exit(1);
+ }
+ else if (g == -1) break;
+ }
+ @<Check the environment for extra settings@>@;
+}
+
+@ @<Forward declarations@>=
+static void parse_options (int argc, char *argv[]);
+
+@ Before we can call the |parse_options| function,
+we might need some special preparations for Windows.
+
+@<parse options@>=
+#if defined(WIN32)
+{@+ char* enc;
+ kpse_set_program_name (argv[0], NULL);
+ enc = kpse_var_value("command_line_encoding");
+ get_command_line_args_utf8(enc, &argc, &argv);@/
+ parse_options (argc, argv);
+ @<record {\tt texmf.cnf}@>@;
+}
+#else
+ parse_options (ac, av);
+#endif
+
+
+@ To handle the options, we compare the name at the given |option_index| with
+the different option names. This is not a very efficient method, but the
+impact is low and it's simple to write.
+
+Comparing the name of the argument with the |name| field in the |option|
+structure is done in the auxiliary function |argument_is|.
+Unfortunately the |name| field is in conflict with the |name| macro
+defined by \TeX. To avoid the conflict, the |argument_is| function
+goes just after the \.{kpathsea.h} header file that defines
+the option structure.
+
+
+@<Header files and function declarations@>=
+#include <kpathsea/kpathsea.h>
+static int argument_is(struct option *opt, char * s)
+{@+ return STREQ(opt->name, s); @+}
+#define ARGUMENT_IS(S) argument_is(long_options+option_index,S)
+
+@ Now we can handle the first two options:
+
+@<handle the options@>=
+if (ARGUMENT_IS("help")) usage_help();
+else if (ARGUMENT_IS("version")){@+
+ printf(TeX_banner@, "\n"@/
+ "Prote version "@, Prote_version_string@, "\n");
+ exit(0);@+
+}
+
+
+@ The ``interaction'' option sets the |interaction_option| variable
+based on its string argument contained in the |optarg| variable.
+If defined, the |interaction_option| will be used to set \TeX's
+|interaction| variable in the |initialize| and the |undump| functions.
+
+@<handle the options@>=
+else @+if (ARGUMENT_IS ("interaction"))@t\2@> {
+ if (STREQ (optarg, "batchmode")) interaction_option = batch_mode;
+ else if (STREQ (optarg, "nonstopmode")) interaction_option = nonstop_mode;
+ else if (STREQ (optarg, "scrollmode")) interaction_option = scroll_mode;
+ else if (STREQ (optarg, "errorstopmode")) interaction_option = error_stop_mode;
+ else WARNING1 ("Ignoring unknown argument `%s' to --interaction", optarg);
+ }
+
+@ The next two options pass the string argument to the
+\.{kpathsearch} library.
+
+@<handle the options@>=
+else @+if (ARGUMENT_IS ("mktex")) @t\2@> kpse_maketex_option (optarg, true);
+else if (ARGUMENT_IS ("no-mktex")) kpse_maketex_option (optarg, false);
+
+
+@ To debug the searching done by the \.{kpathsearch} library,
+the following option can be used.
+The argument value 3 is a good choice to start with.
+
+@<handle the options@>=
+else @+if (ARGUMENT_IS ("kpathsea-debug")) @t\2@>
+ kpathsea_debug |= atoi (optarg);
+
+
+
+@ The next set of options take a string argument
+and assign it to the corresponding string variable.
+
+@<handle the options@>=
+else @+if (ARGUMENT_IS ("progname"))@t\2@>
+ user_progname = normalize_quotes(optarg,"program name");
+else if (ARGUMENT_IS ("fmt"))
+ dump_name = normalize_quotes(optarg,"format name");
+else if (ARGUMENT_IS ("output-directory"))
+ output_directory = normalize_quotes(optarg,"output directory");
+else if (ARGUMENT_IS ("jobname"))
+ c_job_name = normalize_quotes (optarg, "job name");
+
+@ When string arguments specify files or directories,
+special care is needed if arguments are quoted and/or contain spaces.
+The function |normalize_quotes| makes sure that arguments containing
+spaces get quotes around them and it checks for unbalanced quotes.
+
+
+@<\TeX\ Live auxiliary functions@>=
+static char *normalize_quotes (const char *nom, const char *mesg)
+{@+ int quoted = false;
+ int must_quote = (strchr(nom, ' ') != NULL);
+ char *ret = xmalloc(strlen(nom)+3);/*room for two quotes and NUL*/
+ char *p=ret;
+ const char *q;
+
+ if (must_quote)
+ *p++ = '"';
+ for (q = nom; *q; q++)
+ if (*q == '"')
+ quoted = !quoted;@+
+ else
+ *p++ = *q;
+
+ if (must_quote)
+ *p++ = '"';
+ *p = '\0';
+ if (quoted) {
+ fprintf(stderr, "! Unbalanced quotes in %s %s\n", mesg, nom);
+ exit(1);
+ }
+ return ret;
+}
+
+@ If the output directory was specified on the command line,
+we save it in an environment variable so that subbrocesses can
+get the value. If on the other hand the environment specifies
+a directory and the command line does not, save the value from
+the environment to the global variable so that it is used in the
+rest of the code.
+
+@<Check the environment for extra settings@>=
+if (output_directory)
+ xputenv ("TEXMF_OUTPUT_DIRECTORY", output_directory);
+else if (getenv ("TEXMF_OUTPUT_DIRECTORY"))
+ output_directory = getenv ("TEXMF_OUTPUT_DIRECTORY");
+
+@*1 Passing a file name as a general text argument.
+
+|scan_file_name| uses the following code to parse a file name given
+as a general text argument. Such an argument can be any token list
+starting with a left brace and ending with a right brace.
+This token list is then expanded (without the leading and trailing braces)
+and printed into the string pool without making it yet an official string.
+After removing all double quotes, because this is current practice
+for \TeX\ engines that are part of \TeX\ Live,
+and setting the area and extension delimiters,
+all temporary garbage used so far is freed.
+
+Due to the expansion of the token list, this code and hence the
+|scan_file_name| procedure is recursive.
+One can provide the name of a file as the content of an other file.
+
+
+% Thierry Laronde
+@<Define a general text file name and |goto done|@>=
+{@+back_input();name_in_progress=false; /*this version is recursive...*/
+ cur_cs=input_loc; /*|scan_toks| will set |warning_index| from it*/
+ scan_general_x_text();
+ old_setting=selector;selector=new_string;
+ token_show(link(garbage));selector=old_setting;
+ @<Suppress double quotes in braced input file name@>@;
+ j=pool_ptr-1;while ((j >= str_start[str_ptr])&&(area_delimiter==0))
+ {@+
+ if ((str_pool[j]=='/')) area_delimiter=j-str_start[str_ptr];
+ if ((ext_delimiter==0)&&(str_pool[j]=='.'))
+ ext_delimiter=j-str_start[str_ptr];
+ decr(j);
+ }
+ flush_list(link(garbage));
+ goto done;
+}
+
+@ A simple loop removes the double quotes and adjusts the |pool_ptr|.
+
+@<Suppress double quotes in braced input file name@>=
+for (k=j=str_start[str_ptr]; k < pool_ptr; k++) {@+
+ if (str_pool[k]!='"') {@+str_pool[j]=str_pool[k];incr(j);}
+}
+pool_ptr=j;
+
+@*1 The {\tt -recorder} Option.
+The recorder option can be used to enable the file name recorder.
+It is crucial for getting a reliable list of files used in a given run.
+Many post-processors use it, and it is used in \TeX\ Live for
+checking the format building infrastructure.
+
+When we start the file name recorder,
+we would like to use mkstemp, but it is not portable,
+and doing the autoconfiscation (and providing fallbacks) is more
+than we want to cope with. So we have to be content with using a
+default name. We throw in the pid so at least parallel builds might
+work. Windows, however, seems to have no |pid_t|, so instead of storing the
+value returned by |getpid|, we immediately consume it.
+
+@<\TeX\ Live auxiliary functions@>=
+static char *recorder_name=NULL;
+static FILE *recorder_file=NULL;
+static void
+recorder_start(void)
+{ char *cwd;
+ char pid_str[MAX_INT_LENGTH];
+ sprintf (pid_str, "%ld", (long) getpid());
+ recorder_name = concat3(kpse_program_name, pid_str, ".fls");
+ if (output_directory) {
+ char *temp = concat3(output_directory, DIR_SEP_STRING, recorder_name);
+ free(recorder_name);
+ recorder_name = temp;
+ }
+ recorder_file = xfopen(recorder_name, FOPEN_W_MODE);
+ cwd = xgetcwd();
+ fprintf(recorder_file, "PWD %s\n", cwd);
+ free(cwd);
+}
+
+@ After we know the log file name, we have used |recorder_change_filename|
+to change the name of the recorder file to the usual thing.
+@<Forward declarations@>=
+static void recorder_change_filename (const char *new_name);
+
+@ Now its time to define this function.
+Unfortunately, we have to explicitly take
+the output directory into account, since the new name we are
+called with does not; it is just the log file name with {\tt .log}
+replaced by {\tt .fls}.
+
+@ @<\TeX\ Live auxiliary functions@>=
+static void
+recorder_change_filename (const char *new_name)
+{ char *temp = NULL;
+ if (!recorder_file)
+ return;
+#if defined(_WIN32)
+ fclose (recorder_file); /* An open file cannot be renamed. */
+#endif /* |_WIN32| */
+ if (output_directory) {
+ temp = concat3(output_directory, DIR_SEP_STRING, new_name);
+ new_name = temp;
+ }
+
+#if defined(_WIN32)
+ remove (new_name); /* A file with the |new_name| must not exist. */
+#endif /* |_WIN32| */
+ rename(recorder_name, new_name);
+ free(recorder_name);
+ recorder_name = xstrdup(new_name);
+#if defined(_WIN32)
+ recorder_file = xfopen (recorder_name, FOPEN_A_MODE); /* A closed file must be opened. */
+#endif /* |_WIN32| */
+ if (temp)
+ free (temp);
+}
+
+@ Now we are ready to record file names. The prefix INPUT is added
+to an input file and the prefix OUTPUT to an output file.
+But both functions for recording a file name use the same
+function otherwise, which on first use will start the recorder.
+
+@<\TeX\ Live auxiliary functions@>=
+static void
+recorder_record_name (const char *pfx, const char *fname)
+{ @+if (recorder_enabled) {
+ if (!recorder_file)
+ recorder_start();
+ fprintf(recorder_file, "%s %s\n", pfx, fname);
+ fflush(recorder_file);
+ }
+}
+
+
+static void
+recorder_record_input (const char *fname)
+{ recorder_record_name ("INPUT", fname);
+}
+
+static void
+recorder_record_output (const char * fname)
+{ recorder_record_name ("OUTPUT", fname);
+}
+
+@ Because input files are also recorded when writing the optional sections,
+we need the following declaration.
+
+@<Forward declarations@>=
+static void recorder_record_input (const char *fname);
+
+@ In WIN32, texmf.cnf is not recorded in
+the case of {\tt -recorder}, because |parse_options| is executed
+after the start of kpathsea due to special initializations.
+Therefore we record {\tt texmf.cnf} with the following code:
+
+@<record {\tt texmf.cnf}@>=
+if (recorder_enabled) {
+ char **p = kpse_find_file_generic ("texmf.cnf", kpse_cnf_format, 0, 1);
+ if (p && *p) {
+ char **pp = p;
+ while (*p) {
+ recorder_record_input (*p);
+ free (*p);
+ p++;
+ }
+ free (pp);
+ }
+}
+
+
+
+
+@*1 The {\tt -cnf-line} Option.
+With the \.{-cnf-line} option it is possible to specify
+a line of text as if this line were part of \TeX's configuration
+file---even taking precedence over conflicting lines in the configuration
+file. For example it is possible to change \TeX's
+\.{TEXINPUTS} variable by saying \.{--cnf-line=TEXINPUTS=/foo}.
+The configuration lines are temporarily stored in the variable
+|cnf_lines| and counted in |cnf_count| because we can send them
+to the \.{kpathsearch} library only after the library has been
+initialized sufficiently.
+
+@<Global...@>=
+static char **cnf_lines=NULL;
+static int cnf_count=0;
+
+@ @<handle the options@>=
+else @+if (ARGUMENT_IS ("cnf-line"))
+ add_cnf_line(optarg);
+
+@ The function |add_cnf_line| stores the
+given command line argument in the variable |cnf_lines|.
+
+@<\TeX\ Live auxiliary functions@>=
+static void add_cnf_line(char *arg)
+{@+ cnf_count++;
+ cnf_lines=xrealloc(cnf_lines,sizeof(char*)*cnf_count);
+ cnf_lines[cnf_count-1]=arg;
+}
+
+@ To activate the configuration lines they are passed to the
+ \.{kpathsearch} library.
+
+@<activate configuration lines@>=
+#if 1 /* this function does not exists always */
+{@+ int i;
+ for (i=0;i<cnf_count;i++)
+ kpathsea_cnf_line_env_progname (kpse_def, cnf_lines[i]);
+ free(cnf_lines);
+}
+#endif
+
+
+@*1 The Input File.
+After we are done with the options,
+we inform the \.{kpathsearch} library about the program name.
+This is an important piece of information for the library
+because the library serves quite different programs
+and its behavior can be customized for each program
+using configuration files.
+After the program and engine name is set,
+the library is ready to use.
+@<set the program and engine name@>=
+if (!user_progname)
+ user_progname = dump_name;
+#if defined(WIN32)
+ if (user_progname)
+ kpse_reset_program_name (user_progname);
+#else
+ kpse_set_program_name (argv[0], user_progname);
+#endif
+ xputenv ("engine", "texprof");@/
+
+
+@ After the options, the command line usually continues with the name of the input
+file. Getting a hold of the input file name can be quite complicated,
+but the \.{kpathsearch} library will help us to do the job.
+
+We start by looking at the first argument after the options:
+If it does not start with an
+``\.{\AM}'' and neither with a ``\.{\BS}'', it's a simple file name.
+Under Windows, however, filenames might start with a drive letter
+followed by a colon and a ``\.{\BS}'' which is used to separate
+directory names. Finally, if the filename is a quoted string, we need
+to remove the quotes before we use the \.{kpathsearch} library to find it
+and reattach the quotes afterward.
+
+@<\TeX\ Live auxiliary functions@>=
+#ifdef WIN32
+static void clean_windows_filename(char *filename)
+{@+if (strlen (filename) > 2 && isalpha (filename[0]) &&
+ filename[1] == ':' && filename[2] == '\\')
+ { char *pp;
+ for (pp = filename; *pp; pp++)
+ if (*pp == '\\')
+ *pp = '/';
+ }
+}
+#endif
+
+static char *find_file(char *fname, kpse_file_format_type t, int mx)
+{@+char *filename;
+ int final_quote=(int)strlen(fname)-1;
+ int quoted = final_quote>1 && fname[0] == '"' && fname[final_quote]=='"';
+ if (quoted) {
+ /* Overwrite last quote and skip first quote. */
+ fname[final_quote] = '\0';
+ fname++;
+ }
+ filename = kpse_find_file(fname, t, mx);
+ if (full_name_of_file!=NULL)
+ { free(full_name_of_file); full_name_of_file=NULL;}
+ if (filename!=NULL)
+ full_name_of_file=strdup(filename);
+ if (quoted) {
+ /* Undo modifications */
+ fname--;
+ fname[final_quote] = '"';
+ }
+ return filename;
+}
+
+static char *get_input_file_name (void)
+{@+
+ char *input_file_name = NULL;
+
+ if (argv[optind] && argv[optind][0] != '&' && argv[optind][0] != '\\')
+ {
+#ifdef WIN32
+ clean_windows_filename(argv[optind]);
+#endif
+ argv[optind] = normalize_quotes(argv[optind], "input file");
+ input_file_name=find_file(argv[optind],kpse_tex_format, false);
+ }
+ return input_file_name;
+}
+
+
+@ After we called |get_input_file_name|, we might need to look at |argv[argc-1]|
+in case we run under Windows.
+
+@<set the input file name@>=
+ main_input_file = get_input_file_name ();
+
+#ifdef WIN32
+ /* Were we given a simple filename? */
+ if (main_input_file == NULL) {
+ char *file_name=argv[argc-1];
+ if (file_name && file_name[0] != '-' && file_name[0] != '&' && file_name[0] != '\\') {
+ clean_windows_filename(file_name);
+ file_name = normalize_quotes(file_name, "argument");
+ main_input_file=find_file(file_name,kpse_tex_format, false);
+ argv[argc-1] = file_name;
+ }
+ }
+#endif
+
+
+@ After we have an input file, we make an attempt at filling in options
+from the {\tt texmf.cfg} file.
+
+@<set defaults from the {\tt texmf.cfg} file@>=
+ if (filelineerrorstylep < 0)
+ filelineerrorstylep = texmf_yesno ("file_line_error_style");
+ if (parsefirstlinep < 0)
+ parsefirstlinep = texmf_yesno ("parse_first_line");@/@t~@>
+
+@ We needed:
+@<\TeX\ Live auxiliary functions@>=
+static int texmf_yesno(const char *var)
+{@+
+ char *value = kpse_var_value (var);
+ return value && (*value == 't' || *value == 'y' || *value == '1');
+}
+
+@ We need a stack, matching the |line_stack| that
+contains the source file names. For the full source filenames we use
+poiters to |char| because these names are just used for output.
+
+@<Global...@>=
+static char * @!source_filename_stack0[max_in_open]={NULL}, **const @!source_filename_stack = @!source_filename_stack0-1;
+static char * @!full_source_filename_stack0[max_in_open]={NULL}, **const @!full_source_filename_stack = @!full_source_filename_stack0-1;
+static char *full_name_of_file=NULL;
+
+@ The function |print_file_line|
+prints ``file:line:error'' style messages using
+the |source_filename_stack|. If it fails to find the file name, it
+falls back to the ``non-file:line:error'' style.
+
+@<Basic printing...@>=
+static void print_file_line(void)
+{@+int level=in_open;
+ while (level>0 && full_source_filename_stack[level]==NULL) level--;
+ if (level==0) print_nl("! ");
+ else
+ { print_nl(""); print(full_source_filename_stack[level]); print_char(':');
+ if (level==in_open) print_int(line);
+ else print_int(line_stack[level]);
+ print(": ");
+ }
+}
+
+@*1 The Format File.
+Most of the time \TeX\ is not running as \.{initex}
+but it runs with a format file preloaded.
+To set the format name, we first check if the format name was given on the
+command line with an ``\.{\AM}'' prefix,
+second we might check the first line of the input file,
+and last, we check if the program is an initex program.
+
+If we still don't have a format, the program
+name is our best guess.
+There is no need to check for an extension, because the
+\.{kpathsearch} library will take care of that.
+We store the format file name in |dump_name| which is
+used in the function |w_open_in| below.
+
+@<set the format name@>=
+if (parsefirstlinep && !dump_name)
+ parse_first_line (main_input_file);
+if (!main_input_file && argv[1] && argv[1][0] == '&')
+ dump_name = argv[1] + 1;
+if (strcmp(kpse_program_name, "initexprof")==0) iniversion = true;
+if (!dump_name)
+ dump_name = kpse_program_name;
+if (!dump_name)
+{ fprintf(stderr,"Unable to determine format name\n");
+ exit(1);
+}
+if (ltxp) etexp=1;
+if (etexp && !iniversion)
+{ fprintf(stderr,"-etex and -ltx require -ini\n");
+ exit(1);
+}
+
+@ Here is the function |parse_first_line |. It searches the first line of
+the file for a \TeX\ comment of the form ``\%\AM format''\footnote{${}^1$}{The idea of using this format came from Wlodzimierz Bzyl.}.
+If found, we will use the format given there.
+
+@<\TeX\ Live auxiliary functions@>=
+static void parse_first_line(char*filename)
+{@+ FILE *f=NULL;
+ if (filename==NULL) return;
+ f = open_in(filename,kpse_tex_format,"r");
+ if (f!=NULL)
+ { char *r,*s,*t=read_line (f);
+ xfclose (f, filename);
+ if (t==NULL) return;
+ s=t;
+ if (s[0] == '%' && s[1] == '&') {
+ s = s+2;
+ while ( ISBLANK(*s)) ++s;
+ r=s;
+ while (*s!=0 && *s!=' ' && *s!='\r' && *s!='\n') s++;
+ *s=0;
+ if (dump_name==NULL) {
+ char *f_name = concat (r, format_extension);
+ char *d_name = kpse_find_file (f_name,kpse_fmt_format, false);
+ if (d_name && kpse_readable_file (d_name)) {
+ dump_name = xstrdup (r);
+ kpse_reset_program_name (dump_name);
+ }
+ free (f_name);
+ }
+ }
+ free (t);
+ }
+}
+
+@*1 Commands.
+In the old days, \TeX\ was a \PASCAL\ program, and standard \PASCAL\
+did say nothing about a command line. So \TeX\ would open the terminal
+file for input and read all the information from the terminal.
+If you don't give \TeX\ command line arguments, this is still true today.
+In our present time, people got so much used to control the behavior
+of a program using command line arguments---especially when writing
+scripts---that \TeX\ Live allows the specification of commands on
+the command line which \TeX\ would normally expect on
+the first line of its terminal input.
+
+So our next task is
+writing a function to add the remainder of the
+command line to \TeX's input buffer.
+The main job is done by the |input_add_str| function
+which duplicates part of the |input_ln| function.
+Further it skips initial spaces and replaces trailing spaces
+and line endings by a single space.
+
+@<\TeX\ Live auxiliary functions@>=
+static void input_add_char(unsigned int c)
+{@+ if (last >= max_buf_stack)
+ {@+max_buf_stack=last+1;
+ if (max_buf_stack==buf_size)
+ @<Report overflow of the input buffer, and abort@>;
+ }
+ buffer[last]=xord[c];incr(last);
+}
+
+static void input_add_str(const char *str)
+{@+ int prev_last;
+ while (*str==' ') str++;
+ prev_last=last;
+ while (*str!=0)
+
+input_add_char(*str++);
+ for (--last; last >= first ; --last)
+ { char c =buffer[last];
+ if ((c) != ' ' && (c) != '\r' && (c) != '\n') break;
+ }
+ last++;
+ if (last>prev_last) input_add_char(' ');
+}
+
+static int input_command_line(void)
+{@+
+last=first;
+while (optind < argc) input_add_str(argv[optind++]);
+loc=first;
+return (loc < last);
+@+ }
+
+@ @<Forward declarations@>=
+static int input_command_line(void);
+
+
+@*1 Opening Files.
+When we open an output file, there is usually no searching necessary.
+In the best case, we have an absolute path and can open it.
+If the path is relative, we try in this order:
+the |file_name| prefixed by the |output_directory|,
+the |file_name| as is, and
+the |file_name| prefixed with the environment variable |TEXMFOUTPUT|.
+
+If we were successful with one of the modified names, we update
+|name_of_file|.
+
+@<\TeX\ Live functions@>=
+static FILE *open_out(const char *file_name, const char *file_mode)
+{@+ FILE *f=NULL;
+ char *new_name=NULL;
+ int absolute = kpse_absolute_p(file_name, false);
+ if (absolute)
+ { f=fopen(file_name,file_mode);
+ if (f!=NULL) recorder_record_output (file_name);
+ return f;
+ }
+ if (output_directory)
+ { new_name = concat3(output_directory, DIR_SEP_STRING, file_name);
+ f = fopen(new_name,file_mode);
+ if (f==NULL) {@+ free(new_name); new_name=NULL;@+}
+ }
+ if (f==NULL)
+ f = fopen(file_name,file_mode);
+ if (f==NULL)
+ { const char *texmfoutput = kpse_var_value("TEXMFOUTPUT");
+ if (texmfoutput!=NULL && texmfoutput[0]!=0)
+ { new_name = concat3(texmfoutput, DIR_SEP_STRING, file_name);@/
+ f = fopen(new_name,file_mode);
+ if (f==NULL) {@+ free(new_name); new_name=NULL; @+}
+ }
+ }
+ if (f!=NULL && new_name!=NULL)
+ update_name_of_file(new_name,(int)strlen(new_name));
+ if (f!=NULL) recorder_record_output ((char*)name_of_file+1);
+ if (new_name!=NULL) free(new_name);
+ return f;
+}
+
+static bool a_open_out(alpha_file *f) /*open a text file for output*/
+{@+f->f=open_out((char*)name_of_file+1,"w");
+ return f->f!=NULL && ferror(f->f)==0;@+
+}
+
+static bool b_open_out(byte_file *f) /*open a binary file for output*/
+{@+f->f=open_out((char *)name_of_file+1,"wb");
+return f->f!=NULL && ferror(f->f)==0;@+
+}
+
+#ifdef @!INIT
+static bool w_open_out(word_file *f) /*open a word file for output*/
+{@+f->f=open_out((char *)name_of_file+1,"wb");
+ return f->f!=NULL && ferror(f->f)==0;@+
+}
+#endif
+
+
+@ Format file names must be scanned
+before \TeX's string mechanism has been initialized.
+The function |update_name_of_file| will set |name_of_file| from
+a \CEE/ string.
+
+We dare not give error messages here, since \TeX\ calls this routine before
+the |error| routine is ready to roll. Instead, we simply drop excess characters,
+since the error will be detected in another way when a strange file name
+isn't found.
+@^system dependencies@>
+
+@<\TeX\ Live auxiliary functions@>=
+static void update_name_of_file(const char *@!s, int @!k)
+{@+ int j;
+ if (k <= file_name_size) name_length=k;@+
+ else name_length=file_name_size;
+ for (j=0; j<name_length;j++) name_of_file[j+1]=xchr[(int)s[j]];
+ name_of_file[name_length+1]=0;
+}
+
+@ In standard \TeX, the |reset| macro is used to open input files.
+The \.{kpathsearch} library uses different search paths for
+different types of files and therefore different functions are needed to
+open these files. The common code is in the function |open_in|.
+
+@<\TeX\ Live auxiliary functions@>=
+static FILE*open_in(char*filename,kpse_file_format_type t,const char*rwb)
+{@+ char *fname=NULL;
+ FILE*f= NULL;
+ fname= find_file(filename,t,true);
+ if(fname!=NULL)
+ {@+
+ f= fopen(fname,rwb);
+ if (f!=NULL) recorder_record_input(fname);
+ if (full_name_of_file!=NULL) free(full_name_of_file);
+ full_name_of_file=fname;@+
+ }
+ return f;
+}
+
+static bool a_open_in(alpha_file *f) /*open a text file for input*/
+{@+f->f= open_in((char *)name_of_file+1,kpse_tex_format,"r");
+ if (f->f!=NULL) get(*f);
+ return f->f!=NULL && ferror(f->f)==0;
+}
+
+static bool b_open_in(byte_file *f) /*open a binary file for input*/
+{@+f->f= open_in((char *)name_of_file+1,kpse_tfm_format,"rb");
+ if (f->f!=NULL) get(*f);
+ return f->f!=NULL && ferror(f->f)==0;
+}
+
+static bool w_open_in(word_file *f) /*open a word file for input*/
+{@+
+ f->f=NULL;
+ if (name_of_file[1]!=0)
+ f->f= open_in((char*)name_of_file+1,kpse_fmt_format,"rb");
+ if (f->f!=NULL) get(*f);
+ return f->f!=NULL && ferror(f->f)==0;
+}
+
+@ \TeX's |open_fmt_file| function will call the following function
+either with the name of a format file as given with an ``\.{\AM}''
+prefix in the input or with |NULL| if no such name was specified.
+The function will try |dump_name| as a last resort before returning |NULL|.
+
+
+
+@<\TeX\ Live functions@>=
+static bool open_fmt_file(void)
+{@+int j=loc;
+ if (buffer[loc]=='&')
+ {@+incr(loc);j=loc;buffer[last]=' ';
+ while (buffer[j]!=' ') incr(j);
+ update_name_of_file((char *)buffer+loc,j-loc);
+ if (w_open_in(&fmt_file)) goto found;
+ }
+ update_name_of_file(dump_name,(int)strlen(dump_name));
+ if (w_open_in(&fmt_file)) goto found;
+ name_of_file[1]=0;
+ wake_up_terminal;
+ wterm_ln("I can't find a format file!");
+ return false;
+found: loc=j;return true;
+}
+
+
+@ The \TeX\ Live infrastructure is able to generate format files,
+font metric files,
+and even some tex files, if required.
+
+@<enable the generation of input files@>=
+ kpse_set_program_enabled (kpse_tfm_format, MAKE_TEX_TFM_BY_DEFAULT,
+ kpse_src_compile);@/
+ kpse_set_program_enabled (kpse_tex_format, MAKE_TEX_TEX_BY_DEFAULT,
+ kpse_src_compile);@/
+ kpse_set_program_enabled (kpse_fmt_format, MAKE_TEX_FMT_BY_DEFAULT,
+ kpse_src_compile);@/
+
+
+
+@*1 Date and Time.
+We conclude this chapter using \.{time.h} to provide a function
+that is used to initialize
+\TeX's date and time information. Because |time| is one of \TeX's
+macros, we add the function |tl_now| before including \TeX's macros
+to wrap the call to the |time| function.
+It sets the variable |start_time| and returns a pointer to a |tm| structure
+to be used later in |fix_date_and_time|.
+
+To support reproducible output, the environment variable |SOURCE_DATE_EPOCH|
+needs to be checked. If it is set, it is an ASCII representation of
+a UNIX timestamp, defined as the number
+of seconds, excluding leap seconds, since 01 Jan 1970 00:00:00 UTC.
+Its value is then used to initialize the |start_time| variable.
+
+The \TeX\ Live conventions further require that setting the
+|FORCE_SOURCE_DATE| environment variable to $1$ will cause also \TeX's primitives
+\.{\\year}, \.{\\month}, \.{\\day}, and \.{\\time} to use this value
+as the current time. Looking at the \TeX\ Live code also reveals that
+these primitives use the local time instead of the GMT if this variable is
+not set to~1.
+
+@<Header files and function declarations@>=
+#include <time.h>
+static time_t start_time = ((time_t)-1);
+static char *source_date_epoch,*force_source_date;
+
+#if defined(_MSC_VER) && _MSC_VER < 1800
+#define strtoull _strtoui64
+#endif
+
+static struct tm *tl_now(void)
+{@+struct tm *tp;
+ time_t t;
+ source_date_epoch= getenv("SOURCE_DATE_EPOCH");
+ force_source_date= getenv("FORCE_SOURCE_DATE");
+ if (force_source_date!=NULL &&
+ (force_source_date[0]!='1' || force_source_date[1]!=0))
+ force_source_date=NULL;
+
+ if (source_date_epoch!=NULL)
+ { start_time= (time_t)strtoull(source_date_epoch, NULL, 10);
+ if (force_source_date!=NULL)
+ t=start_time;
+ else
+ t=time(NULL);
+ }
+ else
+ t=start_time=time(NULL);
+ if (force_source_date)
+ tp=gmtime(&t);
+ else
+ tp=localtime(&t);
+ return tp;
+}
+
+@*1 Retrieving File Properties.
+To support \LaTeX, a few more time related functions are needed.
+@<Header files and function declarations@>=
+#define TIME_STR_SIZE 30
+static char time_str[TIME_STR_SIZE];
+static void get_creation_date(void);
+static void get_file_mod_date(void);
+static int get_file_size(void);
+
+#include <md5.h>
+#define DIGEST_SIZE 16
+#define FILE_BUF_SIZE 1024
+static md5_byte_t md5_digest[DIGEST_SIZE];
+
+static int get_md5_sum(int s, int file);
+
+@ The code that follows was taken from the \.{texmfmp.c} file of
+the \TeX\ Live distribution and slightly modified.
+
+
+@<\TeX\ Live auxiliary functions@>=
+
+static void
+make_time_str(time_t t, bool utc)
+{
+ struct tm lt, gmt;
+ size_t size;
+ int off, off_hours, off_mins;
+
+ /* get the time */
+ if (utc) {
+ lt = *gmtime(&t);
+ }
+ else {
+ lt = *localtime(&t);
+ }
+ size = strftime(time_str, TIME_STR_SIZE, "D:%Y%m%d%H%M%S", &lt);
+ /* expected format: |"D:YYYYmmddHHMMSS"| */
+ if (size == 0) {
+ /* unexpected, contents of |time_str| is undefined */
+ time_str[0] = '\0';
+ return;
+ }
+
+ /* correction for seconds: S can be in range 00 to 61,
+ the PDF reference expects 00 to59,
+ therefore we map |"60"| and |"61"| to |"59"| */
+ if (time_str[14] == '6') {
+ time_str[14] = '5';
+ time_str[15] = '9';
+ time_str[16] = '\0'; /* for safety */
+ }
+
+ /* get the time zone offset */
+ gmt = *gmtime(&t);
+
+ /* this calculation method was found in exim's tod.c */
+ off = 60 * (lt.tm_hour - gmt.tm_hour) + lt.tm_min - gmt.tm_min;
+ if (lt.tm_year != gmt.tm_year) {
+ off += (lt.tm_year > gmt.tm_year) ? 1440 : -1440;
+ } else if (lt.tm_yday != gmt.tm_yday) {
+ off += (lt.tm_yday > gmt.tm_yday) ? 1440 : -1440;
+ }
+
+ if (off == 0) {
+ time_str[size++] = 'Z';
+ time_str[size] = 0;
+ } else {
+ off_hours = off / 60;
+ off_mins = abs(off - off_hours * 60);
+ snprintf(&time_str[size], TIME_STR_SIZE-size, "%+03d'%02d'", off_hours, off_mins);
+ }
+}
+
+static void get_creation_date(void)
+{ make_time_str(start_time,source_date_epoch!=NULL);
+}
+
+/* static structure for file status set by |find_input_file| */
+#ifdef WIN32
+static struct _stat file_stat;
+#define GET_FILE_STAT _stat(fname,&file_stat)
+#else
+static struct stat file_stat;
+#define GET_FILE_STAT stat(fname,&file_stat)
+#endif
+
+
+static char* find_input_file(void)
+{ char *fname;
+ int r;
+ if (output_directory&&!kpse_absolute_p((char*)name_of_file0, false))
+ { int r=-1;
+ fname = concat3(output_directory, DIR_SEP_STRING, (char*)name_of_file0);
+ r=GET_FILE_STAT;
+ if (r==0)
+ return fname;
+ free(fname);
+ }
+ fname = kpse_find_tex((char*)name_of_file0);
+ if (fname!=NULL)
+ { r=GET_FILE_STAT;
+ if (r==0)
+ return fname;
+ free(fname);
+ }
+ fname=(char*)name_of_file0;
+ r=GET_FILE_STAT;
+ if (r==0)
+ return strdup(fname);
+ return NULL;
+}
+
+
+static void get_file_mod_date(void)
+{ char *fname=NULL;
+ fname= find_input_file();
+ time_str[0]=0;
+ if(fname!=NULL)
+ { make_time_str(file_stat.st_mtime,source_date_epoch!=NULL && force_source_date!=NULL);
+ free(fname);@+
+ }
+}
+
+static int get_file_size(void)
+{ int s=-1;
+ char *fname=NULL;
+ fname= find_input_file();
+ if(fname!=NULL)
+ {s=file_stat.st_size;
+ free(fname);@+
+ }
+ return s;
+}
+
+
+static int get_md5_sum(int s, int file)
+{ md5_state_t st;
+ memset(md5_digest,0,DIGEST_SIZE);
+ if (file)
+ { char *fname;
+ pack_file_name(s,empty_string,empty_string,NULL);
+ fname= find_input_file();
+ if(fname!=NULL)
+ { FILE *f;
+ f = fopen(fname, "rb");
+ if (f != NULL)
+ { int r;
+ char file_buf[FILE_BUF_SIZE];
+ recorder_record_input(fname);
+ md5_init(&st);
+ while ((r = fread(&file_buf, 1, FILE_BUF_SIZE, f)) > 0)
+ md5_append(&st, (const md5_byte_t *) file_buf, r);
+ md5_finish(&st, md5_digest);
+ fclose(f);
+ }
+ free(fname);
+ }
+ else
+ return 0;
+ }
+ else
+ { md5_init(&st);
+ md5_append(&st,(md5_byte_t *)&str_pool[str_start[s]],
+ str_start[s + 1] - str_start[s]);
+ md5_finish(&st, md5_digest);
+ }
+ return DIGEST_SIZE;
+}
+
+@* Profiling.
+If we want to find out where and how \TeX\ spends its time, we need
+a profiler. The common profilers, like {\tt gprof},
+examine the executable code of \TeX, then take some measurments at run time,
+and map this information to the functions or source lines of \TeX's code.
+
+But \TeX\ is a programming language by itself, and when we program
+in \TeX, we want to map the timing information gathered at run time
+to the macros and source lines of \TeX's input files.
+The profiler implemented in the follwing code serves this purpose.
+
+By default, profiling is initialy disabled.
+To enable profiling, you can use the {\tt -prof} option on the
+command line.
+
+@<explain the options@>=
+ " -prof "@/
+ @t\qquad@>"\t enable profiling\n"@/
+
+@ This option will set the |profile_on| variable, defined below, to true.
+
+@<more options@>=
+ { "prof", 0, &profile_on, 1 },
+
+@ @<Global variables@>=
+static int @!profile_on=false;
+
+@ To restrict profiling to selected parts of \TeX's input,
+we define two primitives for switching profiling on and off.
+
+@d profile_on_code (eTeX_last_extension_cmd_mod+3) /* `\.{\\profileon}' */
+@d profile_off_code (eTeX_last_extension_cmd_mod+4) /* `\.{\\profileoff}' */
+@d pdftex_first_extension_code profile_off_code+1
+
+@<Put each...@>=
+primitive("profileon", extension, profile_on_code);
+@!@:profileon\_}{\.{\\profileon} primitive@>
+primitive("profileoff", extension, profile_off_code);
+@!@:profileoff\_}{\.{\\profileoff} primitive@>
+
+@ @<Cases of |extension| for |print_cmd_chr|@>=
+case profile_on_code: print_esc("profileon");@+break;
+case profile_off_code: print_esc("profileoff");@+break;
+
+@ @<Cases for |do_extension|@>=
+case profile_on_code:
+if (!profile_on)
+{ profile_on=true;
+ @<record profiling on@>@;
+}
+break;
+case profile_off_code:
+if (profile_on)
+{ @<record profiling off@>@;
+ profile_on=false;
+}
+break;
+
+@*1 Files and Lines.
+The ``instructions'' that drive the \TeX\ interpreter are called ``tokens''
+and we want to keep track of the files and the lines
+where the tokens come from.
+If a token comes from an external file, then \TeX's variable |line| will
+contain the correct line number, and |cur_file| contains the current file.
+|cur_file| is an abbreviation for |input_file[index]| with
+|index| beeing a short hand for |cur_input.index_field|.
+We can not use the |index| number to identify input files,
+because \TeX\ will reuse these numbers, possibly many times, after a file
+has been closed. Instead we make a new array |input_file_num| that
+runs in parallel to |input_file| and contains a unique current file number.
+Further we make a new |read_file_num| array that runs
+in parallel to |read_file|.
+
+@d cur_file_num input_file_num[index] /*the current unique |alpha_file| number*/
+
+@<Glob...@>=
+static uint8_t @!input_file_num0[max_in_open],
+ *const @!input_file_num = @!input_file_num0-1;
+static uint8_t @! read_file_num[20];
+
+@ To implement the association between unique file numbers and full
+file names, we use the array |file_num_name|. We will store file
+numbers with 8 bits and line numbers with 16 bits in a single 32 bit
+number. We do this because we will need to store this information
+together with the tokens in a token list. This representation will
+keep the most significant byte zero, so that we can later use it for
+the ``command'' value.
+
+@<Global...@>=
+#define FILE_NUM_BITS 8
+#define MAX_FILE_NUM ((1<<FILE_NUM_BITS)-1)
+#define LINE_BITS 16
+#define MAX_LINE ((1<<LINE_BITS)-1)
+#define FILE_LINE(F,L) (((F)<<(LINE_BITS))|(L))
+#define FL_FILE(FL) ((FL)>>LINE_BITS)
+#define FL_LINE(FL) ((FL)&MAX_LINE)
+static char * @!file_num_name[MAX_FILE_NUM+1];
+static int file_from_cmd [MAX_FILE_NUM+1]; /* first command from the file */
+static int file_to_cmd [MAX_FILE_NUM+1]; /* last command from the file */
+static int file_num; /* the the last |file_num_name| allocated */
+static uint32_t cur_file_line=0; /* the file and line for |cur_tok| */
+
+@ @<check |line| for overflow@>=
+if (line>=MAX_LINE) overflow("too many input lines", MAX_LINE);
+
+@ Whenever we have a new |full_name_of_file|, we allocate
+a new unique file number.
+
+@<Allocate a file number@>=
+if (file_num>=MAX_FILE_NUM) overflow("file number",file_num);
+else file_num++;
+file_num_name[file_num]=strdup(full_name_of_file);
+file_from_cmd[file_num]=cmd_count;
+
+@ Whenever \TeX\ sets |cur_file| to a new value, we need to
+set |cur_file_num| as well.
+
+@<Set new |cur_file_num|@>=
+@<Allocate a file number@>@;
+cur_file_num=file_num;
+
+@ Whenever \TeX\ opens |read_file[n]| it needs to set the file count.
+@<Set new |read_file_num[n]|@>=
+@<Allocate a file number@>@;
+read_file_num[n]=file_num;
+
+@ A few file numbers are predefined. Some tokens, for instance those
+that are loaded from a format file, have an unknown origin and are
+classified as comming from an |unknown_file|.
+We use |system_file| to assign time intervals to
+the system and use the command field to distinguish between different
+system events. There might also be tokens from the terminal, either
+typed in interactively (which will messup your timing values)
+or from the command line.
+
+For the commands from the |system_file|, we use
+special line numbers to distinguish various cases.
+
+@d unknown_file 0 /*tokens with unknown origin, usualy from a format file*/
+@d system_file 1 /*tokens generated by the system*/
+@d terminal_file 2 /*tokens read from the terminal*/
+@d system_unknown 0 /*should not happen*/
+@d system_start 1 /*startup of \TeX*/
+@d system_end 2 /*end of \TeX*/
+@d system_ship_out 3 /*time spent in |ship_out()|*/
+@d system_line_break 4 /*time spent in |line_break()|*/
+@d system_init_trie 5 /*time spent in |line_break()|*/
+@d system_build_page 6 /*time spent in |line_break()|*/
+@d system_input_ln 7 /*time spent in |line_break()|*/
+@d system_insert 8 /*time spent on tokens inserted by \TeX*/
+
+
+@<Set init...@>=
+file_num_name[unknown_file]="unknown";
+file_num_name[system_file]="system";
+file_num_name[terminal_file]="terminal";
+file_num=terminal_file;
+
+@ When \TeX\ reads a token from an external file, it has to set
+|cur_file_line| based on the information in |cur_input|.
+This code is parallel to the code that is used to read the next line:
+
+@<Set |cur_file_line| based on the information in |cur_input|@>=
+if (terminal_input)
+ cur_file_line=FILE_LINE(terminal_file,0);
+else if (name<=19)
+ cur_file_line=FILE_LINE(read_file_num[name-1],0); /* do I need the line? */
+else
+ cur_file_line=FILE_LINE(cur_file_num,line);
+
+@ When we end a \.{\\read} line, we set the current file and line.
+@<Set |cur_file_line| when a \.{\\read} line ends@>=
+ cur_file_line=FILE_LINE(read_file_num[name-1],0);
+
+
+@ As described above, we store file and line numbers as 32 bit integers.
+When reading input from external files, we know the file and the line;
+but for token lists we need to store this information together
+with the tokens, when we create the token list.
+As a simple solution, we allocate a 32 bit integer for all pointers.
+
+Whenever in \TeX's code a new token is generated, we know what file and
+line is responsible for what is going on. The newly generated token
+will then point to the same file and line.
+
+@<Glob...@>=
+static uint32_t @!fl_mem0[mem_max-mem_min+1],
+ *const @!fl_mem = @!fl_mem0-mem_min; /*the big dynamic file/line storage*/
+
+@*1 Timing.
+When the interpreter of \TeX\ executes a command, we need to measure the
+time needed to execute it and record that information in a file.
+We call such a measurement a ``time stamp'' or ``stamp''
+for short.
+This process should be quick, in order not to disturb the measurements
+too much by the time needed for the measurement itself and its recording.
+So we keep the data in a large array, and postpone writing the output file
+until \TeX\ terminates.
+
+Now let us consider the measurement of such a time stamp.
+As a general rule, the start of a new time interval is the end
+of the previous time interval. There is only a single look up
+of the current time for both. So all time intervals will add up
+to the total run time.
+
+We keep the start time in two variables |start_sec| and |start_nsec|;
+the first counts the seconds, the other (roughly) the nano seconds.
+When we measure the end time, we should know the previous start time,
+and which command was executed and from which file and line this command
+was taken. The measured end time is the start time for the next command.
+
+
+@<Global variables@>=
+static long start_nsec, start_sec, diff_nsec;
+struct timespec ts;
+static int time_error;
+
+@ To get timing information, we use the POSIX function |clock_gettime|.
+Depending on the operating system there are many different variations.
+The timer used for timing can be selected by defining |GETTIME| at
+compile time. If |GETTIME| is not set, we try to find a good default.
+Currently we use |timespec_get| under Windows and |clock_gettime|
+with a tread specific clock otherwise.
+
+@<get current time@>=
+#ifdef GETTIME
+#if GETTIME==0
+ time_error=clock_gettime(CLOCK_THREAD_CPUTIME_ID, &ts);
+#elif GETTIME==1
+ time_error=clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &ts);
+#elif GETTIME==2
+ time_error=clock_gettime(CLOCK_MONOTONIC_RAW, &ts);
+#elif GETTIME==3
+ time_error=clock_gettime(CLOCK_MONOTONIC, &ts);
+#else
+ time_error=clock_gettime(CLOCK_THREAD_CPUTIME_ID, &ts);
+#endif
+#else
+/* guess a default */
+#ifdef _WIN32
+ time_error=timespec_get(&ts, TIME_UTC);
+#else
+ time_error=clock_gettime(CLOCK_THREAD_CPUTIME_ID, &ts);
+#endif
+#endif
+
+
+@ To record the timing information, we compute the time difference in
+nano seconds, assuming that it fits into unsigned 32 bit which means
+the difference should be less than about 4 seconds.
+
+@<compute elapsed time@>=
+ diff_nsec= (ts.tv_sec-start_sec)*1000000000+ (ts.tv_nsec-start_nsec);
+#if 0
+ printf("time: %ld %ld %ld\n", diff_nsec, (ts.tv_sec), (ts.tv_nsec));
+#endif
+
+ start_nsec=ts.tv_nsec;
+ start_sec=ts.tv_sec;
+
+@*1 Commands.
+The fourth piece of information that we store with file, line, and time
+is the command that \TeX\ is executing.
+The elapsed time will fit into an four byte unsigned integer, and
+luckily also the command, the file and the line will do as well.
+When starting a new time interval, often we will not yet know the
+next command, its file, and its line.
+We keep the command, the file, the line, in a set of variables starting with
+|prof_|. These variables my change within a time interval, and the final
+values are written when the end time is known.
+There are also variables related to macro calls which are explainded later
+but are already defined here because we will initialize them together
+with the other variables.
+
+@<Global variables@>=
+#define MAX_STAMPS 80000000
+#define MAX_MACRO_STACK 40000
+#define POP_BIT 0x80
+
+static uint32_t stamp[MAX_STAMPS]; /*one chunk of profiling information*/
+static uint32_t st_count=0; /*the next available entry in the array above*/
+static uint32_t cmd_count=0; /*the number of time stamps in the array above*/
+static int prof_cmd=0; /*the command to be written */
+static uint32_t prof_file_line=0; /*the file and line to be written */
+
+@ The code below initializes the variables. Note that we use the special
+command value |system_cmd|. The ``real'' command values
+are those that occur in the |big_switch| ranging from 0 (|escape|)
+to 100 (|max_command|), the values that are used when entering
+the |main_loop|(|letter|, |other_char|, |char_given|, and |char_num|)
+are a subset of these.
+The pseudo command |system_cmd| is just outside of this range.
+A few more pseudo commands are needed below and we define them here.
+
+@d system_cmd (max_command+1) /*pseudo command value*/
+@d system_profile_on (system_cmd+1)
+@d system_profile_off (system_cmd+2)
+@d system_macro_push (system_cmd+3)
+@d system_macro_pop (system_cmd+4)
+@d system_macro_pop_small (system_cmd+5)
+@d system_macro_pop_0 (system_cmd+6)
+
+
+@<Initialize profiling@>=
+ @<Initialize the macro call stack@>@;
+ prof_cmd=system_cmd;
+ prof_file_line=FILE_LINE(system_file,system_start);
+ @<record macro call information if necessary@>@;
+#if 0
+ clock_getres(CLOCK_MONOTONIC, &ts);
+ printf("CLOCK_MONOTONIC: %ld %ld\n",ts.tv_sec,ts.tv_nsec);
+
+ clock_getres(CLOCK_MONOTONIC_RAW, &ts);
+ printf("CLOCK_MONOTONIC_RAW: %ld %ld\n",ts.tv_sec,ts.tv_nsec);
+
+ clock_getres(CLOCK_PROCESS_CPUTIME_ID, &ts);
+ printf("CLOCK_PROCESS_CPUTIME_ID: %ld %ld\n",ts.tv_sec,ts.tv_nsec);
+
+ clock_getres(CLOCK_THREAD_CPUTIME_ID, &ts);
+ printf("CLOCK_THREAD_CPUTIME_ID: %ld %ld\n",ts.tv_sec,ts.tv_nsec);
+#endif
+ @<get current time@>@;
+ diff_nsec=0;
+ start_nsec=ts.tv_nsec;
+ start_sec=ts.tv_sec;
+
+@ At the end of \TeX\ we check |last_depth| and clean the remaining
+macro stack if necessary. We end the record always with recording
+timing information.
+
+@<pop the macro call stack at the end of \TeX@>=
+prof_cmd=cur_cmd; /* the stop command */
+prof_file_line=cur_file_line;
+prof_depth=0;
+profile_on=true;
+@<get current time@>@; /* advance the time */
+@<compute elapsed time@>@;
+@<record timing information@>@; /* pop to |prof_depth| */
+
+
+@ When we record the timing information,
+we first store command, file, and line, and then store
+the elapsed time.
+
+@d SHOW_RECORD_TIMING 0
+
+@<record timing information@>=
+if (profile_on)
+{ if (st_count+3>MAX_STAMPS)
+ overflow("profile data",MAX_STAMPS);
+ @<record poping the macro stack if necessary@>@;
+ stamp[st_count++]= (prof_cmd<<24) | prof_file_line;
+ prof_cmd=prof_cmd&~POP_BIT; /*remove a possible |POP_BIT|*/
+#if SHOW_RECORD_TIMING
+ print_char('<');print_int(cmd_count);
+ print_char(':');
+ if (prof_cmd&POP_BIT) print_char('-');
+ print_int(prof_cmd&~POP_BIT);
+ print_char(':');print_int(prof_depth);
+ print_char('>');print_ln();
+#endif
+ cmd_count++;
+ @<get current time@>@;
+ @<compute elapsed time@>@;
+ stamp[st_count++]=diff_nsec;
+}
+
+
+@ To mark the execution of starting and ending profiling
+we use the system commands for the start and the end of profiling.
+
+After switching profiling off, no more data is written to the output
+file. So we have to record the macro call and timing information
+explicitly before switching the profiling off.
+
+@<record profiling off@>=
+ prof_file_line=cur_file_line;
+ prof_cmd=system_profile_off;
+ prof_depth=cur_depth;
+ @<record macro call information if necessary@>@;
+ @<record timing information@>@;
+
+@ When switching profiling on, we have to syncronize the start time
+with the current time and prepare everything for the recording
+of the timing before entering the main loop again.
+Setting the command, file and line information is simple.
+Since we can not assume that the macro call stack is unchanged,
+we record the necessary changes.
+
+@<record profiling on@>=
+ @<get current time@>@;
+ diff_nsec=0;
+ start_nsec=ts.tv_nsec;
+ start_sec=ts.tv_sec;
+ prof_file_line=cur_file_line;
+ prof_cmd=system_profile_on;
+ prof_depth=cur_depth;
+ @<record macro call information if necessary@>@;
+
+@ At the end of \TeX\ we record the timing for the |stop| command
+and pop the macro call stack.
+
+@<record the end of \TeX@>=
+@<pop the macro call stack at the end of \TeX@>@;
+
+@ There are two places where we regularly record the next command.
+The first one is when \TeX\ enters the big switch. Here
+we record the timing information before reading the next expanded token
+and set the current file, line, and command after reading the
+next expanded token.
+The other place is at the start of the main loop. Here both
+actions are done one after the other. Whenever we assiciate
+a command with the current time slot by setting |prof_cmd| and
+the profile data associated with it, we need to make sure that
+changes of entries in the macro call are reported to the profiler.
+
+@<set current file, line, and command for the current time slot@>=
+if (profile_on)
+{ prof_cmd=cur_cmd;
+ prof_depth=cur_depth;
+ prof_file_line=cur_file_line;
+ @<record macro call information if necessary@>@;
+}
+
+@ Some time expensive routines, |line_break| and |ship_out|, consume
+time that depends entirely on the output generated by \TeX\ but
+not on how that output is generated. So we charge the time spent in these
+routines on the system.
+
+@<Local variables to save the profiling context@>=
+int tmp_cmd;
+int tmp_file_line;
+int tmp_depth;
+
+@ @<Charge the time used here on |line_break|@>=
+if (profile_on)
+{ @<record timing information@>@;
+ tmp_file_line=prof_file_line;
+ tmp_cmd=prof_cmd;
+ tmp_depth=prof_depth;
+ prof_file_line=FILE_LINE(system_file,system_line_break);
+ prof_cmd=system_cmd;
+}
+
+
+@ @<restore the previous current file, line, and command@>=
+if (profile_on)
+{ @<record timing information@>@;
+ prof_file_line=tmp_file_line;
+ prof_cmd=tmp_cmd;
+ prof_depth=tmp_depth;
+}
+
+
+
+@ @<Charge the time used here on |init_trie|@>=
+if (profile_on)
+{ @<record timing information@>@;
+ tmp_file_line=prof_file_line;
+ tmp_cmd=prof_cmd;
+ tmp_depth=prof_depth;
+ prof_file_line=FILE_LINE(system_file,system_init_trie);
+ prof_cmd=system_cmd;
+}
+
+
+@ @<Charge the time used here on |build_page|@>=
+if (profile_on)
+{ @<record timing information@>@;
+ tmp_file_line=prof_file_line;
+ tmp_cmd=prof_cmd;
+ tmp_depth=prof_depth;
+ prof_file_line=FILE_LINE(system_file,system_build_page);
+ prof_cmd=system_cmd;
+}
+
+
+
+@ @<Charge the time used here on |ship_out|@>=
+if (profile_on)
+{ @<record timing information@>@;
+ tmp_file_line=prof_file_line;
+ tmp_cmd=prof_cmd;
+ tmp_depth=prof_depth;
+ prof_file_line=FILE_LINE(system_file,system_ship_out);
+ prof_cmd=system_cmd;
+}
+
+
+@*1 Macro Calls.
+Let's talk about collecting information about macro calls.
+Macro calls come in various flavours:
+There are active characters and single letter control sequences;
+both have their own region in the |eqtb| table of equivalents.
+Then there is an empty control sequence, and last there
+are lots of (currently up to 45000) multiletter control sequences.
+The plain format defines about 1000 and the \LaTeX\ format about 23000
+of them. When |get_next| finds a control sequence, it sets |cur_cs|
+to the index in the table of equivalents.
+It will further set |cur_cmd=eq_type(cur_cs)|, |cur_chr=equiv(cur_cs)|,
+and |cur_tok=cs_token_flag+cur_cs|.
+
+The |get_x_token| or |expand| procedure will then notice the macro call
+and invoke |macro_call|.
+|macro_call| will inspect the call site and the parameter section
+of the macro and put the actual macro parameters on the parameter
+stack before pushing the macro body on the input stack.
+
+But wait! |macro_call| will apply a technique called last call optimization
+before doing any of the above: It will remove all entries from
+the input stack that have no more tokens left for processing.
+This allows tail recursive macro calls in constant stack space and
+makes long recursive loops possible.
+For example building the \LaTeX\ format uses a loop to read the
+definition of UTF8 codes.
+This loop has more than 35000 iterations and a nesting depth
+of the same size. With last call optimization, the loop uses less than 3000
+entries on the input stack.
+
+Popping the stack early makes it slightly more difficult
+to track who is calling whom---an information that we need if we want
+to give cumulative timing for a macro call. Cumulative timing is the time spent
+during that macro call including all the calls to sub-macros.
+
+The timing information that we collected in the previous section
+assigned the time caused by the execution of \TeX's commands to the
+specific line and file that caused the execution of that command.
+As a consequnce, the time used for the expansion of a macro call
+is assigned to the file and line that contains the call site of the macro.
+The time that is spent on executing the commands in the macros body
+are assigned to the file and line containing the macro definition.
+But there is no information on the call graph that allows us to
+classify one command as comming from a lower level of expansion than
+the other.
+
+In this section, we want to assign the time used for the execution
+of commands to a macro definition, with its name and the file and line
+that contains the definition of the macro.
+While the name of a macro is an important information, it is not
+sufficient to relate the time to the macros name, because
+the same name can be used for several different macros at various times.
+
+So we collect for a macro call the following data:
+
+\yskip\textindent{1)}The index |cur_cs| into the table of equivalents
+|eqtb|. Using |cur_cs| it is possible to access the macros name. For
+active characters and single letter control sequences, we obtain the
+character by subtracting |active_base| or |single_base| from |cur_cs|.
+For multi-letter control sequences, we find the index to |str_start|
+for the macro name in |text(cur_cs)|, the text field of the corresponding
+hash table entry. The association between |cur_cs| and the macro name
+is stored in a separate section of the profilers output file.
+
+\yskip\textindent{2)}The line and file of the macro definition.
+We store this information in the |fl_mem| array using |equiv(cur_cs)|,
+the pointer to the reference count token of the macro.
+
+\yskip\textindent{3)}The ``unoptimized'' nesting depth of the macro call.
+
+We store this information in a 64 bit integer and define macros
+to assemble and disassemble this integer.
+From this information it is possible to reconstruct the call graph
+and compute cumulative timing information for macro calls.
+We keep the information for the whole ``unoptimized'' macro stack
+in the |macro_stack|.
+The varable |prof_depth| tells us the nesting depth of |prof_cmd|.
+The variable |last_depth| tells us the nesting depth
+of the last recorded time stamp and the variable |unchanged_depth|
+tells us which part of the macro stack is unchanged since then.
+
+
+@d CALL_DEPTH(A) ((A)>>48)
+@d CALL_EQTB(A) (((A)>>32)&0xFFFF)
+@d CALL_FILE(A) (((A)>>16)&0xFF)
+@d CALL_LINE(A) ((A)&0xFFFF)
+@d CALL_DE(A) ((A)>>32)
+@d CALL_CFL(A) ((A)&0xFFFFFFFF)
+@d CALL_INFO(D,C,FL) (((uint64_t)(D)<<48)|((uint64_t)(C)<<32)|
+ (system_macro_push<<24)|FL)
+
+@<Global variables@>=
+static uint64_t macro_stack[MAX_MACRO_STACK]={0}; /* the macro calls */
+static int prof_depth; /* the nesting depth of macros */
+static int last_depth; /* the nesting depth last report1ed*/
+static int unchanged_depth; /* up to here no change on |macro_stack|*/
+static int prof_max_depth;
+
+@ @<Initialize the macro call stack@>=
+{ int macro_depth=-1, macro_cs=0;
+ uint32_t macro_fl=FILE_LINE(terminal_file,0);
+ @<update the macro stack@>@;
+ cmd_count++;
+ stamp[st_count++]=CALL_CFL(macro_stack[0]);
+ stamp[st_count++]=CALL_DE(macro_stack[0]);
+ last_depth=unchanged_depth=prof_depth=0;
+}
+
+@ We maintain the macro stack in the |macro_call| procedure
+by adding local variables that capture the information of
+the call token that cause the invocation of |macro_call|.
+
+@<additional local variables for |macro_call|@>=
+uint32_t macro_fl=fl_mem[cur_chr];
+int macro_cs=cur_cs;
+int macro_depth=cur_depth;
+
+@ At the end of |macro_call|, we use this information to
+update the |macro_stack| and related variables.
+
+@<update the macro stack@>=
+macro_depth++;
+cur_depth=macro_depth;
+if (macro_depth>=MAX_MACRO_STACK)
+ overflow("macro stack size", macro_depth);
+macro_stack[macro_depth]=CALL_INFO(macro_depth,macro_cs,macro_fl);
+if (macro_depth<=unchanged_depth)
+ unchanged_depth=macro_depth-1;
+
+@ We add files as part of the macro stack using the following code:
+@<additional local variables for |start_input|@>=
+uint32_t macro_fl=FILE_LINE(cur_file_num,0);
+int macro_cs=0;
+int macro_depth=cur_depth;
+
+@ We add calls to the output routine and hook macros like \.{\\everypar}, \.{\\everymath}, etc. to the macro stack using tis code:
+@<additional local variables for |begin_token_list|@>=
+uint32_t macro_fl=fl_mem[p];
+int macro_cs=undefined_control_sequence+t;
+/*|undefined_control_sequence| is the last pointer in the hash table*/
+int macro_depth=cur_depth;
+
+
+@ As we have seen above, recording timing information consists
+of two steps:
+In the first step we set global variables like |prof_cmd| and |prof_depth|
+based on the current command. Then we wait until the command is executed,
+and in the second step we compute the elapsed time and store the
+information from the first step together with the time in the |stamp|
+array.
+Now we augment this process and store macro call related information.
+
+The key value that guides this process is |prof_depth|:
+the macro nesting depth associated with |prof_cmd|.
+Using it we can get from the |macro_stack| information about
+all currently active macros at lower nesting levels.
+Two more variables |last_depth| and |unchanged_depth| are related to it:
+After recording macro call information for a command,
+we set |last_depth=unchanged_depth=prof_depth|.
+The next time we need to record macro call information,
+|unchanged_depth| and |prof_depth| may have changed, but
+|last_depth| should be unchanged. Because |unchanged_depth|
+can only decrease, we know that $|unchanged_depth|\le|last_depth|$.
+On the other hand, |prof_depth| can increase as well as decrease.
+This gives us the following cases:
+
+\yskip\textindent{$|unchanged_depth|<|prof_depth|$}
+Record the new stack entries from $|unchanged_depth|+1$ up to |prof_depth|.
+
+\yskip\textindent{$|prof_depth|=|unchanged_depth|=|last_depth|$:}
+No information on macro calls is necessary.
+
+\yskip\textindent{$|prof_depth|<|unchanged_depth|=|last_depth|$:}
+Record that the stack was popped from |last_depth| down to |prof_depth|.
+
+\yskip\textindent{$|prof_depth|\le|unchanged_depth|<|last_depth|$:}
+Record that the stack was popped from |last_depth| down to |prof_depth|.
+
+
+The common case that $|prof_dept|=|last_dept|-1$ asks for a more
+compact representation and we use the most significant bit of the
+command value to encode this case.
+Note that we count this as a separate command.
+
+It would be nice if we could wait with recording the stack changes until
+the command is completely executed. The command however might read
+parameters which causes pops and pushes on the macro stack. So by the
+time the command has finished, the stack might have changed considerably.
+There is a further complication: the execusion of some commands does
+not end with a jump to |big_switch| but with a jump to |big_reswitch|.
+This will set a new value for |prof_cmd| and so will invalidate all our
+preparations.
+For this reason we need to make the recording of macro call information
+idempotent. Recording several pops and pushes explicitly does not harm
+(except for extra output) but setting the pop bit asks for trouble.
+Therefore we postpone the recording of poping the macro stack.
+
+
+@<record macro call information if necessary@>=
+if (unchanged_depth<prof_depth)
+ @<record the new stack entries@>@;
+
+
+@ @<record poping the macro stack if necessary@>=
+if (last_depth>prof_depth)
+{ if (last_depth==prof_depth+1)
+ prof_cmd=prof_cmd|POP_BIT;
+ else
+ stamp[st_count++]=(system_macro_pop<<24)| (last_depth-prof_depth);
+#if SHOW_RECORD_TIMING
+ print_char('{');print_int(cmd_count);
+ print_char(':');print_int(system_macro_pop);
+ print_char(':');print_int(last_depth);
+ print_char('>');print_int(prof_depth);
+ print_char('}');print_ln();
+#endif
+ cmd_count++;
+ last_depth=unchanged_depth=prof_depth;
+}
+
+
+@ The information about new stack entries are taken from the |macro_stack|.
+
+@<record the new stack entries@>=
+{ int i;
+ if (last_depth>unchanged_depth)
+ {
+#if SHOW_RECORD_TIMING
+ print_char('{');print_int(cmd_count);
+ print_char(':');print_int(system_macro_pop);
+ print_char(':');print_int(last_depth);
+ print_char('>');print_int(unchanged_depth);
+ print_char('}');print_ln();
+#endif
+ cmd_count++;
+ }
+ if (st_count+2*(prof_depth-unchanged_depth)>MAX_STAMPS)
+ overflow("profile data",MAX_STAMPS);
+ for (i=unchanged_depth+1;i<=prof_depth;i++)
+ {
+#if SHOW_RECORD_TIMING
+ print_char('[');print_int(cmd_count);
+ print_char(':');print_int(i-1);
+ print_char('<');print_int(CALL_DEPTH(macro_stack[i]));
+ print_char(':'); print_cs(CALL_EQTB(macro_stack[i]));
+ print_char(']');print_ln();
+#endif
+ cmd_count++;
+ stamp[st_count++]=CALL_CFL(macro_stack[i]);
+ stamp[st_count++]=CALL_DE(macro_stack[i]);
+ }
+ last_depth=unchanged_depth=prof_depth;
+ if (prof_depth>prof_max_depth) prof_max_depth=prof_depth;
+}
+
+
+@*1 Output the profile data.
+Here is the code that outputs the results.
+The format tries to be simple but flexible.
+The output file has four parts: size data, file names, macro names,
+and time stamps. To enable some error checking, the three file
+parts start and end with an output marker.
+
+% we have typically from 1000 to 5000, in exteme cases up to 30000,
+% file/line entries
+% cut -d : -f 2,3 tmon.out | fgrep -v '.' | sort |uniq | wc -l
+
+@<Finish the extensions@>=
+{ FILE *prof;
+ pack_job_name(".tprof");
+ prof=fopen((char*)name_of_file0,"wb");
+ if (prof==NULL) print_err("Unable to write the profile data\n");
+ else {
+ int i;
+ prof_file_line=cur_file_line;
+ @<output marker@>@;
+ @<output size data@>@;
+ @<output marker@>@;
+ @<output file names@>@;
+ @<output marker@>@;
+ @<output macro names@>@;
+ @<output marker@>@;
+ @<output timing data@>@>
+ @<output marker@>@;
+ fclose(prof);
+ }
+}
+
+@ The output marker consists of eight byte: the ASCII codes of ``TEX PROF''.
+
+@<output marker@>=
+ fputs("TEX PROF",prof);
+
+
+@ The information necessary to process the file comes after the first
+marker. Here and in the following all numbers are written out in
+``big-endian byte order''. We provide the following information:
+
+The number of files (2 byte),
+the number of byte used for filenames
+including the zero bytes (2 byte),
+the number of time stamps (4 byte).
+The number of macro names (2 byte),
+the number of bytes used for macro names (4 byte),
+again including the zero bytes,
+and the maximum nesting depth used on the |macro_stack|.
+
+We start with the macros to write multibyte integers in bigendian order.
+
+@d PUT1(N) fputc((N)&0xFF,prof)
+@d PUT2(N) PUT1((N)>>8),PUT1(N)
+@d PUT4(N) PUT2((N)>>16),PUT2(N)
+
+
+@<output size data@>=
+ PUT2(file_num+1); /* the number of files */
+{ int n, m;
+ m=0;
+ for (i=0; i<=file_num; i++)
+ { n=strlen(file_num_name[i]);
+ m+=n+1;
+ }
+ PUT2(m); /* the number of byte used for file names */
+}
+ PUT4(cmd_count); /* the number of commands */
+{ int k, m;
+ m=k=0;
+ for (i=hash_base; i<undefined_control_sequence; i++)
+ if (text(i)!=0)
+ { k++;
+ m+=(str_start[text(i)+1]-str_start[text(i)])+1;
+ }
+ PUT2(k); /* the number of macro names */
+ PUT4(m); /* the number of byte for the macro names */
+ PUT2(prof_max_depth); /*the maximum macro nesting depth*/
+}
+
+
+@ Writing the file names is simple.
+
+@<output file names@>=
+for (i=0; i<=file_num; i++)
+ { fputs(file_num_name[i], prof);
+ PUT1(0);
+ }
+
+@ Here is the code to write the macro names to the output file.
+It traverses the |hash_table| from |hash_base| up to
+|undefined_control_sequence|. Each macro name is preceeded by
+its index in the hash table stored in two byte.
+
+@<output macro names@>=
+for (i=hash_base; i<undefined_control_sequence; i++)
+ if (text(i)!=0)
+ { int k;
+ PUT2(i);
+ for (k=str_start[text(i)];k<str_start[text(i)+1];k++)
+ PUT1(str_pool[k]);
+ PUT1(0);
+ }
+
+
+@ Next we output the time stamps. We use a variable format
+depending on the command value which is always in the top byte
+of the stamp. We optimize the output size slightly:
+For macro calls the |stamp| array contains the depth values.
+It is of course more efficient to write the relative changes
+into the output instead of the absolute values.
+A most common case is poping the stack by one. This is recorded
+in the |stamp| array by setting the |POP_BIT| in the command value
+and we simply keep this bit. Small relative values $n$ in the range
+$0 \le n \le 10$ are encoded using the command value
+$|system_macro_pop_0|+n$. Values in the range $11 \le n \le 255$
+encoded as single byte values following the commmand code
+|system_macro_pop_small|. All other values are encoded using
+two byte following the command code |system_macro_pop|.
+Encoding the pops separate from the calls, we need no longer
+store the depth values with the calls.
+
+@<encode pop $n$@>=
+#if SHOW_WRITE_TIMING
+ printf("!%d:%d:%d>%d\n",j++,i,last_depth,last_depth-n);
+#endif
+ if (n<=10)
+ PUT1(system_macro_pop_0+n);
+ else if (n<=255)
+ { PUT1(system_macro_pop_small);
+ PUT1(n);
+ }
+ else
+ { PUT1(system_macro_pop);
+ PUT2(n);
+ }
+ last_depth=last_depth-n;
+
+@ Now we can output the timing data.
+
+@d SHOW_WRITE_TIMING 0
+
+@<output timing data@>=
+
+ i=0;
+{ int j=0;
+ last_depth=-1;
+ while (i< st_count)
+ { int8_t c = stamp[i]>>24;
+ if (c==system_macro_pop)
+ { int n=stamp[i]&0xFFFF;
+ @<encode pop $n$@>@;
+ i++;
+ }
+ else if (c==system_macro_push)
+ { int d=stamp[i+1]>>16;
+ if (d<=last_depth)
+ { int n=last_depth-d+1;
+ @<encode pop $n$@>@;
+ /* here I could optimize the case $n=1$ by setting the |POP_BIT|
+ in |system_macro_push|*/
+ }
+#if SHOW_WRITE_TIMING
+ print_char('!');print_int(j++);
+ print_char(':');print_int(i);
+ print_char(':'); print_int(system_macro_push);
+ print_char(':');print_int(d);
+ print_char(':'); print_cs(stamp[i+1]&0xffff);
+ print_char(']');print_ln();
+#endif
+ PUT4(stamp[i]); i++;
+ PUT2(stamp[i]); i++;
+ last_depth=d;
+ }
+ else
+ {
+ if (c&POP_BIT)
+ { last_depth--;
+#if SHOW_WRITE_TIMING
+ printf("!%d:%d:%d>%d}\n",j++,i,last_depth+1,last_depth);
+#endif
+ }
+#if SHOW_WRITE_TIMING
+ printf("!%d:%d:%d:%d>\n",j++,i,stamp[i]>>24,last_depth);
+#endif
+ PUT4(stamp[i]);i++;
+ PUT4(stamp[i]);i++;
+ }
+ }
+}
+
+@*1 Dumping the profiling information.
+
+We dump the number of files first and then dump
+the file names including the
+terminating zero byte.
+We put eight byte into a memory word and dump them together.
+The last memory word is padded with zeros.
+
+@<Dump the full file names@>=
+{ two_halves hw;
+ int i,j;
+ hw.rh=file_num;
+ i=0;
+ for (k=0; k<=file_num; k++)
+ { j=strlen(file_num_name[k]);
+ if (j>i) i=j;
+ }
+ hw.lh=i;
+ dump_hh(hw);
+}
+{ memory_word m;
+ int word_count=0;
+ int byte_count=0;
+ m.w=0;
+ for (k=0;k<=file_num;k++)
+ { char *s=file_num_name[k];
+ do
+ { m.w=(m.w<<8)|*s;
+ byte_count++;
+ if (byte_count==8)
+ { dump_wd(m); word_count++;
+ m.w=0; byte_count=0;
+ }
+ } while (*s++!=0);
+ }
+ if (byte_count >0)
+ { while (byte_count<8)
+ { m.w=(m.w<<8)|0;
+ byte_count++;
+ }
+ dump_wd(m);word_count++;
+ }
+ print_ln();print_int(file_num);
+ print(" file names dumped; total size "); print_int(word_count*8);
+}
+
+@ @<Undump the full file names@>=
+
+{ two_halves hw;
+ memory_word m;
+ int byte_count=0;
+ int i,j;
+ char *buf;
+ undump_hh(hw);
+ file_num=hw.rh;
+ if (file_num>MAX_FILE_NUM) overflow("file number",file_num);
+ i=hw.lh;
+ buf=malloc(i+1);
+ if (buf==NULL)
+ { print_err("Out of memory while reading filenames from format file");
+ succumb;
+ }
+ for (k=0;k<=file_num;k++)
+ { char *s=buf;
+ do
+ { if (byte_count==0)
+ { undump_wd(m); byte_count=8; }
+ *s=(m.w>>56)&0xFF; m.w=m.w<<8;
+ byte_count--;
+ } while (*s++!=0);
+ file_num_name[k]=strdup(buf);
+ if (file_num_name[k]==NULL)
+ { print_err("Out of memory while reading filenames from format file");
+ succumb;
+ }
+ }
+ free(buf);
+}
+
+
+@
+Next we dump the file and line information from the |fl_mem| array.
+Because some formats leave many unallocated memory words in the |avail|
+list we dump only those memory words that are not on the avail list.
+We set the entry |fl_mem[p]| to zero if |p| is on the |avail| list
+while we traverse the list when dumping the memory words.
+Besides the unallocated entries also entries that specify the unknown
+file with line zero are completely zero. We do not dump these as well.
+
+When dumping an entry we set the topmost bit of the entry to 1.
+An entry that has the topmost bit equal to zero contains the count
+of the following zero entries. We dump entries in pairs since
+the dump file is organized as a file of memory words
+
+@<Dump the file and line information@>=
+{ int word_count=0;
+ k=hi_mem_min;
+ while (k<=mem_end)
+ { two_halves hw;
+ halfword e;
+ int word_count=0;
+ @<compute the next file line entry@>@;
+ hw.rh=e;
+ if (k>mem_end) e=0;
+ else
+ { @<compute the next file line entry@>@; }
+ hw.lh=e;
+ dump_hh(hw); word_count++;
+ }
+ print_ln(); print_int(word_count);
+ print(" words of file/line information dumped.");
+}
+@ @<compute the next file line entry@>=
+ if (fl_mem[k]!=0) e=fl_mem[k++]|0x80000000;
+ else
+ { int i;
+ i=1; k++;
+ while (k<=mem_end && fl_mem[k]==0)
+ { i++; k++; }
+ e=i;
+ }
+
+@ @<Undump the file and line information@>=
+k=hi_mem_min;
+while (k<=mem_end)
+{ two_halves hw;
+ halfword e;
+ undump_hh(hw);
+ e=hw.rh;
+ @<get the next file line entry@>@;
+ if (k<=mem_end)
+ { e=hw.lh;
+ @<get the next file line entry@>@;
+ }
+}
+
+@ @<get the next file line entry@>=
+if (e&0x80000000)
+ fl_mem[k++]=e&~0x80000000;
+else
+{ int i;
+ i=e;
+ while (i>0)
+ {i--; k++;}
+}
+
+@*1 Profiling \.{PDF} Specific Macros.
+Macro packages providing \.{PDF} specific features typicaly test
+whether the current engine is generating \.{PDF} output
+before activating \.{PDF}-specific macros, and these macros
+then use \.{PDF}-specific primitives. To be able to profile
+these macros, the engine must provide (or at least pretend to provide)
+the necessary primitives to make the test for \.{PDF} output succeed
+and prevent the macros from failing because of undefined
+primitives or unexpected behaviour. Generating \.{PDF} ouput
+is usualy not required.
+
+The {\tt -pdf} option is designed to provide the necessary behaviour
+by implementing replacements for the primitives of \pdfTeX.
+
+@<explain the options@>=
+ " -pdf "@/
+ @t\qquad@>"\t pretend to be pdftex; requires -ini; implies -ltx\n"@/
+
+@ This option will set the |pdf_on| variable, defined below, to true.
+ The option requires the {\tt -ini} option because its effect is just
+ the definition of additional primitives during initialization. It further
+ implies the {\tt -ltx} option because the primitives necesary to
+ run \LaTeX\ are part of \pdfTeX.
+
+@<handle the options@>=
+else @+if (ARGUMENT_IS ("pdf")) @t\2@>
+ if (iniversion) pdf_on=ltxp=true;
+ else
+ { fprintf(stderr,"-pdf requires -ini; try -ini -pdf\n");
+ exit(1);
+ }
+
+@ @<more options@>=
+ { "pdf", 0, 0, 0 },
+
+@ @<Global variables@>=
+static int @!pdf_on=false;
+
+@ Let us start with implementing the \.{PDF} version primitive.
+This primitive uses the |last_item| command with a special modifier
+\pdfTeX\ adds the codes for its extensions: |pdftex_version_code|, \dots
+between \TeX's |badness_code| and \eTeX's |eTeX_int| codes.
+Some codes like |elapsed_time_code| or |random_seed_code| are already defined above
+but we duplicate them here for simplicity.
+So here is the pdf version as well as all of PDF\TeX's command codes
+for the |last_item| command:
+
+@d pdftex_version 140 /* \.{\\pdftexversion} */
+@d pdftex_revision "25" /* \.{\\pdftexrevision} */
+@d pdftex_version_string "-1.40.25" /*current \pdfTeX\ version*/
+@#
+@d pdftex_first_rint_code (badness_code+1) /*base for \pdfTeX's command codes*/
+@d pdftex_version_code (pdftex_first_rint_code+0) /*code for \.{\\pdftexversion}*/
+@d pdf_last_obj_code (pdftex_first_rint_code+1) /*code for \.{\\pdflastobj}*/
+@d pdf_last_xform_code (pdftex_first_rint_code+2) /*code for \.{\\pdflastxform}*/
+@d pdf_last_ximage_code (pdftex_first_rint_code+3) /*code for \.{\\pdflastximage}*/
+@d pdf_last_ximage_pages_code (pdftex_first_rint_code+4) /*code for \.{\\pdflastximagepages}*/
+@d pdf_last_annot_code (pdftex_first_rint_code+5) /*code for \.{\\pdflastannot}*/
+@d pdf_retval_code (pdftex_first_rint_code+6) /*global multi-purpose return value*/
+@d pdf_last_ximage_colordepth_code (pdftex_first_rint_code+7) /*code for \.{\\pdflastximagecolordepth}*/
+@d pdf_last_link_code (pdftex_first_rint_code+8) /*code for \.{\\pdflastlink}*/
+@d pdftex_last_item_codes (pdftex_first_rint_code+8) /*end of \pdfTeX's command codes*/
+
+@ Using these codes we can add the primitives:
+
+@<Put each...@>=
+if (pdf_on)
+{
+primitive("pdftexversion", last_item, pdftex_version_code);@/
+@!@:pdftex\_version\_}{\.{\\pdftexversion} primitive@>
+primitive("pdflastobj", last_item, pdf_last_obj_code);@/
+@!@:pdf\_last\_obj\_}{\.{\\pdflastobj} primitive@>
+primitive("pdflastxform", last_item, pdf_last_xform_code);@/
+@!@:pdf\_last\_xform\_}{\.{\\pdflastxform} primitive@>
+primitive("pdflastximage", last_item, pdf_last_ximage_code);@/
+@!@:pdf\_last\_ximage\_}{\.{\\pdflastximage} primitive@>
+primitive("pdflastximagepages", last_item, pdf_last_ximage_pages_code);@/
+@!@:pdf\_last\_ximage\_pages\_}{\.{\\pdflastximagepages} primitive@>
+primitive("pdflastannot", last_item, pdf_last_annot_code);@/
+@!@:pdf\_last\_annot\_}{\.{\\pdflastannot} primitive@>
+primitive("pdfretval", last_item, pdf_retval_code);@/
+@!@:pdf\_retval\_}{\.{\\pdfretval} primitive@>
+primitive("pdflastximagecolordepth", last_item, pdf_last_ximage_colordepth_code);@/
+@!@:pdf\_last\_ximage\_colordepth\_}{\.{\\pdflastximagecolordepth} primitive@>
+primitive("pdflastlink", last_item, pdf_last_link_code);@/
+@!@:pdf\_last\_link\_}{\.{\\pdflastlink} primitive@>
+}
+
+@ Next we need to print the commands:
+
+@<Cases of |last_item| for |print_cmd_chr|@>=
+ case pdftex_version_code: print_esc("pdftexversion");@+break;
+ case pdf_last_obj_code: print_esc("pdflastobj");@+break;
+ case pdf_last_xform_code: print_esc("pdflastxform");@+break;
+ case pdf_last_ximage_code: print_esc("pdflastximage");@+break;
+ case pdf_last_ximage_pages_code: print_esc("pdflastximagepages");@+break;
+ case pdf_last_annot_code: print_esc("pdflastannot");@+break;
+ case pdf_retval_code: print_esc("pdfretval");@+break;
+ case pdf_last_ximage_colordepth_code: print_esc("pdflastximagecolordepth");@+break;
+ case pdf_last_link_code: print_esc("pdflastlink");@+break;
+
+@ The implementation needs a few global variables.
+
+@<Glob...@>=
+static int
+@!pdf_last_obj,
+@!pdf_last_xform,
+@!pdf_last_ximage,
+@!pdf_last_ximage_pages,
+@!pdf_last_annot,
+@!pdf_retval,
+@!pdf_last_ximage_colordepth,
+@!pdf_last_link;
+
+
+@ And this is sufficient to finish the implementation of this first batch of primitives:
+@<Cases for fetching an integer value@>=
+ case pdftex_version_code: cur_val=pdftex_version;@+break;
+ case pdf_last_obj_code: cur_val=pdf_last_obj;@+break;
+ case pdf_last_xform_code: cur_val=pdf_last_xform;@+break;
+ case pdf_last_ximage_code: cur_val=pdf_last_ximage;@+break;
+ case pdf_last_ximage_pages_code: cur_val=pdf_last_ximage_pages;@+break;
+ case pdf_last_annot_code: cur_val=pdf_last_annot;@+break;
+ case pdf_retval_code: cur_val=pdf_retval;@+break;
+ case pdf_last_ximage_colordepth_code: cur_val=pdf_last_ximage_colordepth;@+break;
+ case pdf_last_link_code: cur_val=pdf_last_link;@+break;
+
+
+@ The next group of primitves uses the assignment commands. We start
+by defining the necessary command codes followed by the primitives.
+
+@d pdftex_first_loc tex_toks /*base for \pdfTeX's token list parameters*/
+@d pdf_pages_attr_loc (pdftex_first_loc+0) /*points to token list for \.{\\pdfpagesattr}*/
+@d pdf_page_attr_loc (pdftex_first_loc+1) /*points to token list for \.{\\pdfpageattr}*/
+@d pdf_page_resources_loc (pdftex_first_loc+2) /*points to token list for \.{\\pdfpageresources}*/
+@d pdf_pk_mode_loc (pdftex_first_loc+3) /*points to token list for \.{\\pdfpkmode}*/
+@d pdf_toks (pdftex_first_loc+4) /*end of \pdfTeX's token list parameters*/
+@#
+@d pdftex_first_integer_code tex_int_pars /*base for \pdfTeX's integer parameters*/
+@d pdf_output_code (pdftex_first_integer_code+0) /*switch on PDF output if positive*/
+@d pdf_compress_level_code (pdftex_first_integer_code+1) /*compress level of streams*/
+@d pdf_decimal_digits_code (pdftex_first_integer_code+2) /*digits after the decimal point of numbers*/
+@d pdf_move_chars_code (pdftex_first_integer_code+3) /*move chars 0..31 to higher area if possible*/
+@d pdf_image_resolution_code (pdftex_first_integer_code+4) /*default image resolution*/
+@d pdf_pk_resolution_code (pdftex_first_integer_code+5) /*default resolution of PK font*/
+@d pdf_unique_resname_code (pdftex_first_integer_code+6) /*generate unique names for resouces*/
+@d pdf_option_always_use_pdfpagebox_code (pdftex_first_integer_code+7) /*if the PDF inclusion should always use a specific PDF page box*/
+@d pdf_option_pdf_inclusion_errorlevel_code (pdftex_first_integer_code+8) /*if the PDF inclusion should treat pdfs newer than |pdf_minor_version| as an error*/
+@d pdf_major_version_code (pdftex_first_integer_code+9) /*integer part of the PDF version produced*/
+@d pdf_minor_version_code (pdftex_first_integer_code+10) /*fractional part of the PDF version produced*/
+@d pdf_force_pagebox_code (pdftex_first_integer_code+11) /*if the PDF inclusion should always use a specific PDF page box*/
+@d pdf_pagebox_code (pdftex_first_integer_code+12) /*default pagebox to use for PDF inclusion*/
+@d pdf_inclusion_errorlevel_code (pdftex_first_integer_code+13) /*if the PDF inclusion should treat pdfs newer than |pdf_minor_version| as an error*/
+@d pdf_gamma_code (pdftex_first_integer_code+14)
+@d pdf_image_gamma_code (pdftex_first_integer_code+15)
+@d pdf_image_hicolor_code (pdftex_first_integer_code+16)
+@d pdf_image_apply_gamma_code (pdftex_first_integer_code+17)
+@d pdf_adjust_spacing_code (pdftex_first_integer_code+18) /*level of spacing adjusting*/
+@d pdf_protrude_chars_code (pdftex_first_integer_code+19) /*protrude chars at left/right edge of paragraphs*/
+@d pdf_tracing_fonts_code (pdftex_first_integer_code+20) /*level of font detail in log*/
+@d pdf_objcompresslevel_code (pdftex_first_integer_code+21) /*activate object streams*/
+@d pdf_adjust_interword_glue_code (pdftex_first_integer_code+22) /*adjust interword glue?*/
+@d pdf_prepend_kern_code (pdftex_first_integer_code+23) /*prepend kern before certain characters?*/
+@d pdf_append_kern_code (pdftex_first_integer_code+24) /*append kern before certain characters?*/
+@d pdf_gen_tounicode_code (pdftex_first_integer_code+25) /*generate ToUnicode for fonts?*/
+@d pdf_draftmode_code (pdftex_first_integer_code+26) /*switch on draftmode if positive*/
+@d pdf_inclusion_copy_font_code (pdftex_first_integer_code+27) /*generate ToUnicode for fonts?*/
+@d pdf_suppress_warning_dup_dest_code (pdftex_first_integer_code+28) /*suppress warning about duplicated destinations*/
+@d pdf_suppress_warning_dup_map_code (pdftex_first_integer_code+29) /*suppress warning about duplicated map lines*/
+@d pdf_suppress_warning_page_group_code (pdftex_first_integer_code+30) /*suppress warning about multiple pdfs with page group*/
+@d pdf_info_omit_date_code (pdftex_first_integer_code+31) /*omit generating CreationDate and ModDate*/
+@d pdf_suppress_ptex_info_code (pdftex_first_integer_code+32) /*suppress /PTEX.* entries in PDF dictionaries*/
+@d pdf_omit_charset_code (pdftex_first_integer_code+33) /*suppress /PTEX.* entries in PDF dictionaries*/
+@d pdf_omit_info_dict_code (pdftex_first_integer_code+34) /*suppress /PTEX.* entries in PDF dictionaries*/
+@d pdf_omit_procset_code (pdftex_first_integer_code+35) /*suppress /PTEX.* entries in PDF dictionaries*/
+@d pdf_int_pars (pdftex_first_integer_code+36) /*total number of \pdfTeX's integer parameters*/
+@#
+@d pdftex_first_dimen_code 23 /*first number defined in this section*/
+@d pdf_h_origin_code (pdftex_first_dimen_code+0) /*horigin of the PDF output*/
+@d pdf_v_origin_code (pdftex_first_dimen_code+1) /*vorigin of the PDF output*/
+@d pdf_link_margin_code (pdftex_first_dimen_code+2) /*link margin in the PDF output*/
+@d pdf_dest_margin_code (pdftex_first_dimen_code+3) /*dest margin in the PDF output*/
+@d pdf_thread_margin_code (pdftex_first_dimen_code+4) /*thread margin in the PDF output*/
+@d pdf_first_line_height_code (pdftex_first_dimen_code+5)
+@d pdf_last_line_depth_code (pdftex_first_dimen_code+6)
+@d pdf_each_line_height_code (pdftex_first_dimen_code+7)
+@d pdf_each_line_depth_code (pdftex_first_dimen_code+8)
+@d pdf_ignored_dimen_code (pdftex_first_dimen_code+9)
+@d pdf_px_dimen_code (pdftex_first_dimen_code+10)
+@d pdftex_last_dimen_code (pdftex_first_dimen_code+10) /*last number defined in this section*/
+@d dimen_pars (pdftex_last_dimen_code+1) /*total number of dimension parameters*/
+
+@<Put each...@>=
+if (pdf_on)
+{
+primitive("pdfpagesattr", assign_toks, pdf_pages_attr_loc);
+@!@:pdf\_pages\_attr\_}{\.{\\pdfpagesattr} primitive@>
+primitive("pdfpageattr", assign_toks, pdf_page_attr_loc);
+@!@:pdf\_page\_attr\_}{\.{\\pdfpageattr} primitive@>
+primitive("pdfpageresources", assign_toks, pdf_page_resources_loc);
+@!@:pdf\_page\_resources\_}{\.{\\pdfpageresources} primitive@>
+primitive("pdfpkmode", assign_toks, pdf_pk_mode_loc);
+@!@:pdf\_pk\_mode\_}{\.{\\pdfpkmode} primitive@>
+@#
+primitive("pdfoutput", assign_int, int_base+pdf_output_code);@/
+@!@:pdf\_output\_}{\.{\\pdfoutput} primitive@>
+primitive("pdfcompresslevel", assign_int, int_base+pdf_compress_level_code);@/
+@!@:pdf\_compress\_level\_}{\.{\\pdfcompresslevel} primitive@>
+primitive("pdfobjcompresslevel", assign_int, int_base+pdf_objcompresslevel_code);@/
+@!@:pdf\_objcompresslevel\_}{\.{\\pdfobjcompresslevel} primitive@>
+primitive("pdfdecimaldigits", assign_int, int_base+pdf_decimal_digits_code);@/
+@!@:pdf\_decimal\_digits\_}{\.{\\pdfdecimaldigits} primitive@>
+primitive("pdfmovechars", assign_int, int_base+pdf_move_chars_code);@/
+@!@:pdf\_move\_chars\_}{\.{\\pdfmovechars} primitive@>
+primitive("pdfimageresolution", assign_int, int_base+pdf_image_resolution_code);@/
+@!@:pdf\_image\_resolution\_}{\.{\\pdfimageresolution} primitive@>
+primitive("pdfpkresolution", assign_int, int_base+pdf_pk_resolution_code);@/
+@!@:pdf\_pk\_resolution\_}{\.{\\pdfpkresolution} primitive@>
+primitive("pdfuniqueresname", assign_int, int_base+pdf_unique_resname_code);@/
+@!@:pdf\_unique\_resname\_}{\.{\\pdfuniqueresname} primitive@>
+primitive("pdfoptionpdfminorversion", assign_int, int_base+pdf_minor_version_code);@/
+@!@:pdf\_minor\_version\_}{\.{\\pdfoptionpdfminorversion} primitive@>
+primitive("pdfoptionalwaysusepdfpagebox", assign_int, int_base+pdf_option_always_use_pdfpagebox_code);@/
+@!@:pdf\_option\_always\_use\_pdfpagebox\_}{\.{\\pdfoptionalwaysusepdfpagebox} primitive@>
+primitive("pdfoptionpdfinclusionerrorlevel", assign_int, int_base+pdf_option_pdf_inclusion_errorlevel_code);@/
+@!@:pdf\_option\_pdf\_inclusion\_errorlevel\_}{\.{\\pdfoptionpdfinclusionerrorlevel} primitive@>
+primitive("pdfmajorversion", assign_int, int_base+pdf_major_version_code);@/
+@!@:pdf\_major\_version\_}{\.{\\pdfmajorversion} primitive@>
+primitive("pdfminorversion", assign_int, int_base+pdf_minor_version_code);@/
+@!@:pdf\_minor\_version\_}{\.{\\pdfminorversion} primitive@>
+primitive("pdfforcepagebox", assign_int, int_base+pdf_force_pagebox_code);@/
+@!@:pdf\_force\_pagebox\_}{\.{\\pdfforcepagebox} primitive@>
+primitive("pdfpagebox", assign_int, int_base+pdf_pagebox_code);@/
+@!@:pdf\_pagebox\_}{\.{\\pdfpagebox} primitive@>
+primitive("pdfinclusionerrorlevel", assign_int, int_base+pdf_inclusion_errorlevel_code);@/
+@!@:pdf\_inclusion\_errorlevel\_}{\.{\\pdfinclusionerrorlevel} primitive@>
+primitive("pdfgamma", assign_int, int_base+pdf_gamma_code);@/
+@!@:pdf\_gamma\_}{\.{\\pdfgamma} primitive@>
+@!@:pdf\_gamma\_}{\.{\\pdfgamma} primitive@>
+primitive("pdfimagegamma", assign_int, int_base+pdf_image_gamma_code);@/
+@!@:pdf\_image\_gamma\_}{\.{\\pdfimagegamma} primitive@>
+primitive("pdfimagehicolor", assign_int, int_base+pdf_image_hicolor_code);@/
+@!@:pdf\_image\_hicolor\_}{\.{\\pdfimagehicolor} primitive@>
+primitive("pdfimageapplygamma", assign_int, int_base+pdf_image_apply_gamma_code);@/
+@!@:pdf\_image\_apply\_gamma\_}{\.{\\pdfimageapplygamma} primitive@>
+primitive("pdfadjustspacing", assign_int, int_base+pdf_adjust_spacing_code);@/
+@!@:pdf\_adjust\_spacing\_}{\.{\\pdfadjustspacing} primitive@>
+primitive("pdfprotrudechars", assign_int, int_base+pdf_protrude_chars_code);@/
+@!@:pdf\_protrude\_chars\_}{\.{\\pdfprotrudechars} primitive@>
+primitive("pdftracingfonts", assign_int, int_base+pdf_tracing_fonts_code);@/
+@!@:pdf\_tracing\_fonts\_}{\.{\\pdftracingfonts} primitive@>
+primitive("pdfadjustinterwordglue", assign_int, int_base+pdf_adjust_interword_glue_code);@/
+@!@:pdf\_adjust\_interword\_glue\_}{\.{\\pdfadjustinterwordglue} primitive@>
+primitive("pdfprependkern", assign_int, int_base+pdf_prepend_kern_code);@/
+@!@:pdf\_prepend\_kern\_}{\.{\\pdfprependkern} primitive@>
+primitive("pdfappendkern", assign_int, int_base+pdf_append_kern_code);@/
+@!@:pdf\_append\_kern\_}{\.{\\pdfappendkern} primitive@>
+primitive("pdfgentounicode", assign_int, int_base+pdf_gen_tounicode_code);@/
+@!@:pdf\_gen\_tounicode\_}{\.{\\pdfgentounicode} primitive@>
+primitive("pdfdraftmode", assign_int, int_base+pdf_draftmode_code);@/
+@!@:pdf\_draftmode\_}{\.{\\pdfdraftmode} primitive@>
+primitive("pdfinclusioncopyfonts", assign_int, int_base+pdf_inclusion_copy_font_code);@/
+@!@:pdf\_inclusion\_copy\_font\_}{\.{\\pdfinclusioncopyfonts} primitive@>
+primitive("pdfsuppresswarningdupdest", assign_int, int_base+pdf_suppress_warning_dup_dest_code);@/
+@!@:pdf\_suppress\_warning\_dup\_dest\_}{\.{\\pdfsuppresswarningdupdest} primitive@>
+primitive("pdfsuppresswarningdupmap", assign_int, int_base+pdf_suppress_warning_dup_map_code);@/
+@!@:pdf\_suppress\_warning\_dup\_map\_}{\.{\\pdfsuppresswarningdupmap} primitive@>
+primitive("pdfsuppresswarningpagegroup", assign_int, int_base+pdf_suppress_warning_page_group_code);@/
+@!@:pdf\_suppress\_warning\_page\_group\_}{\.{\\pdfsuppresswarningpagegroup} primitive@>
+primitive("pdfinfoomitdate", assign_int, int_base+pdf_info_omit_date_code);@/
+@!@:pdf\_info\_omit\_date\_}{\.{\\pdfinfoomitdate} primitive@>
+primitive("pdfsuppressptexinfo", assign_int, int_base+pdf_suppress_ptex_info_code);@/
+@!@:pdf\_suppress\_ptex\_info\_}{\.{\\pdfsuppressptexinfo} primitive@>
+primitive("pdfomitcharset", assign_int, int_base+pdf_omit_charset_code);@/
+@!@:pdf\_omit\_charset}{\.{\\pdfomitcharset} primitive@>
+primitive("pdfomitinfodict", assign_int, int_base+pdf_omit_info_dict_code);@/
+@!@:pdf\_omit\_info\_dict}{\.{\\pdfomitinfodict} primitive@>
+primitive("pdfomitprocset", assign_int, int_base+pdf_omit_procset_code);@/
+@!@:pdf\_omit\_procset}{\.{\\pdfomitprocset} primitive@>
+@#
+primitive("pdfhorigin", assign_dimen, dimen_base+pdf_h_origin_code);@/
+@!@:pdf\_h\_origin\_}{\.{\\pdfhorigin} primitive@>
+primitive("pdfvorigin", assign_dimen, dimen_base+pdf_v_origin_code);@/
+@!@:pdf\_v\_origin\_}{\.{\\pdfvorigin} primitive@>
+primitive("pdflinkmargin", assign_dimen, dimen_base+pdf_link_margin_code);@/
+@!@:pdf\_link\_margin\_}{\.{\\pdflinkmargin} primitive@>
+primitive("pdfdestmargin", assign_dimen, dimen_base+pdf_dest_margin_code);@/
+@!@:pdf\_dest\_margin\_}{\.{\\pdfdestmargin} primitive@>
+primitive("pdfthreadmargin", assign_dimen, dimen_base+pdf_thread_margin_code);@/
+@!@:pdf\_thread\_margin\_}{\.{\\pdfthreadmargin} primitive@>
+primitive("pdffirstlineheight", assign_dimen, dimen_base+pdf_first_line_height_code);@/
+@!@:pdf\_first\_line\_height\_}{\.{\\pdffirstlineheight} primitive@>
+primitive("pdflastlinedepth", assign_dimen, dimen_base+pdf_last_line_depth_code);@/
+@!@:pdf\_last\_line\_depth\_}{\.{\\pdflastlinedepth} primitive@>
+primitive("pdfeachlineheight", assign_dimen, dimen_base+pdf_each_line_height_code);@/
+@!@:pdf\_each\_line\_height\_}{\.{\\pdfeachlineheight} primitive@>
+primitive("pdfeachlinedepth", assign_dimen, dimen_base+pdf_each_line_depth_code);@/
+@!@:pdf\_each\_line\_depth\_}{\.{\\pdfeachlinedepth} primitive@>
+primitive("pdfignoreddimen", assign_dimen, dimen_base+pdf_ignored_dimen_code);@/
+@!@:pdf\_ignored\_dimen\_}{\.{\\pdfignoreddimen} primitive@>
+primitive("pdfpxdimen", assign_dimen, dimen_base+pdf_px_dimen_code);@/
+@!@:pdf\_px\_dimen\_}{\.{\\pdfpxdimen} primitive@>
+primitive("ifpdfprimitive", if_test, if_primitive_code);
+@!@:if\_primitive\_}{\.{\\if} primitive@>
+
+
+}
+
+@ As before we add cases to the printing routines.
+
+@<Cases of |assign_toks| for |print_cmd_chr|@>=
+ case pdf_pages_attr_loc: print_esc("pdfpagesattr");@+break;
+ case pdf_page_attr_loc: print_esc("pdfpageattr");@+break;
+ case pdf_page_resources_loc: print_esc("pdfpageresources");@+break;
+ case pdf_pk_mode_loc: print_esc("pdfpkmode");@+break;
+
+@ @<Cases for |print_param|@>=
+case pdf_output_code: print_esc("pdfoutput");@+break;
+case pdf_compress_level_code: print_esc("pdfcompresslevel");@+break;
+case pdf_objcompresslevel_code: print_esc("pdfobjcompresslevel");@+break;
+case pdf_decimal_digits_code: print_esc("pdfdecimaldigits");@+break;
+case pdf_move_chars_code: print_esc("pdfmovechars");@+break;
+case pdf_image_resolution_code: print_esc("pdfimageresolution");@+break;
+case pdf_pk_resolution_code: print_esc("pdfpkresolution");@+break;
+case pdf_unique_resname_code: print_esc("pdfuniqueresname");@+break;
+case pdf_option_always_use_pdfpagebox_code: print_esc("pdfoptionalwaysusepdfpagebox");@+break;
+case pdf_option_pdf_inclusion_errorlevel_code: print_esc("pdfoptionpdfinclusionerrorlevel");@+break;
+case pdf_major_version_code: print_esc("pdfmajorversion");@+break;
+case pdf_minor_version_code: print_esc("pdfminorversion");@+break;
+case pdf_force_pagebox_code: print_esc("pdfforcepagebox");@+break;
+case pdf_pagebox_code: print_esc("pdfpagebox");@+break;
+case pdf_inclusion_errorlevel_code: print_esc("pdfinclusionerrorlevel");@+break;
+case pdf_gamma_code: print_esc("pdfgamma");@+break;
+case pdf_image_gamma_code: print_esc("pdfimagegamma");@+break;
+case pdf_image_hicolor_code: print_esc("pdfimagehicolor");@+break;
+case pdf_image_apply_gamma_code: print_esc("pdfimageapplygamma");@+break;
+case pdf_adjust_spacing_code: print_esc("pdfadjustspacing");@+break;
+case pdf_protrude_chars_code: print_esc("pdfprotrudechars");@+break;
+case pdf_tracing_fonts_code: print_esc("pdftracingfonts");@+break;
+case pdf_adjust_interword_glue_code: print_esc("pdfadjustinterwordglue");@+break;
+case pdf_prepend_kern_code: print_esc("pdfprependkern");@+break;
+case pdf_append_kern_code: print_esc("pdfappendkern");@+break;
+case pdf_gen_tounicode_code: print_esc("pdfgentounicode");@+break;
+case pdf_draftmode_code: print_esc("pdfdraftmode");@+break;
+case pdf_inclusion_copy_font_code: print_esc("pdfinclusioncopyfonts");@+break;
+case pdf_suppress_warning_dup_dest_code: print_esc("pdfsuppresswarningdupdest");@+break;
+case pdf_suppress_warning_dup_map_code: print_esc("pdfsuppresswarningdupmap");@+break;
+case pdf_suppress_warning_page_group_code: print_esc("pdfsuppresswarningpagegroup");@+break;
+case pdf_info_omit_date_code: print_esc("pdfinfoomitdate");@+break;
+case pdf_suppress_ptex_info_code: print_esc("pdfsuppressptexinfo");@+break;
+case pdf_omit_charset_code: print_esc("pdfomitcharset");@+break;
+case pdf_omit_info_dict_code: print_esc("pdfomitinfodict");@+break;
+case pdf_omit_procset_code: print_esc("pdfomitprocset");@+break;
+
+@ @<Cases for |print_lenght_param|@>=
+case pdf_h_origin_code: print_esc("pdfhorigin");@+break;
+case pdf_v_origin_code: print_esc("pdfvorigin");@+break;
+case pdf_link_margin_code: print_esc("pdflinkmargin");@+break;
+case pdf_dest_margin_code: print_esc("pdfdestmargin");@+break;
+case pdf_thread_margin_code: print_esc("pdfthreadmargin");@+break;
+case pdf_first_line_height_code: print_esc("pdffirstlineheight");@+break;
+case pdf_last_line_depth_code: print_esc("pdflastlinedepth");@+break;
+case pdf_each_line_height_code: print_esc("pdfeachlineheight");@+break;
+case pdf_each_line_depth_code: print_esc("pdfeachlinedepth");@+break;
+case pdf_ignored_dimen_code: print_esc("pdfignoreddimen");@+break;
+case pdf_px_dimen_code: print_esc("pdfpxdimen");@+break;
+
+@ It remains to initialize some of these entries.
+
+@<Initialize table entries...@>=
+dimen_par(pdf_h_origin_code)=4736287; /* 0.5 inch */
+dimen_par(pdf_v_origin_code)=4736287;
+int_par(pdf_compress_level_code)=9;
+int_par(pdf_objcompresslevel_code)=0;
+int_par(pdf_decimal_digits_code)=3;
+int_par(pdf_image_resolution_code)=72;
+int_par(pdf_major_version_code)=1;
+int_par(pdf_minor_version_code)=4;
+int_par(pdf_gamma_code)=1000;
+int_par(pdf_image_gamma_code)=2200;
+int_par(pdf_image_hicolor_code)=1;
+int_par(pdf_image_apply_gamma_code)=0;
+int_par(pdf_draftmode_code)=0;
+dimen_par(pdf_px_dimen_code)=65782; /* 1bp */
+
+@ The next class of primitives extend the |convert| comand.
+These codes go in between |Prote_last_convert_code| and |job_name_code|.
+
+@d pdftex_first_convert_code (Prote_last_convert_code+1) /*base for \pdfTeX's command codes*/
+@d pdftex_revision_code (pdftex_first_convert_code+0) /*command code for \.{\\pdftexrevision}*/
+@d pdftex_banner_code (pdftex_first_convert_code+1) /*command code for \.{\\pdftexbanner}*/
+@d pdf_font_name_code (pdftex_first_convert_code+2) /*command code for \.{\\pdffontname}*/
+@d pdf_font_objnum_code (pdftex_first_convert_code+3) /*command code for \.{\\pdffontobjnum}*/
+@d pdf_font_size_code (pdftex_first_convert_code+4) /*command code for \.{\\pdffontsize}*/
+@d pdf_page_ref_code (pdftex_first_convert_code+5) /*command code for \.{\\pdfpageref}*/
+@d pdf_xform_name_code (pdftex_first_convert_code+6) /*command code for \.{\\pdfxformname}*/
+@d pdf_escape_string_code (pdftex_first_convert_code+7) /*command code for \.{\\pdfescapestring}*/
+@d pdf_escape_name_code (pdftex_first_convert_code+8) /*command code for \.{\\pdfescapename}*/
+@d left_margin_kern_code (pdftex_first_convert_code+9) /*command code for \.{\\leftmarginkern}*/
+@d right_margin_kern_code (pdftex_first_convert_code+10) /*command code for \.{\\rightmarginkern}*/
+@d pdf_colorstack_init_code (pdftex_first_convert_code+11) /*command code for \.{\\pdfcolorstackinit}*/
+@d pdf_escape_hex_code (pdftex_first_convert_code+12) /*command code for \.{\\pdfescapehex}*/
+@d pdf_unescape_hex_code (pdftex_first_convert_code+13) /*command code for \.{\\pdfunescapehex}*/
+@d pdf_match_code (pdftex_first_convert_code+14) /*command code for \.{\\pdfmatch}*/
+@d pdf_last_match_code (pdftex_first_convert_code+15) /*command code for \.{\\pdflastmatch}*/
+@d pdf_insert_ht_code (pdftex_first_convert_code+16) /*command code for \.{\\pdfinsertht}*/
+@d pdf_ximage_bbox_code (pdftex_first_convert_code+17) /*command code for \.{\\pdfximagebbox}*/
+@d pdftex_convert_codes (pdftex_first_convert_code+18) /*end of \pdfTeX's command codes*/
+
+
+@<Put each...@>=
+if (pdf_on)
+{
+primitive("pdftexrevision", convert, pdftex_revision_code);@/
+@!@:pdftex\_revision\_}{\.{\\pdftexrevision} primitive@>
+primitive("pdftexbanner", convert, pdftex_banner_code);@/
+@!@:pdftex\_banner\_}{\.{\\pdftexbanner} primitive@>
+primitive("pdffontname", convert, pdf_font_name_code);@/
+@!@:pdf\_font\_name\_}{\.{\\pdffontname} primitive@>
+primitive("pdffontobjnum", convert, pdf_font_objnum_code);@/
+@!@:pdf\_font\_objnum\_}{\.{\\pdffontobjnum} primitive@>
+primitive("pdffontsize", convert, pdf_font_size_code);@/
+@!@:pdf\_font\_size\_}{\.{\\pdffontsize} primitive@>
+primitive("pdfpageref", convert, pdf_page_ref_code);@/
+@!@:pdf\_page\_ref\_}{\.{\\pdfpageref} primitive@>
+primitive("leftmarginkern", convert, left_margin_kern_code);@/
+@!@:left\_margin\_kern\_}{\.{\\leftmarginkern} primitive@>
+primitive("rightmarginkern", convert, right_margin_kern_code);@/
+@!@:right\_margin\_kern\_}{\.{\\rightmarginkern} primitive@>
+primitive("pdfxformname", convert, pdf_xform_name_code);@/
+@!@:pdf\_xform\_name\_}{\.{\\pdfxformname} primitive@>
+primitive("pdfescapestring", convert, pdf_escape_string_code);@/
+@!@:pdf\_escape\_string\_}{\.{\\pdfescapestring} primitive@>
+primitive("pdfescapename", convert, pdf_escape_name_code);@/
+@!@:pdf\_escape\_name\_}{\.{\\pdfescapename} primitive@>
+primitive("pdfescapehex", convert, pdf_escape_hex_code);@/
+@!@:pdf\_escape\_hex\_}{\.{\\pdfescapehex} primitive@>
+primitive("pdfunescapehex", convert, pdf_unescape_hex_code);@/
+@!@:pdf\_unescape\_hex\_}{\.{\\pdfunescapehex} primitive@>
+primitive("pdfmatch", convert, pdf_match_code);@/
+@!@:pdf\_match\_}{\.{\\pdfmatch} primitive@>
+primitive("pdflastmatch", convert, pdf_last_match_code);@/
+@!@:pdf\_last\_match\_}{\.{\\pdflastmatch} primitive@>
+primitive("pdfcolorstackinit", convert, pdf_colorstack_init_code);@/
+@!@:pdf\_colorstack\_init\_}{\.{\\pdfcolorstackinit} primitive@>
+}
+
+@ The usual code for printing:
+
+@<Cases of |convert| for |print_cmd_chr|@>=
+ case pdftex_revision_code: print_esc("pdftexrevision");@+break;
+ case pdftex_banner_code: print_esc("pdftexbanner");@+break;
+ case pdf_font_name_code: print_esc("pdffontname");@+break;
+ case pdf_font_objnum_code: print_esc("pdffontobjnum");@+break;
+ case pdf_font_size_code: print_esc("pdffontsize");@+break;
+ case pdf_page_ref_code: print_esc("pdfpageref");@+break;
+ case left_margin_kern_code: print_esc("leftmarginkern");@+break;
+ case right_margin_kern_code: print_esc("rightmarginkern");@+break;
+ case pdf_xform_name_code: print_esc("pdfxformname");@+break;
+ case pdf_escape_string_code: print_esc("pdfescapestring");@+break;
+ case pdf_escape_name_code: print_esc("pdfescapename");@+break;
+ case pdf_escape_hex_code: print_esc("pdfescapehex");@+break;
+ case pdf_unescape_hex_code: print_esc("pdfunescapehex");@+break;
+ case pdf_match_code: print_esc("pdfmatch");@+break;
+ case pdf_last_match_code: print_esc("pdflastmatch");@+break;
+ case pdf_colorstack_init_code: print_esc("pdfcolorstackinit");@+break;
+ case pdf_insert_ht_code: print_esc("pdfinsertht");@+break;
+ case pdf_ximage_bbox_code: print_esc("pdfximagebbox");@+break;
+
+@ The following code is taken from pdftex. At this point, I should drop
+some of the primitives that I do not want to implement.
+To skip general text we use the following macro
+
+
+@<Cases of `Scan the argument for command |c|'@>=
+case pdftex_revision_code: do_nothing;@+break;
+case pdftex_banner_code: do_nothing;@+break;
+case pdf_font_name_code: case pdf_font_objnum_code:
+ case pdf_font_size_code: {@+
+ scan_font_ident();
+ if (cur_val==null_font)
+ pdf_error("font","invalid font identifier");
+} @+break;
+case pdf_page_ref_code: {@+
+ scan_int();
+ if (cur_val <= 0)
+ pdf_error("pageref","invalid page number");
+} @+break;
+case left_margin_kern_code: case right_margin_kern_code: {@+
+ pointer p;
+ scan_register_num();
+ fetch_box(p);
+ if ((p==null)||(type(p)!=hlist_node))
+ pdf_error("marginkern","a non-empty hbox expected");
+} @+break;
+case pdf_xform_name_code: {@+
+ scan_int();
+} @+break;
+case pdf_escape_string_code:
+case pdf_escape_name_code:
+case pdf_escape_hex_code:
+case pdf_unescape_hex_code:
+ scan_general_x_text(); /*the simplified version*/
+ ins_list(link(link(garbage)));
+ free_avail(link(garbage)); /*drop reference count*/
+ return;
+case pdf_match_code:
+ scan_keyword("icase");
+ if (scan_keyword("subcount")) scan_int();
+ scan_pdf_ext_toks(); /*the simplified version*/
+ scan_pdf_ext_toks(); /*the simplified version*/
+ break;
+case pdf_last_match_code:
+ {@+pointer p,q;
+ scan_int();
+ /* always returning ``\.{-1->}''*/
+ p=temp_head;link(p)=null;
+ store_new_token(other_token+'-');
+ store_new_token(other_token+'1');
+ store_new_token(other_token+'-');
+ store_new_token(other_token+'>');
+ ins_list(link(temp_head));
+ return;
+ }
+case pdf_colorstack_init_code:
+ {@+
+ scan_keyword("page");
+ if (!scan_keyword("direct"))
+ scan_keyword("page");
+ scan_pdf_ext_toks(); /*the simplified version*/
+ cur_val=0;
+ } @+break;
+case pdf_insert_ht_code: scan_register_num();@+break;
+case pdf_ximage_bbox_code: {@+
+ scan_int();
+ scan_int();
+}@+break;
+
+
+@ This is the ``dummy'' |scan_pdf_ext_toks| routine we used above
+and a variation of it which we need later.
+
+@<Declare procedures needed in |do_ext...@>=
+static void scan_pdf_ext_toks(void)
+{ scan_toks(false,true); /*like \.{\\special}*/
+ flush_list(def_ref);
+}
+static void scan_pdf_ext_late_toks(void)
+{ scan_toks(false, false); /*like \.{\\special}, but doesn't expand*/
+ flush_list(def_ref);
+}
+
+
+@ The second part of handling conversion commands is the printing of the result.
+
+@<Cases of `Print the result of command |c|'@>=
+case pdftex_revision_code: print(pdftex_revision);@+break;
+case pdftex_banner_code: print("This is pdfTeX, Version 3.141592653");@+break;
+case pdf_font_name_code: case pdf_font_objnum_code: print_int(cur_val);@+break;
+case pdf_font_size_code:
+ print_scaled(font_size[cur_val]);
+ print("pt");@+break;
+case pdf_page_ref_code: print_int(1);@+break;
+case pdf_xform_name_code: print_int(cur_val);@+break;
+case pdf_escape_string_code:
+case pdf_escape_name_code:
+case left_margin_kern_code: print("0pt");@+break;
+case right_margin_kern_code: print("0pt");@+break;
+case pdf_colorstack_init_code: print_int(cur_val);@+break;
+case pdf_escape_hex_code:
+case pdf_unescape_hex_code:
+case pdf_match_code:
+case pdf_last_match_code:
+case pdf_insert_ht_code: print("10pt");@+break;
+case pdf_ximage_bbox_code: print("10pt");@+break;
+
+
+@ A few functions are neede to complete the implementation of the \pdfTeX\ simulation.
+
+
+@<Error handling procedures@>=
+static void pdf_error(char *t, char *p)
+{@+
+ normalize_selector();
+ print_err("pdfTeX error");
+ if (t!=0) {@+
+ print(" (");
+ print(t);
+ print(")");
+ }
+ print(": ");print(p);
+ succumb;
+}
+
+
+@ The last command we need to consider is the |extension| command.
+
+@d pdf_literal_node (pdftex_first_extension_code+0)
+
+@d pdf_lateliteral_node (pdftex_first_extension_code+1) /*Not needed*/
+
+@d pdf_obj_code (pdftex_first_extension_code+2)
+@d pdf_refobj_node (pdftex_first_extension_code+3)
+@d pdf_xform_code (pdftex_first_extension_code+4)
+@d pdf_refxform_node (pdftex_first_extension_code+5)
+@d pdf_ximage_code (pdftex_first_extension_code+6)
+@d pdf_refximage_node (pdftex_first_extension_code+7)
+@d pdf_annot_node (pdftex_first_extension_code+8)
+@d pdf_start_link_node (pdftex_first_extension_code+9)
+@d pdf_end_link_node (pdftex_first_extension_code+10)
+@d pdf_outline_code (pdftex_first_extension_code+11)
+@d pdf_dest_node (pdftex_first_extension_code+12)
+@d pdf_thread_node (pdftex_first_extension_code+13)
+@d pdf_start_thread_node (pdftex_first_extension_code+14)
+@d pdf_end_thread_node (pdftex_first_extension_code+15)
+
+@d pdf_info_code (pdftex_first_extension_code+16)
+@d pdf_catalog_code (pdftex_first_extension_code+17)
+@d pdf_names_code (pdftex_first_extension_code+18)
+
+@d pdf_font_attr_code (pdftex_first_extension_code+19)
+@d pdf_include_chars_code (pdftex_first_extension_code+20)
+@d pdf_map_file_code (pdftex_first_extension_code+21)
+@d pdf_map_line_code (pdftex_first_extension_code+22)
+@d pdf_trailer_code (pdftex_first_extension_code+23)
+@d pdf_trailer_id_code (pdftex_first_extension_code+24)
+
+@d pdf_font_expand_code (pdftex_first_extension_code+25)
+
+@d pdf_snap_ref_point_node (pdftex_first_extension_code+26)
+@d pdf_snapy_node (pdftex_first_extension_code+27)
+@d pdf_snapy_comp_node (pdftex_first_extension_code+28)
+@d pdf_glyph_to_unicode_code (pdftex_first_extension_code+29)
+@d pdf_colorstack_node (pdftex_first_extension_code+30)
+@d pdf_setmatrix_node (pdftex_first_extension_code+31)
+@d pdf_save_node (pdftex_first_extension_code+32)
+@d pdf_restore_node (pdftex_first_extension_code+33)
+@d pdf_nobuiltin_tounicode_code (pdftex_first_extension_code+34)
+@d pdf_interword_space_on_node (pdftex_first_extension_code+35)
+@d pdf_interword_space_off_node (pdftex_first_extension_code+36)
+@d pdf_fake_space_node (pdftex_first_extension_code+37)
+@d pdf_running_link_off_node (pdftex_first_extension_code+38)
+@d pdf_running_link_on_node (pdftex_first_extension_code+39)
+@d pdf_space_font_code (pdftex_first_extension_code+40)
+@#
+@d tracing_stack_level_code (pdftex_first_extension_code+41)
+@d pdf_par_token_context_code (pdftex_first_extension_code+42)
+@d pdf_par_token_name_code (pdftex_first_extension_code+43)
+@d pdf_show_stream_code (pdftex_first_extension_code+44)
+@#
+@d pdftex_last_extension_code (pdftex_first_extension_code+44)
+
+@<Put each...@>=
+if (pdf_on)
+{
+primitive("pdfliteral", extension, pdf_literal_node);@/
+@!@:pdf\_literal\_}{\.{\\pdfliteral} primitive@>
+primitive("pdfobj", extension, pdf_obj_code);@/
+@!@:pdf\_obj\_}{\.{\\pdfobj} primitive@>
+primitive("pdfrefobj", extension, pdf_refobj_node);@/
+@!@:pdf\_refobj\_}{\.{\\pdfrefobj} primitive@>
+primitive("pdfxform", extension, pdf_xform_code);@/
+@!@:pdf\_xform\_}{\.{\\pdfxform} primitive@>
+primitive("pdfrefxform", extension, pdf_refxform_node);@/
+@!@:pdf\_refxform\_}{\.{\\pdfrefxform} primitive@>
+primitive("pdfximage", extension, pdf_ximage_code);@/
+@!@:pdf\_ximage\_}{\.{\\pdfximage} primitive@>
+primitive("pdfrefximage", extension, pdf_refximage_node);@/
+@!@:pdf\_refximage\_}{\.{\\pdfrefximage} primitive@>
+primitive("pdfannot", extension, pdf_annot_node);@/
+@!@:pdf\_annot\_}{\.{\\pdfannot} primitive@>
+primitive("pdfstartlink", extension, pdf_start_link_node);@/
+@!@:pdf\_start\_link\_}{\.{\\pdfstartlink} primitive@>
+primitive("pdfendlink", extension, pdf_end_link_node);@/
+@!@:pdf\_end\_link\_}{\.{\\pdfendlink} primitive@>
+primitive("pdfoutline", extension, pdf_outline_code);@/
+@!@:pdf\_outline\_}{\.{\\pdfoutline} primitive@>
+primitive("pdfdest", extension, pdf_dest_node);@/
+@!@:pdf\_dest\_}{\.{\\pdfdest} primitive@>
+primitive("pdfthread", extension, pdf_thread_node);@/
+@!@:pdf\_thread\_}{\.{\\pdfthread} primitive@>
+primitive("pdfstartthread", extension, pdf_start_thread_node);@/
+@!@:pdf\_start\_thread\_}{\.{\\pdfstartthread} primitive@>
+primitive("pdfendthread", extension, pdf_end_thread_node);@/
+@!@:pdf\_end\_thread\_}{\.{\\pdfendthread} primitive@>
+
+primitive("pdfinfo", extension, pdf_info_code);@/
+@!@:pdf\_info\_}{\.{\\pdfinfo} primitive@>
+primitive("pdfcatalog", extension, pdf_catalog_code);@/
+@!@:pdf\_catalog\_}{\.{\\pdfcatalog} primitive@>
+primitive("pdfnames", extension, pdf_names_code);@/
+@!@:pdf\_names\_}{\.{\\pdfnames} primitive@>
+
+primitive("pdffontattr", extension, pdf_font_attr_code);@/
+@!@:pdf\_font\_attr\_}{\.{\\pdffontattr} primitive@>
+primitive("pdfincludechars", extension, pdf_include_chars_code);@/
+@!@:pdf\_include\_chars\_}{\.{\\pdfincludechars} primitive@>
+primitive("pdfmapfile", extension, pdf_map_file_code);@/
+@!@:pdf\_map\_file\_}{\.{\\pdfmapfile} primitive@>
+primitive("pdfmapline", extension, pdf_map_line_code);@/
+@!@:pdf\_map\_line\_}{\.{\\pdfmapline} primitive@>
+primitive("pdftrailer", extension, pdf_trailer_code);@/
+@!@:pdf\_trailer\_}{\.{\\pdftrailer} primitive@>
+primitive("pdftrailerid", extension, pdf_trailer_id_code);@/
+@!@:pdf\_trailer\_id\_}{\.{\\pdftrailerid} primitive@>
+
+primitive("pdffontexpand", extension, pdf_font_expand_code);@/
+@!@:pdf\_font\_expand\_}{\.{\\pdffontexpand} primitive@>
+
+primitive("pdfsnaprefpoint", extension, pdf_snap_ref_point_node);@/
+@!@:pdf\_snap\_ref\_point\_}{\.{\\pdfsnaprefpoint} primitive@>
+primitive("pdfsnapy", extension, pdf_snapy_node);@/
+@!@:pdf\_snapy\_}{\.{\\pdfsnapy} primitive@>
+primitive("pdfsnapycomp", extension, pdf_snapy_comp_node);@/
+@!@:pdf\_snapy\_comp\_}{\.{\\pdfsnapycomp} primitive@>
+primitive("pdfglyphtounicode", extension, pdf_glyph_to_unicode_code);@/
+@!@:pdf\_glyph\_to\_unicode\_}{\.{\\pdfglyphtounicode} primitive@>
+primitive("pdfcolorstack", extension, pdf_colorstack_node);@/
+@!@:pdf\_colorstack\_}{\.{\\pdfcolorstack} primitive@>
+primitive("pdfsetmatrix", extension, pdf_setmatrix_node);@/
+@!@:pdf\_setmatrix\_}{\.{\\pdfsetmatrix} primitive@>
+primitive("pdfsave", extension, pdf_save_node);@/
+@!@:pdf\_save\_}{\.{\\pdfsave} primitive@>
+primitive("pdfrestore", extension, pdf_restore_node);@/
+@!@:pdf\_restore\_}{\.{\\pdfrestore} primitive@>
+primitive("pdfnobuiltintounicode", extension, pdf_nobuiltin_tounicode_code);@/
+@!@:pdf\_nobuiltin\_tounicode\_}{\.{\\pdfnobuiltintounicode} primitive@>
+primitive("pdfinterwordspaceon", extension, pdf_interword_space_on_node);@/
+@!@:pdf\_interword\_space\_on\_}{\.{\\pdfinterwordspaceon} primitive@>
+primitive("pdfinterwordspaceoff", extension, pdf_interword_space_off_node);@/
+@!@:pdf\_interword\_space\_off\_}{\.{\\pdfinterwordspaceoff} primitive@>
+primitive("pdffakespace", extension, pdf_fake_space_node);@/
+@!@:pdf\_fake\_space\_}{\.{\\pdffakespace} primitive@>
+primitive("pdfrunninglinkoff", extension, pdf_running_link_off_node);@/
+@!@:pdf\_running\_link\_off\_}{\.{\\pdfrunninglinkoff} primitive@>
+primitive("pdfrunninglinkon", extension, pdf_running_link_on_node);@/
+@!@:pdf\_running\_link\_on\_}{\.{\\pdfrunninglinkon} primitive@>
+primitive("pdfspacefont", extension, pdf_space_font_code);@/
+@!@:pdf\_space\_font\_}{\.{\\pdfspacefont} primitive@>
+@#
+primitive("tracingstacklevels", extension, tracing_stack_level_code);@/
+@!@:tracingstacklevels\_}{\.{\\tracingstacklevels} primitive@>
+primitive("partokencontext", extension, pdf_par_token_context_code);@/
+@!@:\partokencontext}{\.{\\partokencontext} primitive@>
+primitive("partokenname", extension, pdf_par_token_name_code);@/
+@!@:partokenname\_}{\.{\\partokenname} primitive@>
+primitive("showstream", extension, pdf_show_stream_code);@/
+@!@:showstream\_}{\.{\\showstream} primitive@>
+}
+
+@ After having defined the primitives, we arange for printing them.
+
+@<Cases of |extension| for |print_cmd_chr|@>=
+ case pdf_annot_node: print_esc("pdfannot");@+break;
+ case pdf_catalog_code: print_esc("pdfcatalog");@+break;
+ case pdf_dest_node: print_esc("pdfdest");@+break;
+ case pdf_end_link_node: print_esc("pdfendlink");@+break;
+ case pdf_end_thread_node: print_esc("pdfendthread");@+break;
+ case pdf_font_attr_code: print_esc("pdffontattr");@+break;
+ case pdf_font_expand_code: print_esc("pdffontexpand");@+break;
+ case pdf_include_chars_code: print_esc("pdfincludechars");@+break;
+ case pdf_info_code: print_esc("pdfinfo");@+break;
+ case pdf_literal_node: print_esc("pdfliteral");@+break;
+ case pdf_colorstack_node: print_esc("pdfcolorstack");@+break;
+ case pdf_setmatrix_node: print_esc("pdfsetmatrix");@+break;
+ case pdf_save_node: print_esc("pdfsave");@+break;
+ case pdf_restore_node: print_esc("pdfrestore");@+break;
+ case pdf_map_file_code: print_esc("pdfmapfile");@+break;
+ case pdf_map_line_code: print_esc("pdfmapline");@+break;
+ case pdf_names_code: print_esc("pdfnames");@+break;
+ case pdf_obj_code: print_esc("pdfobj");@+break;
+ case pdf_outline_code: print_esc("pdfoutline");@+break;
+ case pdf_refobj_node: print_esc("pdfrefobj");@+break;
+ case pdf_refxform_node: print_esc("pdfrefxform");@+break;
+ case pdf_refximage_node: print_esc("pdfrefximage");@+break;
+ case pdf_snap_ref_point_node: print_esc("pdfsnaprefpoint");@+break;
+ case pdf_snapy_comp_node: print_esc("pdfsnapycomp");@+break;
+ case pdf_snapy_node: print_esc("pdfsnapy");@+break;
+ case pdf_start_link_node: print_esc("pdfstartlink");@+break;
+ case pdf_start_thread_node: print_esc("pdfstartthread");@+break;
+ case pdf_thread_node: print_esc("pdfthread");@+break;
+ case pdf_trailer_code: print_esc("pdftrailer");@+break;
+ case pdf_trailer_id_code: print_esc("pdftrailerid");@+break;
+ case pdf_xform_code: print_esc("pdfxform");@+break;
+ case pdf_ximage_code: print_esc("pdfximage");@+break;
+ case pdf_nobuiltin_tounicode_code: print_esc("pdfnobuiltintounicode");@+break;
+ case pdf_glyph_to_unicode_code: print_esc("pdfglyphtounicode");@+break;
+ case pdf_interword_space_on_node: print_esc("pdfinterwordspaceon");@+break;
+ case pdf_interword_space_off_node: print_esc("pdfinterwordspaceoff");@+break;
+ case pdf_fake_space_node: print_esc("pdffakespace");@+break;
+ case pdf_running_link_off_node: print_esc("pdfrunninglinkoff");@+break;
+ case pdf_running_link_on_node: print_esc("pdfrunninglinkon");@+break;
+ case pdf_space_font_code: print_esc("pdfspacefont");@+break;
+ case tracing_stack_level_code: print_esc("tracingstacklevels");@+break;
+ case pdf_par_token_context_code: print_esc("partokencontext");@+break;
+ case pdf_par_token_name_code: print_esc("partokenname");@+break;
+ case pdf_show_stream_code: print_esc("showstream");@+break;
+
+
+@ And last not least we have to implement the primitives.
+
+@<Cases for |do_extension|@>=
+ case pdf_annot_node: @<Implement \.{\\pdfannot}@>@;@+break;
+ case pdf_catalog_code: @<Implement \.{\\pdfcatalog}@>@;@+break;
+ case pdf_dest_node: @<Implement \.{\\pdfdest}@>@;@+break;
+ case pdf_end_link_node: @+break;
+ case pdf_end_thread_node: @+break;
+ case pdf_font_attr_code: @<Implement \.{\\pdffontattr}@>@;@+break;
+ case pdf_font_expand_code: read_expand_font();@+break;
+ case pdf_include_chars_code: pdf_include_chars();@+break;
+ case pdf_info_code: scan_pdf_ext_toks();@+break;
+ case pdf_literal_node: @<Implement \.{\\pdfliteral}@>@;@+break;
+ case pdf_colorstack_node: @<Implement \.{\\pdfcolorstack}@>@;@+break;
+ case pdf_setmatrix_node: scan_pdf_ext_toks();@+break;
+ case pdf_save_node: @+break;
+ case pdf_restore_node: @+break;
+ case pdf_map_file_code: scan_pdf_ext_toks();@+break;
+ case pdf_map_line_code: scan_pdf_ext_toks();@+break;
+ case pdf_names_code: scan_pdf_ext_toks();@+break;
+ case pdf_obj_code: @<Implement \.{\\pdfobj}@>@;@+break;
+ case pdf_outline_code: @<Implement \.{\\pdfoutline}@>@;@+break;
+ case pdf_refobj_node: scan_int();@+break;
+ case pdf_refxform_node: scan_int();@+break;
+ case pdf_refximage_node: scan_int();@+break;
+ case pdf_snap_ref_point_node: @+break;
+ case pdf_snapy_comp_node: scan_int();@+break;
+ case pdf_snapy_node: @+break;
+ case pdf_start_link_node: scan_annot(); scan_action();@+break;
+ case pdf_start_thread_node: scan_annot(); scan_thread_id();@+break;
+ case pdf_thread_node: scan_annot(); scan_thread_id();@+break;
+ case pdf_trailer_code: scan_pdf_ext_toks();@+break;
+ case pdf_trailer_id_code: scan_pdf_ext_toks();@+break;
+ case pdf_xform_code: @<Implement \.{\\pdfxform}@>@;@+break;
+ case pdf_ximage_code: scan_image(); @+break;
+ case pdf_glyph_to_unicode_code: scan_pdf_ext_toks();scan_pdf_ext_toks();@+break;
+ case pdf_nobuiltin_tounicode_code: scan_font_ident();@+break;
+ case pdf_interword_space_on_node: @+break;
+ case pdf_interword_space_off_node: @+break;
+ case pdf_fake_space_node: @+break;
+ case pdf_running_link_off_node: @+break;
+ case pdf_running_link_on_node: @+break;
+ case pdf_space_font_code: scan_pdf_ext_toks(); @+break;
+ case tracing_stack_level_code: scan_int();@+break;
+ case pdf_par_token_context_code: scan_optional_equals();scan_int();@+break;
+ case pdf_par_token_name_code: scan_optional_equals();get_token();@+break;
+ case pdf_show_stream_code: scan_optional_equals();scan_int();@+break;
+
+
+@ @<Implement \.{\\pdfannot}@>=
+{@+ if (scan_keyword("reserveobjnum")) {@+@<Scan an optional space@>;}
+ else{@+
+ if (scan_keyword("useobjnum")) {@+scan_int();}
+ scan_annot();
+ scan_pdf_ext_toks();
+ }
+}
+
+@ @<Implement \.{\\pdfcatalog}@>=
+{@+ scan_pdf_ext_toks();
+ if (scan_keyword("openaction")) scan_action();
+}
+
+@ The |scan_action| routine is used not only here
+but also for links and outlines.
+
+
+@d pdf_action_page 0 /*GoTo action*/
+@d pdf_action_goto 1 /*GoTo action*/
+@d pdf_action_thread 2 /*Thread action*/
+@d pdf_action_user 3 /*user-defined action*/
+
+@<Declare procedures needed in |do_ext...@>=
+static void scan_action(void) /*read an action specification*/
+{@+int pdf_action;
+ int pdf_action_file=0;
+ int pdf_action_new_window=0;
+ if (scan_keyword("user"))
+ pdf_action=pdf_action_user;
+ else if (scan_keyword("goto"))
+ pdf_action=pdf_action_goto;
+ else if (scan_keyword("thread"))
+ pdf_action=pdf_action_thread;
+ else
+ pdf_error("ext1","action type missing");
+ if (pdf_action==pdf_action_user) {@+
+ scan_pdf_ext_toks();
+ return; }
+ if (scan_keyword("file")) {@+
+ scan_pdf_ext_toks();
+ pdf_action_file=1;
+ }
+ if (scan_keyword("struct")) {@+
+ if (pdf_action!=pdf_action_goto)
+ pdf_error("ext1","only GoTo action can be used with `struct'");
+ if (pdf_action_file!=0) {@+
+ scan_pdf_ext_toks();
+ }
+ else if (scan_keyword("name")) {@+
+ scan_pdf_ext_toks();
+ }
+ else if (scan_keyword("num")) {@+
+ scan_int();
+ if (cur_val <= 0)
+ pdf_error("ext1","num identifier must be positive");
+ }
+ else
+ pdf_error("ext1","identifier type missing");
+ }
+ if (scan_keyword("page")) {@+
+ if (pdf_action!=pdf_action_goto)
+ pdf_error("ext1","only GoTo action can be used with `page'");
+ pdf_action=pdf_action_page;
+ scan_int();
+ if (cur_val <= 0)
+ pdf_error("ext1","page number must be positive");
+ scan_pdf_ext_toks();
+ }
+ else if (scan_keyword("name")) {@+
+ scan_pdf_ext_toks();
+ }
+ else if (scan_keyword("num")) {@+
+ if ((pdf_action==pdf_action_goto)&&
+ (pdf_action_file!=0))
+ pdf_error("ext1",
+ "`goto' option cannot be used with both `file' and `num'");
+ scan_int();
+ if (cur_val <= 0)
+ pdf_error("ext1","num identifier must be positive");
+ }
+ else
+ pdf_error("ext1","identifier type missing");
+ if (scan_keyword("newwindow")) {@+
+ pdf_action_new_window=1;
+ @<Scan an optional space@>;}
+ else if (scan_keyword("nonewwindow")) {@+
+ pdf_action_new_window=2;
+ @<Scan an optional space@>;}
+ else
+ pdf_action_new_window=0;
+ if ((pdf_action_new_window > 0)&&
+ (((pdf_action!=pdf_action_goto)&&
+ (pdf_action!=pdf_action_page))||
+ (pdf_action_file==0)))
+ pdf_error("ext1",
+ "`newwindow'/`nonewwindow' must be used with `goto' and `file' option");
+}
+
+@ Next we scan a destination.
+
+@d pdf_dest_xyz 0
+@d pdf_dest_fit 1
+@d pdf_dest_fith 2
+@d pdf_dest_fitv 3
+@d pdf_dest_fitb 4
+@d pdf_dest_fitbh 5
+@d pdf_dest_fitbv 6
+@d pdf_dest_fitr 7
+
+@<Implement \.{\\pdfdest}@>=
+{@+ int pdf_dest_type;
+ if (scan_keyword("struct")) {@+
+ scan_int();
+ if (cur_val <= 0)
+ pdf_error("ext1","struct identifier must be positive");
+ }
+ if (scan_keyword("num")) {@+
+ scan_int();
+ if (cur_val <= 0)
+ pdf_error("ext1","num identifier must be positive");
+ if (cur_val > max_halfword)
+ pdf_error("ext1","number too big");
+ }
+ else if (scan_keyword("name")) {@+
+ scan_pdf_ext_toks();
+ }
+ else
+ pdf_error("ext1","identifier type missing");
+ if (scan_keyword("xyz")) {@+
+ pdf_dest_type=pdf_dest_xyz;
+ if (scan_keyword("zoom")) {@+
+ scan_int();
+ if (cur_val > max_halfword)
+ pdf_error("ext1","number too big");
+ }
+ }
+ else if (scan_keyword("fitbh"))
+ pdf_dest_type=pdf_dest_fitbh;
+ else if (scan_keyword("fitbv"))
+ pdf_dest_type=pdf_dest_fitbv;
+ else if (scan_keyword("fitb"))
+ pdf_dest_type=pdf_dest_fitb;
+ else if (scan_keyword("fith"))
+ pdf_dest_type=pdf_dest_fith;
+ else if (scan_keyword("fitv"))
+ pdf_dest_type=pdf_dest_fitv;
+ else if (scan_keyword("fitr"))
+ pdf_dest_type=pdf_dest_fitr;
+ else if (scan_keyword("fit"))
+ pdf_dest_type=pdf_dest_fit;
+ else
+ pdf_error("ext1","destination type missing");
+ @<Scan an optional space@>;
+ if (pdf_dest_type==pdf_dest_fitr) {@+
+ scan_alt_rule(); /*scans | < rule spec > | to |alt_rule|*/
+ }
+}
+
+
+
+@ @<Declare procedures needed in |do_ext...@>=
+static void scan_alt_rule(void) /*scans rule spec to |alt_rule|*/
+{@+
+reswitch:
+ if (scan_keyword("width")) {@+
+ scan_normal_dimen;
+ goto reswitch;
+ }
+ if (scan_keyword("height")) {@+
+ scan_normal_dimen;
+ goto reswitch;
+ }
+ if (scan_keyword("depth")) {@+
+ scan_normal_dimen;
+ goto reswitch;
+ }
+}
+
+static void scan_annot(void) /*scan an annotation*/
+{@+
+ scan_alt_rule(); /*scans | < rule spec > |*/
+ if (scan_keyword("attr"))
+ scan_pdf_ext_toks();
+}
+
+
+
+@ @<Implement \.{\\pdffontattr}@>=
+{@+
+ scan_font_ident();
+ if (cur_val==null_font)
+ pdf_error("font","invalid font identifier");
+ scan_pdf_ext_toks();
+}
+
+@ To implement \.{\\pdffontexpand} we need the following routine:
+
+@<Declare procedures needed in |do_ext...@>=
+static void read_expand_font(void) /*read font expansion spec*/
+{@+int shrink_limit, stretch_limit, font_step;
+ internal_font_number f;
+
+ /*read font expansion parameters*/
+ scan_font_ident();
+ f=cur_val;
+ if (f==null_font)
+ pdf_error("font expansion","invalid font identifier");
+ scan_optional_equals();
+ scan_int();
+ scan_int();
+ scan_int();
+ if (scan_keyword("autoexpand")) {@+
+ @<Scan an optional space@>;
+ }
+}
+
+@ Do implement \.{\\pdfincludechars} we need the next function.
+@<Declare procedures needed in |do_ext...@>=
+static void pdf_include_chars(void)
+{@+str_number s;
+ pool_pointer k; /*running indices*/
+ internal_font_number f;
+
+ scan_font_ident();
+ f=cur_val;
+ if (f==null_font)
+ pdf_error("font","invalid font identifier");
+ scan_pdf_ext_toks();
+}
+
+@ @<Implement \.{\\pdfliteral}@>=
+{@+ int k;
+ if (scan_keyword("shipout")) k=pdf_lateliteral_node;
+ else k=pdf_literal_node;
+ if (scan_keyword("direct")) do_nothing;
+ else if (scan_keyword("page")) do_nothing;
+ else do_nothing;
+ if (k==pdf_literal_node) scan_pdf_ext_toks();
+ else scan_pdf_ext_late_toks();
+}
+
+@
+
+@d colorstack_set 0
+@d colorstack_push 1
+@d colorstack_data 1 /* last value where data field is set */
+@d colorstack_pop 2
+@d colorstack_current 3
+
+@<Implement \.{\\pdfcolorstack}@>=
+{@+int i;
+ scan_int();
+ if (scan_keyword("set")) {@+
+ i=colorstack_set;
+ }
+ else if (scan_keyword("push")) {@+
+ i=colorstack_push;
+ }
+ else if (scan_keyword("pop")) {@+
+ i=colorstack_pop;
+ }
+ else if (scan_keyword("current")) {@+
+ i=colorstack_current;
+ }
+ else{@+
+ i=-1; /*error*/
+ }
+ if (i >= 0) {@+
+ if (i <= colorstack_data) {@+
+ scan_pdf_ext_toks();
+ }
+ }
+}
+
+@ @<Implement \.{\\pdfobj}@>=
+{@+
+ if (scan_keyword("reserveobjnum")) {@+
+ @<Scan an optional space@>;
+ }
+ else{@+
+ if (scan_keyword("useobjnum")) scan_int();
+ if (scan_keyword("stream")) {@+
+ if (scan_keyword("attr")) scan_pdf_ext_toks();
+ }
+ if (scan_keyword("file")) do_nothing;
+ scan_pdf_ext_toks();
+ }
+}
+
+@ @<Implement \.{\\pdfoutline}@>=
+{@+
+ if (scan_keyword("attr")) scan_pdf_ext_toks();
+ scan_action();
+ if (scan_keyword("count")) scan_int();
+ scan_pdf_ext_toks();
+}
+
+@ @<Implement \.{\\pdfxform}@>=
+{@+
+ if (scan_keyword("attr")) scan_pdf_ext_toks();
+ if (scan_keyword("resources")) scan_pdf_ext_toks();
+ scan_register_num();
+}
+
+@ The next procedure is neede for \.{\\pdfthread} and \.{\\pdfstartthread}
+
+@<Declare procedures needed in |do_ext...@>=
+static void scan_thread_id(void)
+{@+if (scan_keyword("num")) scan_int();
+ else if (scan_keyword("name")) scan_pdf_ext_toks();
+}
+
+@ The next functions are used to implement \.{\\pdfximage}.
+@<Declare procedures needed in |do_ext...@>=
+static void scan_pdf_box_spec(void) /*scans PDF pagebox specification*/
+{@+
+ if (scan_keyword("mediabox")) return;
+ else if (scan_keyword("cropbox")) return;
+ else if (scan_keyword("bleedbox")) return;
+ else if (scan_keyword("trimbox")) return;
+ else if (scan_keyword("artbox")) return;
+}
+
+static void scan_image(void)
+{@+ scan_alt_rule();
+ if (scan_keyword("attr")) scan_pdf_ext_toks();
+ if (scan_keyword("named")) scan_pdf_ext_toks();
+ else if (scan_keyword("page")) scan_int();
+ if (scan_keyword("colorspace")) scan_int();
+ scan_pdf_box_spec();
+ scan_pdf_ext_toks();
+}
+
+
+@ The current version of the {\tt -pdf} version does not implementa all \pdfTeX\ primitives.
+ For example, the primitves \.{\\pdfcopyfont} and \.{\\letterspacefont} are not defined but
+ dummy versions could be implemented with |scan_font_ident| and |scan_int|.
+
+
+@* Index.
+Here is where you can find all uses of each identifier in the program,
+with underlined entries pointing to where the identifier was defined.
+If the identifier is only one letter long, however, you get to see only
+the underlined entries. {\sl All references are to section numbers instead of
+page numbers.}
+
+This index also lists error messages and other aspects of the program
+that you might want to look up some day. For example, the entry
+for ``system dependencies'' lists all sections that should receive
+special attention from people who are installing \TeX\ in a new
+operating environment. A list of various things that can't happen appears
+under ``this can't happen''. Approximately 40 sections are listed under
+``inner loop''; these account for about 60\pct! of \TeX's running time,
+exclusive of input and output.
diff --git a/Build/source/texk/web2c/texprofdir/texprofile.w b/Build/source/texk/web2c/texprofdir/texprofile.w
new file mode 100644
index 00000000000..42ef5ed324c
--- /dev/null
+++ b/Build/source/texk/web2c/texprofdir/texprofile.w
@@ -0,0 +1,1981 @@
+% This file is part of HINT
+% Copyright 2017-2024 Martin Ruckert,
+% Hochschule Muenchen, Lothstrasse 64, 80336 Muenchen
+%
+% 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
+% 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 holders 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 holders.
+
+\input texprofilemac.tex
+
+%% defining how to display certain C identifiers
+
+@s int8_t int
+@s uint8_t int
+@s int16_t int
+@s uint16_t int
+@s uint32_t int
+@s int32_t int
+@s uint64_t int
+@s bool int
+
+@
+
+\makeindex
+\maketoc
+\makecode
+%\makefigindex
+\titletrue
+
+\null
+
+\font\largetitlefont=cmssbx10 scaled\magstep4
+\font\Largetitlefont=cmssbx10 at 40pt
+\font\hugetitlefont=cmssbx10 at 48pt
+\font\smalltitlefontit=cmbxti10 scaled\magstep3
+\font\smalltitlefont=cmssbx10 scaled\magstep3
+
+%halftitle
+\def\raggedleft{\leftskip=0pt plus 5em\parfillskip=0pt
+\spaceskip=.3333em \xspaceskip=0.5em \emergencystretch=1em\relax
+\hyphenpenalty=1000\exhyphenpenalty=1000\pretolerance=10000\linepenalty=5000
+}
+\hbox{}
+\vskip 0pt plus 1fill
+{ \baselineskip=60pt
+ \hugetitlefont\hfill \TeX profile:\par
+ \Largetitlefont\raggedleft Profiling \TeX\ Source Files\par
+}
+\vskip 0pt plus 5fill
+\eject
+% verso of half title
+\titletrue
+\null
+\vfill
+\eject
+
+% title
+\titletrue
+\hbox{}
+\vskip 0pt plus 1fill
+{
+ \baselineskip=1cm\parindent=0pt
+ {\largetitlefont\raggedright {\TeX}profile}\par
+ \leftline{\smalltitlefont Version 1.0}
+ \vskip 10pt plus 0.5fill
+ \leftline{\smalltitlefont Profiling \TeX}
+ \vskip-3pt
+ \leftline{\smalltitlefont Source Files}
+ \vskip 10pt plus 0.5fill
+ \hskip 0pt plus 2fill{\it F\"ur Beatriz}\hskip 0pt plus 0.5fill\hbox{}
+ \bigskip
+ \vskip 10pt plus 3fill
+ \raggedright\baselineskip=12pt
+ {\bf MARTIN RUCKERT} \ {\it Munich University of Applied Sciences}\par
+ \bigskip
+ \leftline{First edition}
+ \bigskip
+% \leftline{\bf Eigendruck im Selbstverlag}
+% \bigskip
+}
+\eject
+
+% verso of title
+% copyright page (ii)
+\titletrue
+\begingroup
+\figrm
+\parindent=0pt
+%\null
+{\raggedright\advance\rightskip 3.5pc
+The author has taken care in the preparation of this book,
+but makes no expressed or implied warranty of any kind and assumes no
+responsibility for errors or omissions. No liability is assumed for
+incidental or consequential damages in connection with or arising out
+of the use of the information or programs contained herein.
+
+\bigskip
+{\figtt\obeylines\obeyspaces\baselineskip=11pt
+Ruckert, Martin.
+ Profiling \TeX\ Source Files
+ Includes index.
+% ISBN XXX-XXXXXXX-X
+}
+\bigskip
+
+{\raggedright\advance\rightskip 3.5pc plus 35pt
+\def\:{\discretionary{}{}{}}
+The internet page {\tt http:\://hint.\:userweb.\:mwn.\:de/}
+may contain current information about this book, downloadable software,
+and news.
+
+\vfill
+Copyright $\copyright$ 2024 by Martin Ruckert
+\smallskip
+All rights reserved.
+Printed by Kindle Direct Publishing.
+This publication is protected by copyright, and permission must be
+obtained prior to any prohibited reproduction, storage in
+a~retrieval system, or transmission in any form or by any means, electronic,
+mechanical, photocopying, recording, or likewise.
+To obtain permission to use material from this work, please submit a written
+request to Martin Ruckert,
+Hochschule M\"unchen,
+Fakult\"at f\"ur Informatik und Mathematik,
+Lothstrasse 64,
+80335 M\"unchen,
+Germany.
+\medskip
+{\tt martin.ruckert\:@@hm.edu}
+\medskip
+%ISBN-13: 979-854992684-4\par
+\medskip
+%First printing: August 2019\par
+\medskip
+%Last commit: \input lastcommit.tex
+\par
+}
+}
+\endgroup
+
+
+\frontmatter
+
+
+
+\plainsection{Preface}
+This program is a result not of need or necessity but of curiosity.
+Being used to have a profiler at my disposal whenever I write a program,
+I just wanted to know if a profiler for \TeX\ could be written and what it would
+tell me.
+So when I was considering what could be my contribution to the TUG 2024 conference
+in Prague, I thought ``How about writing a profiler for \TeX?''
+So I started the project and completed it just in time for the conference.
+
+The following document describes how to use {\tt texprofile}---the program that
+displays the data gathered by {\tt texprof}---in a readable form.
+
+In a future version, this document will also contain a ``User Manual''
+that contains just the information needed by the person who wants to
+find out how \TeX\ is spending its time when processing a document.
+Until then be brave and try to cope with all the extra information
+contained in the following document!
+
+\vskip 1cm
+\noindent {\it M\"unchen\hfil\break
+May 28, 2024 \hfill Martin Ruckert}
+
+
+\tableofcontent
+%\thefigindex
+
+
+\mainmatter
+\section{The |main| Program}
+
+The program presented here reads data collected by a run of {\tt texprof}
+and presents it in a readable way.
+The structure of the program is similar to a lot of \CEE\ programs:
+
+@p #include <stdlib.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <ctype.h>
+#include <string.h>
+@<declarations@>@;
+@<error handling@>@;
+@<functions@>@;
+
+int main(int argc, char *argv[])
+{ int i, k;
+ @<process the command line@>@;
+ @<read the input file@>@;
+ @<write the output file@>@;
+ return 0;
+}
+@
+
+\section{Reading the Input File}
+
+Let us start with the interesting stuff and read the input file.
+@<declarations@>=
+static char *input_file_name=NULL;
+static FILE *in=NULL;
+@
+
+First we need to open the input file. If that is successful, we check
+that there is a ``file marker'': a special sequence of characters that we expect
+at the very beginning of the input file and at various other places in the file just
+to make sure we have a file that is really meant to be read by {\tt texprofile}.
+
+@d FILE_MARKER "TEX PROF"
+
+@<read the input file@>=
+if (input_file_name==NULL)
+ commandline_error("no input file given");
+in = fopen(input_file_name,"rb");
+if (in==NULL)
+{ char *tmp=malloc(strlen(input_file_name)+7);
+ if (tmp!=NULL)
+ { strcpy(tmp,input_file_name);
+ strcat(tmp,".tprof");
+ input_file_name=tmp;
+ in = fopen(input_file_name,"rb");
+ }
+}
+if (in==NULL)
+ error("Unable to open input file");
+check_file_marker("The input file does not seem to contain TeX profile data");
+@
+
+After the initial file marker we read the whole file which is
+organized in four sections separated by file markers.
+
+@<read the input file@>=
+ @<read size data@>@;
+ check_file_marker("Invalid size data");
+ @<read file names@>@;
+ check_file_marker("Invalid file names");
+ @<read macro names@>@;
+ check_file_marker("Invalid macro names");
+ @<read timing data@>@>@;
+ check_file_marker("Invalid timing data");
+ fclose(in);
+@
+
+Before we explain how to read the different sections, we define functions
+to read multi-byte numbers in big endian format.
+
+@<functions@>=
+static uint8_t fget1(void)
+{ unsigned char buf[1];
+ if (fread(buf,1,1,in)!=1)
+ error("Unexpected end of input");
+ return (uint8_t)buf[0];
+}
+
+static uint16_t fget2(void)
+{ unsigned char buf[2];
+ if (fread(buf,1,2,in)!=2)
+ error("Unexpected end of input");
+ return ((uint16_t)buf[0]<<8)+buf[1];
+}
+
+static uint32_t fget4(void)
+{ unsigned char buf[4];
+ if (fread(buf,1,4,in)!=4)
+ error("Unexpected end of input");
+ return ((((((uint32_t)buf[0]<<8)+buf[1])<<8)+buf[2])<<8)+buf[3];
+}
+@
+
+\subsection{Reading the Size Data}
+Here are the variables for the size data and the code to read them.
+
+@<declarations@>=
+static unsigned int file_num, /*number of input files*/
+file_name_num, /*number of byte needed for the file names*/
+macro_num, /*number of macros defined*/
+macro_name_num, /*number of byte needed for the macro names*/
+stamp_num, /*number of time stamps*/
+depth_num; /*maximum stack depth*/
+@
+
+@<read size data@>=
+file_num=fget2();
+file_name_num=fget2();
+stamp_num=fget4();
+if (stamp_num==0)
+ error("The input file contains no time samples");
+macro_num=fget2();
+macro_name_num=fget4();
+depth_num=fget2();
+@
+
+We conclude the reading of size data by using these values to allocate
+properly sized arrays.
+
+@<read size data@>=
+ @<allocate arrays@>@;
+@
+
+
+\subsection{Reading the File Names}
+The first section in the input file contains the full names of all
+\TeX input files processed by {\tt texprof}.
+We need arrays of pointers to the full file names and
+to the short file names and a character array to store the actual file names.
+
+@<declarations@>=
+static char **full_file_names, **file_names, *file_name_buffer;
+#define ALLOCATE(V,S) V = calloc(S,sizeof(*V));\
+ if (V==NULL)@+ error("Out of memory for " #V "[" #S "]");
+@
+
+@<allocate arrays@>=
+ALLOCATE(full_file_names,file_num);
+ALLOCATE(file_names,file_num);
+ALLOCATE(file_name_buffer,file_name_num);
+@
+
+
+The first three files are not ordinary files but are used for special
+purposes: File number 0 is used for tokens that {\tt texprof} can not
+associate with an input file. These are usually only the tokens that
+are loaded from a \TeX\ format files since format files produced by \TeX's
+\.{\\dump} primitive do not contain file and line information.
+File number 1 is used for the ``system'' file. Its ``line numbers'' identify
+not a ``line'' but the kind of action that was taken by the system.
+File number 2 is used for the ``terminal'' file. It is used for
+text that is entered either on the command line or in an interactive run of \TeX.
+
+@d unknown_file 0 /*unknown origin*/
+@d system_file 1 /*generated by the system*/
+@d terminal_file 2 /*read from the terminal*/
+@d DIR_SEPARATOR '/'
+
+@<read file names@>=
+i=k=0;
+while (k< file_num)
+{ char c;
+ full_file_names[k]=file_names[k]=file_name_buffer+i;
+ do { c = fget1();
+ if (i>=file_name_num) error("File name buffer overflow");
+ file_name_buffer[i++]=c;
+ if (c==DIR_SEPARATOR) file_names[k]=file_name_buffer+i;
+ } while (c!=0);
+ k++;
+}
+@
+
+
+\subsection{Reading the Macro Names}
+After the file names, we read the macro names and construct the
+|macro_map| table. It maps the multi-letter macro numbers from \TeX's
+hash table to the sequential numbering used to access the
+|macro_names| array. This map is used when reading the time stamps to
+map \TeX's control sequence numbers to the sequential numbers needed
+to access the macro names. After reading the time stamps, the
+|macro_map| array can be deallocated.
+
+@<declarations@>=
+static char *macro_name_buffer, **macro_names;
+static uint16_t *macro_stack;
+static struct map
+{ uint16_t n; /* name */
+ uint16_t m; /*macro definition*/
+} *macro_map;
+@
+
+@<allocate arrays@>=
+ALLOCATE(macro_names,macro_num);
+ALLOCATE(macro_name_buffer,macro_name_num);
+ALLOCATE(macro_map,undefined_control_sequence+every_eof_text+1);
+ALLOCATE(macro_stack,depth_num+1);
+@
+
+@<read macro names@>=
+i=0;k=0;
+while (k< macro_num)
+{ int j;
+ char c;
+ j=fget2();
+ macro_map[j].n=k;
+ macro_map[j].m=0;
+ macro_names[k++]=macro_name_buffer+i;
+ do { c=fget1();
+ if (i>=macro_name_num) error("Name buffer overflow");
+ macro_name_buffer[i++]=c;
+ } while (c!=0);
+}
+for (i=1;i<=every_eof_text;i++)
+{ macro_map[undefined_control_sequence+i].n=macro_num+i;
+ macro_map[undefined_control_sequence+i].m=0;
+}
+
+@
+
+To print a macro name, we use the function |print_cs|.
+Given a control sequence number |n|,
+it can be either from an active character, or from a
+single letter control sequence or from a multi-letter
+control sequence.
+If the number |n| comes from an active character,
+we get the character by subtracting |active_base|;
+If it comes comes from a single character
+control sequence, we get the character by subtracting
+|single_base|; if |n|is equal to |null_cs|, it comes
+from the empty control sequence; and if it comes
+from a multi-letter control sequence, |n| is already
+mapped to the macro name index plus |hash_base|.
+
+@d active_base 1 /*beginning of region 1, for active character equivalents*/
+@d single_base (active_base+256) /*equivalents of one-character control sequences*/
+@d null_cs (single_base+256) /*equivalent of \.{\\csname\\endcsname}*/
+@d hash_base (null_cs+1) /*beginning of region 2, for the hash table*/
+@d hash_size 45000 /*maximum number of control sequences; it should be at most
+ about |(mem_max-mem_min)/(double)10|*/
+@d frozen_control_sequence (hash_base+hash_size) /*for error recovery*/
+@d frozen_null_font (frozen_control_sequence+11) /*permanent `\.{\\nullfont}'*/
+@d undefined_control_sequence (frozen_null_font+257) /*dummy location*/
+@d output_text 6
+@d every_eof_text 15
+
+@<functions@>=
+void print_cs(int n)
+{ if (n< hash_base)
+ { if (n < single_base)
+ { int c=n-active_base;
+ if (c<0) printf("File");
+ else if (isprint(c)) printf("%c",c); else printf("0x%02x",c);
+ }
+ else if (n < null_cs)
+ { int c=n-single_base;
+ if (isprint(c)) printf("\\%c",c); else printf("\\0x%02x",c);
+ }
+ else printf("Empty");
+ }
+ else if (n>=macro_num+hash_base)
+ printf("\\%s",every_name[n-hash_base-macro_num-output_text]);
+ else
+ printf("\\%s",macro_names[n-hash_base]);
+}
+@
+
+
+\subsection{Reading the Time Stamps}
+The last section contains the time stamps collected during the run of
+{\tt texprof}. The time stamps are stored for later processing in the
+|stamps| array. In addition to the timing data, the time stamps
+section contains information about macro calls.
+
+@<declarations@>=
+struct stamp {
+uint8_t c; /*command*/
+uint8_t f; /*file*/
+uint16_t l; /*line*/
+uint16_t d; /*depth*/
+uint16_t m; /*macro*/
+uint32_t t; /*time*/
+} *stamps;
+
+struct macro_def {
+uint16_t n; /* name */
+uint8_t f; /*file*/
+uint16_t l; /*line*/
+uint16_t link; /* link to another |macro_def|*/
+int a; /* number of concurrent activation records */
+int count; /* number of calls */
+uint16_t e; /* list of outgoing edges */
+uint64_t t; /* time used for this macro */
+uint64_t T; /* cumulative time spent in this macro */
+uint64_t s; /* start time of first activation */
+} *macro_defs;
+
+int macro_def_num; /*macros allocated*/
+int macro_def_count; /*macros used*/
+@
+
+Since the |macro_defs| array will contain only those macros that
+actually occur in the timing data, it is hard to predict the size
+of the |macro_defs| array. So either a first pass over all the timing
+data or a dynamic array is needed. Here the decision was to use
+a dynamic array.
+
+@<allocate arrays@>=
+ALLOCATE(stamps,stamp_num);
+macro_def_num=16;
+macro_def_count=1; /* zero element is a sentinel */
+ALLOCATE(macro_defs,macro_def_num);
+@
+
+@<functions@>=
+#define REALLOC(A,C,N) \
+ { N=N*1.4142136+0.5; /* $\sqrt 2$ */\
+ A=realloc(A,N*sizeof(*A));\
+ if (A==NULL) error("Out of memory"); \
+ memset(A+C,0,(N-C)*sizeof(*A)); \
+ }
+
+int new_macro_def(void)
+{ if (macro_def_count<macro_def_num)
+ return macro_def_count++;
+ REALLOC(macro_defs,macro_def_count,macro_def_num);
+ return macro_def_count++;
+}
+@
+
+When reading the timing data, we will allocate for each new macro
+call a new macro definition with its name and the file and line of
+its definition. One problem to overcome here is that \TeX\ allocates
+a macro name in the hash table only once for every name even if that
+name is reused for various macros in different files or in different lines.
+Here we need a unique number for each of these macros.
+For simplification, however, we will use the same number if two
+macros with the same name are defined in the same file and the same
+line. If you need to distinguish these macros, move the definitions
+to separate lines (or use different names because it will not only
+confuse the profiler but also the readers of your program).
+
+To convert the file, line, and control sequence number to
+a unique macro definition number, we use the |macro_map| array
+to store the macro definition number and link multiple macro
+definitions that share the same name into a linked list
+using the |link| field of the macro definition.
+
+@<functions@>=
+int get_macro_def(int f, int l, int c)
+{ int m = macro_map[c].m;
+ if (m==0)
+ { m=new_macro_def();
+ macro_map[c].m=m;
+ }
+ else
+ { loop:
+ if (macro_defs[m].f==f && macro_defs[m].l==l)
+ return m;
+ else if (macro_defs[m].link==0)
+ { int n=new_macro_def();
+ macro_defs[m].link=n;
+ m= n;
+ }
+ else
+ { m= macro_defs[m].link;
+ goto loop;
+ }
+ }
+ if (c<hash_base)
+ macro_defs[m].n=c;
+ else
+ macro_defs[m].n=hash_base+macro_map[c].n;
+ macro_defs[m].f=f;
+ macro_defs[m].l=l;
+ macro_defs[m].link=0;
+ return m;
+}
+@
+
+To print a macro name we use the function |print_macro|. Because the name
+will not uniquely identify a macro---\TeX\ allows using the same name for
+different macros---there is an option to print the file and line number in
+square brackets after the macro name.
+
+@<declarations@>=
+bool opt_macro_id=false;
+@
+
+@<process options@>=
+case 'i': opt_macro_id=true;break;
+@
+
+@<explain format options@>=
+"-i add the macros file and line numbers after the macro name\n"@;
+@
+
+@<functions@>=
+void print_macro(int i)
+{ print_cs(macro_defs[i].n);
+ if (opt_macro_id) printf(" [%d,%d]",macro_defs[i].f,macro_defs[i].l);
+}
+@
+
+Now we can read the time stamps and the changes to the macro stack.
+Each element of the timing data starts with a one byte command value
+which is followed by a variable number of data bytes.
+
+After the |system_macro_pop| command follows a two byte
+new stack depth.
+After all other commands follows a one byte file number and a two byte
+line number. The |system_macro_push| command is then followed
+by a two byte new stack depth and a two byte control sequence number.
+All remaining commands are followed by a four byte elapsed
+time in nano seconds.
+
+@<declarations@>=
+uint64_t total_time; /*sum of all command times*/
+uint32_t total_num; /*number of all command times*/
+uint64_t *file_time, *cmd_time;
+int *file_line; /* maximum line number/offsets per file */
+int *cmd_freq;
+int cur_depth;
+@
+
+@
+
+@d POP_BIT 0x80
+
+@<read timing data@>=
+ALLOCATE(file_time,file_num);
+ALLOCATE(cmd_time,cmd_num);
+ALLOCATE(file_line,file_num+1);
+ALLOCATE(cmd_freq,cmd_num);
+
+
+cur_depth=-1;
+total_time=0;
+total_num=0;
+macro_defs[0].a=1;
+i=0;
+{ fget1(); /* the initial call */
+ @<read a macro call@>@;
+ @<time a push command@>@;
+}
+while (i<stamp_num)
+{ uint8_t c=fget1();
+ switch (c)
+ { case system_macro_pop_0: break;
+ case system_macro_pop_0+1:
+ case system_macro_pop_0+2:
+ case system_macro_pop_0+3:
+ case system_macro_pop_0+4:
+ case system_macro_pop_0+5:
+ case system_macro_pop_0+6:
+ case system_macro_pop_0+7:
+ case system_macro_pop_0+8:
+ case system_macro_pop_0+9:
+ case system_macro_pop_0+10:
+ { int d=c-system_macro_pop_0;
+ @<store pop |d|@>@;
+ break;
+ }
+ case system_macro_pop_small:
+ { int d=fget1();
+ @<store pop |d|@>@;
+ break;
+ }
+ case system_macro_pop:
+ { int d=fget2();
+ @<store pop |d|@>@;
+ break;
+ }
+ case system_macro_push:
+ { @<read a macro call@>@;
+ cur_depth++;
+ macro_stack[cur_depth]=m;
+ macro_defs[m].count++;
+ macro_defs[m].a++;
+ if (macro_defs[m].a==1)
+ { macro_defs[m].s=total_time;
+ }
+ start_child(macro_stack[cur_depth-1],m);
+ }
+ break;
+default:
+ if (c&POP_BIT)
+ { int d=1;
+ @<store pop |d|@>@;
+ c=c&~POP_BIT;
+ }
+ { uint8_t f=fget1();
+ uint16_t l=fget2();
+ uint32_t t=fget4();
+ uint16_t m=macro_stack[cur_depth];
+ if (f>=file_num) error("File number out of range");
+ if (c>=cmd_num) error("Command number out of range");
+ stamps[i].c=c;
+ stamps[i].f=f;
+ if (l> file_line[f])
+ file_line[f]=l;
+ stamps[i].l=l;
+ stamps[i].t=t;
+ total_time+=t;
+ file_time[f]+=t;
+ macro_defs[m].t+=t;
+ cmd_time[c]+=t;
+ cmd_freq[c]++;
+ total_num++;
+ stamps[i].d=cur_depth;
+ stamps[i].m=m;
+ }
+ i++;
+#ifdef DEBUG
+ printf("<%d:%d:%d> %s\n",i-1,c, cur_depth, cmd_name[c]);
+#endif
+ break;
+ }
+}
+macro_defs[macro_stack[0]].T=total_time;
+macro_defs[0].count=0;
+macro_defs[0].T=0;
+macro_defs[0].f=0;
+macro_defs[0].l=0;
+
+
+#ifdef DEBUG
+ printf("Finished reading %d commands, depth=%d\n",i,cur_depth);
+#endif
+@
+
+The table of |cmd_name|s is defined in the appendix.
+
+@<store pop |d|@>=
+ stamps[i].c=system_macro_pop;
+ stamps[i].d=cur_depth-d;
+#ifdef DEBUG
+ printf("{%d:%d:%d>%d}\n",i,system_macro_pop,cur_depth+d,cur_depth);
+#endif
+ @<time a pop command@>@;
+ i++;
+@
+
+
+@<read a macro call@>=
+ int f,l,n,m;
+ f=fget1();
+ l=fget2();
+ n=fget2();
+ m=get_macro_def(f, l, n);
+ stamps[i].c=system_macro_push;
+ stamps[i].f=f;
+ stamps[i].l=l;
+ stamps[i].d=cur_depth;
+ stamps[i].m=m;
+#ifdef DEBUG
+ printf("[%d:%d<%d:",i,cur_depth, cur_depth+1);
+ print_macro(m);
+ printf("]\n");
+#endif
+ i++;
+@
+
+\section{Writing the Output}
+For simplicity and because it is often sufficient to have a
+short look at the results, we send the output to the standard output stream.
+Various tables of output can be selected using options on the command line.
+For the selection of tables, we use uppercase letters like ``{\tt R}''; to modify the
+presentation of the tables, we use lower case letters like ``{\tt s}'' or ``{\tt n}''.
+
+\subsection{The Raw Time Stamps}
+Let us start with some simple options: ``{\tt -R}'' will output
+the raw time stamps. This is useful if the profiler runs for
+a very short time or if a separate program wants to analyze the data.
+In the long list of time stamps the file names
+would only be distracting. So we print only the file numbers and
+show the file names in a separate table (see below).
+The option ``-s'' will show information about the macro stack along
+with the time stamps and the option ``-n'' will show the serial numbers,
+a feature that is mainly needed for debugging the {\tt texprofile} program.
+
+@<declarations@>=
+bool opt_raw=false;
+bool opt_raw_stack=false;
+bool opt_raw_num=false;
+@
+
+@<process options@>=
+case 'R': opt_raw=true; break;
+case 's': opt_raw_stack=true; break;
+case 'n': opt_raw_num=true; break;
+@
+
+@<explain table options@>=
+"-R show the table of raw time stamps\n"@;
+@ @<explain format options@>=
+"-s show the changes of the macro stack\n"@;
+"-n show the time stamp numbers\n"@;
+@
+
+The following table shows the time in nano seconds,
+the file number, the line number and the command executed.
+Here and in the following tables, all fields are separated
+by a TAB characters. So you can import the data in common
+office products for further processing (see also the option {\tt -m} below).
+
+@<show all time stamps if requested@>=
+if (opt_raw)
+{ Mprintf("\nAll time stamps in order of appearance:\n");
+ if (opt_raw_num) Mprintf("number\t");
+ Mprintf("file\t line\ttime/ns\tcommand\t level\tmacro\n");
+ for (i=1;i<stamp_num;i++)
+ { int m=stamps[i].m;
+ @<extract |t|, |f|, |l|, and |c| from stamp |i|@>@;
+ if (c <= system_profile_off)
+ { if (opt_raw_num) printf("%6d\t",i);
+ print_fl(f,l);
+ printf("\t%7u\t%-23s\t%5d\t",t,CMD_NAME(c),stamps[i].d);
+ print_macro(m);
+ printf("\n");
+ }
+ else if (opt_raw_stack)
+ { if (opt_raw_num) printf("%6d\t",i);
+ printf("\t\t\t%-23s\t%5d\t",CMD_NAME(c),stamps[i].d);
+ if (c==system_macro_push)
+ print_macro(m);
+ printf("\n");
+ }
+ }
+}
+@
+
+
+
+
+
+@<extract |t|, |f|, |l|, and |c| from stamp |i|@>=
+uint32_t t=stamps[i].t;
+uint16_t l=stamps[i].l;
+uint8_t f=stamps[i].f;
+uint8_t c=stamps[i].c;
+@
+
+\subsection{The Raw Macro Stack Changes}
+Further we can display the raw macro stack changes.
+@<declarations@>=
+bool opt_stack=false;
+@
+
+@<process options@>=
+case 'S': opt_stack=true; break;
+@
+
+@<explain table options@>=
+"-S show the table of macro stack changes\n"@;
+@
+
+@<show the complete macro stack if requested@>=
+if (opt_stack)
+{ int c,d,m;
+ Mprintf("\nThe macro stack and its nesting:\n"
+ "command\tdepth\tname [id]\n");
+ for(i=0;i<stamp_num;i++)
+ { c=stamps[i].c;
+ if (c<system_macro_push)
+ continue;
+ d=stamps[i].d;
+ m=stamps[i].m;
+ printf("%5d\t%s\t",d,CMD_NAME(c));
+ if (c==system_macro_push)
+ print_macro(m);
+ printf("\n");
+ }
+}
+@
+
+
+
+
+\subsection{The Summary}
+Now we can print print the totals for the \TeX\ run. Note that the option to
+show the summary is on by default, but can be switched off with the
+``{\tt -N}'' command line switch.
+
+@<declarations@>=
+bool opt_summary=true;
+@
+
+@<process options@>=
+case 'N': opt_summary=false;break;
+@
+
+@<explain table options@>=
+"-N do not show the table of global information\n"@;
+@
+
+
+@<show the total time if requested@>=
+if (opt_summary)
+{ printf("\n"
+ "Total time measured: %s\n", time_str(total_time));
+ printf("Total number of samples: %6d\n",total_num);
+ if (total_num>0)
+ printf("Average time per sample: %s\n",time_str(total_time/total_num));
+ printf("Total number of files: %5d\n",file_num);
+ printf("Total number of macros: %5d\n",macro_num);
+ printf("Maximum stack nesting depth: %5d\n",depth_num);
+}
+@
+
+To print times over a wide range in a useful format,
+we use the following function. The option ``{\tt -m}'' can be used
+to display plain nano seconds which is the preferable if you plan
+to import the data into another program for further processing.
+The macro |Mprintf| is used instead of |printf| for lines that
+should not be printed when |opt_machine| is true.
+
+@<declarations@>=
+bool opt_machine=false;
+#define Mprintf(...) opt_machine?(void)0:printf(__VA_ARGS__)
+@
+
+@<process options@>=
+case 'm': opt_machine=true;opt_summary=false;break;
+@
+
+@<explain format options@>=
+"-m optimize output for machine readability\n"@;
+@
+
+
+@<functions@>=
+char * time_str(double t)
+{ static char str[20];
+ if (opt_machine)
+ snprintf(str,20,"%12ld",(long)t);
+ else
+ { if (t<1000.0)
+ snprintf(str,20," %7.2f ns",t);
+ else if (t<1000000.0)
+ snprintf(str,20," %7.2f us",t/1000.0);
+ else if (t<1000000000.0)
+ snprintf(str,20," %7.2f ms",t/1000000.0);
+ else
+ snprintf(str,20," %7.2f s",t/1000000000.0);
+ }
+ return str;
+}
+@
+
+We continue with a function to print file and line information in a readable way.
+
+
+@<functions@>=
+void print_fl(int f, int l)
+{ if (f==system_file && !opt_machine)
+ printf("system\t%7s",line_name[l]);
+ else
+ printf("%4d\t%5d",f,l);
+}
+@
+
+
+
+
+\subsection{The File Summary}
+
+@<declarations@>=
+bool opt_files=false;
+@
+
+@<process options@>=
+case 'F': opt_files=true; break;
+@
+
+@<explain table options@>=
+"-F show all files\n"
+@
+
+
+Next we can print a summary table for the files:
+
+@<show the file summary if requested@>=
+if (opt_files)
+{ Mprintf("\nFiles in the order of appearance:\n");
+ Mprintf(" file\t lines\tpercent\t time"
+ "\tname\n");
+ for (i=0; i<file_num; i++)
+ { double p=(100.0*file_time[i])/total_time;
+ if (p>=percent_limit)
+ printf("%6d\t%6d\t%6.2f%%\t%s\t%s\n",
+ i, file_line[i],
+ p, time_str(file_time[i]),
+ full_file_names[i]);
+ }
+}
+@
+
+\subsection{The Command Summary}
+And similar for commands:
+
+@<declarations@>=
+bool opt_cmd=false;
+@
+
+@<process options@>=
+case 'C': opt_cmd=true;break;
+@
+
+@<explain table options@>=
+"-C show table of command summaries\n"@;
+@
+
+
+@<show the command summary if requested@>=
+if (opt_cmd)
+{ int *cmd_link=NULL;
+ @<sort the commands by time@>@;
+ Mprintf("\nCommand summary:\n");
+ Mprintf(" cmd\t time\tpercent\t count\t average\tname\n");
+ for (i=cmd_link[cmd_num]; i>=0; i=cmd_link[i])
+ if (cmd_freq[i]>0)
+ { printf("%4d\t%s\t%6.2f%%",
+ i, time_str(cmd_time[i]),
+ (100.0*cmd_time[i])/total_time);
+ printf("\t%6d\t %s\t%s\n",
+ cmd_freq[i],time_str(cmd_time[i]/cmd_freq[i]),
+ CMD_NAME(i));
+ }
+ free(cmd_link);
+}
+@
+
+To sort the commands in decreasing order of times,
+we allocate an array of links and sort the linked
+list using a simple insertion sort.
+We keep the links in |cmd_link| and the list head in
+|cmd_link[cmd_num]|. The end of the link is marked
+by $-1$.
+
+@<sort the commands by time@>=
+ALLOCATE(cmd_link,cmd_num+1);
+cmd_link[cmd_num]=-1;
+for (i=0; i<cmd_num; i++)
+{ int l=cmd_num;
+ uint32_t t=cmd_time[i];
+ while (cmd_link[l]>=0 && cmd_time[cmd_link[l]]>t)
+ l=cmd_link[l];
+ cmd_link[i]=cmd_link[l];
+ cmd_link[l]=i;
+}
+@
+
+\subsection{The Macro Summary}
+Here is a table of all macros called. It should be possible to sort
+this table by either the direct time or be the cumulative
+time. Further there should be an option to switch between absolute
+times and relative times as a percentage of the total time.
+
+@<declarations@>=
+bool opt_macro=false;
+@
+
+@<process options@>=
+case 'M': opt_macro=true;break;
+@
+
+@<explain table options@>=
+"-M show the table of all macros called\n"@;
+@
+
+To compute the macro summary, we traverse the time stamps while reading.
+
+If there is a push command we do this:
+@<time a push command@>=
+ cur_depth++;
+ macro_stack[cur_depth]=m;
+ macro_defs[m].count++;
+ macro_defs[m].a++;
+ if (macro_defs[m].a==1)
+ { macro_defs[m].s=total_time;
+ }
+@
+
+If there is a pop command we do this:
+
+@<time a pop command@>=
+while (cur_depth>stamps[i].d)
+{ int m=macro_stack[cur_depth];
+ if (macro_defs[m].a==1)
+ macro_defs[m].T+=total_time-macro_defs[m].s;
+ end_child(macro_stack[cur_depth-1],m);
+ macro_defs[m].a--;
+ cur_depth--;
+}
+
+@
+
+@<show the table of macros profiled if requested@>=
+if (opt_macro)
+{ sort_macros();
+ Mprintf("\nMacros profiled:\n");
+ Mprintf(" file\t line\tcalls\t time direct\t cumulative\tname\n");
+ i=0;
+ do
+ { printf("%5d\t%5d\t",macro_defs[i].f,macro_defs[i].l);
+ printf("%5d\t%s",macro_defs[i].count, time_str(macro_defs[i].t));
+ printf("\t%s\t",time_str(macro_defs[i].T));
+ print_macro(i);
+ printf("\n");
+ i=macro_defs[i].link;
+ } while (i!=0);
+}
+@
+
+To sort the edges by their cumulative time, we use a simple insertion sort.
+We use |edges[0]| as the head of a new list and insert the list of edges
+starting at |macro_defs[i].e|. While we do this we keep the estimates for
+the cumulative time of the edges that we have to compute anyway.
+
+@<functions@>=
+void sort_edges(uint16_t p)
+{ int i,j,k;
+ edges[0].sibling=0; /*head node becomes the empty list*/
+ i=macro_defs[p].e;
+ while (i!=0)
+ { uint64_t Ti=edges[i].T;
+ k=0;
+ while ((j=edges[k].sibling)!=0)
+ { uint64_t Tj=edges[j].T;
+ if (Ti<Tj)
+ k=j;
+ else
+ break;
+ }
+ edges[k].sibling=i;
+ k=i;
+ i=edges[i].sibling;
+ edges[k].sibling=j;
+ }
+ macro_defs[p].e=edges[0].sibling;
+}
+@
+
+@ To sort the macros, we use again an insertion sort. So the procedure is
+similar to the one we have just seen.
+The value 0 is used as a sentinel
+and |macro_defs[0].link| is used as the list head.
+
+
+@<functions@>=
+void sort_macros(void)
+{ int i, k, j;
+ static bool sorted=false;
+ if (sorted) return;
+ macro_defs[0].link=0;
+ sort_edges(0);
+ for (i=1;i<macro_def_count;i++)
+ { uint64_t Ti=macro_defs[i].T;
+ k=0;
+ while ((j=macro_defs[k].link)!=0)
+ { uint64_t Tj=macro_defs[j].T;
+ if (Ti<Tj)
+ k=j;
+ else
+ break;
+ }
+ macro_defs[i].link=j;
+ macro_defs[k].link=i;
+ sort_edges(i);
+ }
+ sorted=true;
+}
+@
+
+
+
+\subsection{Cumulative Information for Lines}
+On the second pass over the time samples, we collect line based
+information. To store this information, we allocate a big array
+|total_line_time| containing the information for all lines and all
+files. To access a specific line, we convert the |file_line| array
+into an array of offsets for each file and add to it the line number.
+
+@<declarations@>=
+int line_num=0;
+@
+
+@<turn |file_line| into an array of offsets
+ and compute the total number of lines@>=
+line_num=0;
+for (i=0;i<file_num;i++)
+{ int fl= file_line[i];
+ file_line[i]=line_num;
+ line_num=line_num+fl+1;
+}
+file_line[file_num]=line_num;
+@
+
+Now we are ready to compute the total time per line and the line
+frequency. For the frequency counts, we count any consecutive sequence
+of samples from the same file and line as one use of this line. So
+using multiple macros from the same line without intervening code from
+other lines will count only as a single access.
+
+We need the following arrays:
+@<declarations@>=
+static uint64_t *line_time=NULL;
+static int *line_freq=NULL;
+@
+
+The following function allocates the dynamic arrays just declared and
+fills them with content. Before doing so it checks that the arrays are
+not yet allocated because this would imply that the function was
+already called and there is nothing left to do. The information
+collected here is required for both, the |opt_lines| and the
+|opt_top_ten| tables, but there is no need to compute it twice.
+
+@<functions@>=
+void collect_line_time(void)
+{ int i, cur_f=-1,cur_l=-1;
+ if (line_time!=NULL) return; /*don't run a second time*/
+ @<turn |file_line| into an array of offsets
+ and compute the total number of lines@>@;
+ ALLOCATE(line_time,line_num);
+ ALLOCATE(line_freq,line_num);
+ for (i=0; i<stamp_num; i++)
+ { @<extract |t|, |f|, |l|, and |c| from stamp |i|@>@;
+ if (c<=system_profile_off)
+ { line_time[file_line[f]+l]+=t;
+ if (cur_f!=f || cur_l!=l)
+ { line_freq[file_line[f]+l]++;
+ cur_f=f; cur_l=l;
+ }
+ }
+ }
+}
+@
+
+Printing all lines with their time is usually not required.
+We want only those lines that have a contribution that is above
+a certain limit given as the percentage of total time.
+
+@<declarations@>=
+double percent_limit=1.0;
+bool opt_lines=false;
+@
+
+@<process options@>=
+case 'L': opt_lines=true; break;
+case 'p':{ char *endptr;
+ percent_limit=strtod(option+1, &endptr);
+ if (endptr==option+1)
+ commandline_error("-p<n> without a numeric argument <n>");
+ else
+ option=endptr-1;
+ }
+ break;
+@
+
+@<explain table options@>=
+"-L show the table of times per input line\n"@;
+@ @<explain format options@>=
+"-p<n> don't show information for items with cumulative time below <n> percent\n"@;
+@
+
+
+@<show lines above limit if requested@>=
+if (opt_lines)
+{ uint64_t limit= total_time*percent_limit/100.0;
+ int k;
+ collect_line_time();
+ Mprintf("\nLine summary:\n");
+ if (percent_limit>0)
+ Mprintf("Only files and lines above %.2f%%:\n",percent_limit);
+ for (k=i=0; i<file_num; i++)
+ { if (file_time[i]<=limit)
+ k=file_line[i+1];
+ else
+ { printf("%s\n",file_names[i]);
+ printf(" file\t line\tpercent\t time"
+ "\t count\t average\n");
+ printf("%6d\t\t%6.2f%%\t%s\n",
+ i, (100.0*file_time[i])/total_time,time_str(file_time[i]));
+
+ for (;k<file_line[i+1]; k++)
+ { uint64_t t=line_time[k];
+ if (line_freq[k]>0 && t>=limit)
+ { if (i==system_file)
+ printf("\t%7s",line_name[k-file_line[i]]);
+ else
+ printf("\t%6d", k-file_line[i]);
+ printf("\t%6.2f%%\t%s",
+ 100.0*t/total_time,time_str(t));
+ printf("\t%6d\t%s",line_freq[k],time_str(t/line_freq[k]));
+ printf("\n");
+ }
+ }
+ printf("\n");
+ }
+ }
+}
+@
+
+\subsection{Finding the ``Top Ten'' Lines}
+Now that we have for each line in one of the input files the total
+time used by it, we can sort the lines by their time use. It is
+neither efficient nor useful to sort all the lines, but it sufficient
+to find the ``top ten''.
+
+@<declarations@>=
+static uint64_t *tt_time;
+static int *tt_file, *tt_line;
+static int tt=10+1, tt_count;;
+bool opt_top_ten;
+@
+
+@<process options@>=
+case 'T': opt_top_ten=true;break;
+case 't': { char *endptr;
+ tt=strtol(option+1, &endptr,10)+1;
+ if (endptr==option+1)
+ commandline_error("-t<n> without a numeric argument <n>");
+ else if (tt<2 || tt>101)
+ commandline_error("-t<n> with <n> out of bounds");
+ option=endptr-1;
+ }
+ break;
+@
+
+@<explain table options@>=
+"-T show the table of the top 10 input lines\n"@;
+@ @<explain format options@>=
+"-t<n> replace 10 by n (2<=n<=100, default 10) for the top 10 input lines\n"@;
+@
+
+
+@<find the top ten lines@>=
+ALLOCATE(tt_time,tt);
+ALLOCATE(tt_file,tt);
+ALLOCATE(tt_line,tt);
+tt_time[0]=0xFFFFFFFFFFFFFFFF;
+tt_file[0]=0;
+tt_line[0]=0;
+tt_time[1]=0;
+tt_file[1]=0;
+tt_line[1]=0;
+tt_count=2;
+{ int f,l;
+ for (f=l=0; f< file_num; f++)
+ { for (;l<file_line[f+1]; l++)
+ { uint32_t t=line_time[l];
+ if (t>tt_time[tt_count-1])
+ @<insert |t| into the top ten list@>@;
+ }
+ }
+}
+@
+
+Inserting a new time into the top ten array using insertion sort.
+
+@<insert |t| into the top ten list@>=
+{
+ if (tt_count<tt)tt_count++;
+ i= tt_count-1;
+ while (tt_time[i-1]<t)
+ { i--;
+ tt_time[i+1]=tt_time[i];
+ tt_file[i+1]=tt_file[i];
+ tt_line[i+1]=tt_line[i];
+ }
+ tt_time[i]=t;
+ tt_file[i]=f;
+ tt_line[i]=l-file_line[f];
+}
+@
+
+@<show the top ten lines@>=
+if (opt_top_ten)
+{ collect_line_time();
+ @<find the top ten lines@>@;
+ Mprintf("\nThe top ten lines:\n");
+ Mprintf(" file\t line\tpercent\t absolute"
+ "\t count\t average\tfile\n");
+ for (i=1; i< tt_count; i++)
+ { int freq=line_freq[tt_line[i]+file_line[tt_file[i]]];
+ uint32_t t =tt_time[i];
+ if (t>0 && freq>0)
+ { print_fl(tt_file[i],tt_line[i]);
+ printf("\t%6.2f%%\t%s", 100.0*t/total_time,time_str(t));
+ printf("\t%6d\t%s\t%s\n",freq, time_str(t/freq), file_names[tt_file[i]]);
+ }
+ }
+}
+@
+
+
+\subsection{The Call Graph}
+For each macro, we know already how often it was called, the
+cumulative time, and the time directly spent in the macro. For the
+call graph, we want to know for each macro which child macros it did
+call, how often it did call each child, how much this child
+contributed directly and cumulative to the macro.
+From this data we could derive which parent macros
+called it, how often it was called by each parent, and how much time
+it contributed directly and cumulatively to the parent.
+We need to form linked lists of edges, so
+each edge has a link to its siblings.
+
+Following the conventions of {\tt gprof}, call counts are
+displayed in the format $n/m$ where $n$ is the number of calls on the
+edge of the call graph under consideration and $m$ is the total number
+of calls.
+
+The table of macro definitions is already available, so what we need
+is just a collection of the edges with counts, direct times and
+cumulative times. It is possible to estimate direct and
+cumulative times from the counts and the timing information we have
+already for each macro, but this method is not very precise.
+
+Since macro calls can form recursive loops, the parent is not only
+an ancestor of the child, the child may also be an ancestor of the parent
+and its siblings. (Sounds horrible, but we are talking about macro calls.)
+Therefore the cumulative time spent in executing a child macro
+might include time spent in the parent macro or in its sibling macros.
+Therefore we need to split the cumulative time spent in a macro into
+two components: The cumulative time |T| that can be attributed to this macro alone,
+and the time that is spent in this macro but is attributed to the
+parent or a sibling. The call the later time the ``loop time'' |L|.
+To compute |T| and |L|, we keep track of the activation count |a|
+of each child, and keep two auxiliary variables |ts| and |Ts| when
+we push the first activation record, and update |T| and |L| only
+when we pop the last activation of the macro.
+The variable |ts| is used to record the |total_time|
+and |Ts| is used to record the sum of the time directly attributed to the parent
+and the cumulative times attributed to all siblings.
+Recomputing these values when we pop the final activation,
+we can obtain the time |dt| passed since its first activation,
+and the time |dT| attributed to parent and siblings since then.
+We use |dT| to update |L| and |dt-dT| to update |T|.
+
+Here is the declaration of an |edge|:
+@<declarations@>=
+struct edge {
+uint16_t child, sibling;
+int count, a;
+uint64_t T, L, ts, Ts;
+} *edges;
+int edges_count, edges_num;
+@
+
+The total number of outgoing and incoming edges can only be estimated.
+So a dynamic allocation is used.
+
+@<allocate arrays@>=
+edges_num=1024;
+edges_count=1; /* zero element is a sentinel and a head */
+ALLOCATE(edges,edges_num);
+@
+
+@<functions@>=
+uint16_t new_edge(void)
+{ if (edges_count<edges_num)
+ return edges_count++;
+ REALLOC(edges,edges_count,edges_num);
+ return edges_count++;
+}
+@
+
+When a parent macro with definition |p| calls
+a child macro with definition |c|, the |start_child| function
+is called after the new child macro is put on top of the macro stack.
+We first search the existing edges of the parent for an existing
+edge, possibly from a previous invocation, before we add a new edge.
+Once the edge is found, we increment |count| and |a|.
+If |a| changes from zero to one we update |T|, |L|, |ts|, and |Ts|
+as described above.
+
+@<functions@>=
+void start_child(int p, int c)
+{ int e =macro_defs[p].e;
+ if (e==0)
+ { e =new_edge();
+ macro_defs[p].e=e;
+ edges[e].child=c;
+ goto found;
+ }
+ do
+ { if (edges[e].child==c)
+ goto found;
+ else if (edges[e].sibling==0)
+ { int s=new_edge();
+ edges[e].sibling=s;
+ e = s;
+ edges[e].child=c;
+ goto found;
+ }
+ else
+ e = edges[e].sibling;
+ } while (true);
+found:
+ edges[e].a++;
+ edges[e].count++;
+ if (edges[e].a==1)
+ { uint16_t i;
+ edges[e].ts=total_time;
+ edges[e].Ts=macro_defs[p].t;
+ for (i= macro_defs[p].e;i!=0;i=edges[i].sibling)
+ if (i!=e)
+ edges[e].Ts +=edges[i].T;
+ }
+}
+@
+
+
+When we pop a child macro, we use the following function
+to update |a| and in the case of |a==1| also |T| and |L|.
+
+@<functions@>=
+void end_child(int p, int c)
+{ uint16_t e = macro_defs[p].e;
+ while (e!=0)
+ { if (edges[e].child==c)
+ goto found;
+ else
+ e = edges[e].sibling;
+ }
+found:
+ if (edges[e].a==1)
+ { uint16_t i;
+ uint64_t dt;
+ uint64_t dT;
+ dT=macro_defs[p].t;
+ for (i= macro_defs[p].e;i!=0;i=edges[i].sibling)
+ if (i!=e)
+ dT +=edges[i].T;
+ dt=total_time-edges[e].ts;
+ dT=dT-edges[e].Ts;
+ edges[e].L+=dT;
+ edges[e].T+=dt-dT;
+ }
+ edges[e].a--;
+}
+@
+
+The information contained in the edges gives
+a good view of the call graph. It can be shown using the ``{\tt -G}'' option.
+
+@ @<declarations@>=
+bool opt_graph=false;
+@
+
+@<process options@>=
+case 'G': opt_graph=true; break;
+@
+
+@<explain table options@>=
+"-G show the table of the macro call graph\n"@;
+@
+
+@<show the macro call graph if requested@>=
+if (opt_graph)
+{ sort_macros();
+ Mprintf("\nThe macro call graph:\n"
+ " time\t loop\tpercent\t count/total\tchild\n");
+ i=macro_defs[0].link;
+ do
+ { int e;
+ uint64_t Ti=macro_defs[i].T;
+ if (100.0*Ti/total_time<percent_limit)
+ break;
+ print_macro(i);
+ printf("\n");
+ printf("%s\t \t%6.2f%%\t",
+ time_str(Ti), 100.0*Ti/total_time);
+ if (opt_machine)
+ printf("\t%6d\t",macro_defs[i].count);
+ else
+ printf(" * \t");
+ print_macro(i);
+ printf("\n");
+ printf("%s\t \t", time_str(macro_defs[i].t));
+ if (Ti!=0)
+ printf("%6.2f%%\t", 100.0*macro_defs[i].t/Ti);
+ else
+ printf("\t");
+ if (opt_machine)
+ printf("%6d\t\t",macro_defs[i].count);
+ else
+ printf("%7d \t",macro_defs[i].count);
+ print_macro(i);
+ printf("\n");
+ e =macro_defs[i].e;
+ while (e!=0)
+ { int c = edges[e].child;
+ int n =edges[e].count;
+ int m =macro_defs[c].count;
+ uint64_t Te=edges[e].T;
+ int64_t L=edges[e].L;
+ double p= 100.0*Te/Ti;
+ if (p>=percent_limit)
+ { printf("%s\t",time_str(Te));
+ if (L==0 && !opt_machine)
+ printf(" \t");
+ else
+ printf("%s\t", time_str(L));
+ if (Ti!=0)
+ printf("%6.2f%%\t", p);
+ else
+ printf("\t");
+ if (opt_machine)
+ printf("%6d\t%6d\t",n,m);
+ else
+ printf("%6d/%-6d\t",n,m);
+ print_macro(c);
+ printf("\n");
+ }
+ e = edges[e].sibling;
+ }
+ printf("\n");
+ i=macro_defs[i].link;
+ } while (i!=0);
+}
+@
+
+
+\subsection{All Tables}
+
+Now its time to bring all the different output tables together
+in a useful sequence.
+
+@<process options@>=
+case 'A': opt_files=opt_summary=
+ opt_cmd=opt_top_ten=
+ opt_graph=true; break;
+@
+
+@<explain table options@>=
+"-A show all tables (equal to -TGFC) tables\n"@;
+@
+
+@<write the output file@>=
+@<show all time stamps if requested@>@;
+@<show the complete macro stack if requested@>@;
+@<show the command summary if requested@>@;
+@<show the file summary if requested@>@;
+@<show lines above limit if requested@>@;
+@<show the top ten lines@>@;
+@<show the table of macros profiled if requested@>@;
+@<show the macro call graph if requested@>@;
+@<show the total time if requested@>@;
+@
+
+
+\section{Processing the Command Line}
+
+
+@<process the command line@>=
+i=1;
+while (i<argc)
+{ char *option;
+ if (argv[i][0]=='-')
+ { option=argv[i]+1;
+ while (*option!=0)
+ { switch(*option)
+ { @<process options@>@;
+ default: commandline_error("unknown option");
+ }
+ option++;
+ }
+ }
+ else if (input_file_name==NULL)
+ input_file_name=argv[i];
+ else
+ commandline_error("multiple input files given");
+ i++;
+}
+@
+
+
+Sometimes an error means that the program should also explain how to use it.
+
+@<functions@>=
+void explain_usage(void)
+{ fprintf(stderr,
+ "Use: "@;
+ "texprofile [-options] <input file>\n"@;
+ "options:\n"@;
+ @<explain general options@>@;
+ "\n"
+ @<explain table options@>@;
+ "\n"
+ @<explain format options@>@;
+ "\n"
+ );
+ exit(1);
+}
+@
+
+A simple option that most users will use occasionaly is the {\tt -?} option.
+This option can also be look like {\tt --help}, {\tt -help} or {\tt -h}.
+
+@<process options@>=
+case 'h':
+case '?': explain_usage(); break;
+@
+
+@<explain general options@>=
+"-? display this help and exit\n"@;
+"--help display this help and exit\n"@;
+@
+
+The help option should also be available as a long option:
+
+@<process options@>=
+case '-':
+ option++;
+ @<process long options@>@;
+ else commandline_error("unknown long option");
+ break;
+@
+
+@<process long options@>=
+ if (strcmp(option,"help")==0) explain_usage();
+@
+
+The only other long option currently supported is the {\tt --version} option.
+
+@d VERSION_STR "texprofile version 1.0"
+
+@<process long options@>=
+ if (strcmp(option,"version")==0)
+ { printf(VERSION_STR "\n");
+ exit(1);
+ }
+@
+
+@<explain general options@>=
+"--version output version information and exit\n"@;
+@
+
+\section{Error Handling}
+If an error occurs this program will print an error message
+and terminate. There is no attempt to recover from errors.
+
+@<error handling@>=
+int error(char *msg)
+{ fprintf(stderr,"texprofile: %s\n", msg);
+ exit(1);
+ return 0;
+}
+@
+
+The program is a little bit more verbose if there is an error in the
+command line.
+
+@<error handling@>=
+int commandline_error(char *msg)
+{ fprintf(stderr,"texprofile: %s\n", msg);
+ fprintf(stderr,"Try 'texprofile --help' for more information.\n");
+ exit(1);
+ return 0;
+}
+@
+
+
+
+
+The input file should start with a marker: the ASCII codes of ``TEX PROF''.
+The same marker is used later to separate the different sections of the file.
+
+@<functions@>=
+void check_file_marker(char *msg)
+{ char marker[8];
+ if (fread(marker,1,8,in)!=8)
+ error("Unexpected end of input");
+ if(strncmp(marker,FILE_MARKER,8)!=0)
+ error(msg);
+}
+@
+
+\appendix
+
+\section{The Table of Command Names}
+To print commands nicely, here is an array of command names. Some
+of the commands also have names to be able to test for them;
+the definitions are taken from {\tt texprof.w}.
+
+@d max_command 100 /*the largest command code seen at |big_switch|*/
+
+@d system_cmd (max_command+1) /*pseudo command value*/
+@d system_profile_on (system_cmd+1)
+@d system_profile_off (system_cmd+2)
+@d system_macro_push (system_cmd+3)
+@d system_macro_pop (system_cmd+4)
+@d system_macro_pop_small (system_cmd+5)
+@d system_macro_pop_0 (system_cmd+6)
+
+@<declarations@>=
+static char *cmd_name[]= {@/
+"relax", /*do nothing ( \.{\\relax} )*/
+"left_brace", /*beginning of a group ( \.\{ )*/
+"right_brace", /*ending of a group ( \.\} )*/
+"math_shift", /*mathematics shift character ( \.\$ )*/
+"tab_mark", /*alignment delimiter ( \.\&, \.{\\span} )*/
+"car_ret/" /*end of line ( |carriage_return|, \.{\\cr}, \.{\\crcr} )*/
+"out_param", /*output a macro parameter*/
+"mac_param", /*macro parameter symbol ( \.\# )*/
+"sup_mark", /*superscript ( \.{\char'136} )*/
+"sub_mark", /*subscript ( \.{\char'137} )*/
+"ignore/" /*characters to ignore ( \.{\^\^@@} )*/
+"endv", /*end of \<$v_j$> list in alignment template*/
+"spacer", /*characters equivalent to blank space ( \.{\ } )*/
+"letter", /*characters regarded as letters ( \.{A..Z}, \.{a..z} )*/
+"other_char", /*none of the special character types*/
+"active/" /*characters that invoke macros ( \.{\char`\~} )*/
+ "par/" /*end of paragraph ( \.{\\par} )*/
+ "match", /*match a macro parameter*/
+"comment/" /*characters that introduce comments ( \.\% )*/
+ "end_match/" /*end of parameters to macro*/
+ "stop", /*end of job ( \.{\\end}, \.{\\dump} )*/
+"invalid_char/" /*characters that shouldn't appear ( \.{\^\^?} )*/
+ "delim_num", /*specify delimiter numerically ( \.{\\delimiter} )*/
+
+"char_num", /*character specified numerically ( \.{\\char} )*/
+"math_char_num", /*explicit math code ( \.{\\mathchar} )*/
+"mark", /*mark definition ( \.{\\mark} )*/
+"xray", /*peek inside of \TeX\ ( \.{\\show}, \.{\\showbox}, etc.~)*/
+"make_box", /*make a box ( \.{\\box}, \.{\\copy}, \.{\\hbox}, etc.~)*/
+"hmove", /*horizontal motion ( \.{\\moveleft}, \.{\\moveright} )*/
+"vmove", /*vertical motion ( \.{\\raise}, \.{\\lower} )*/
+"un_hbox", /*unglue a box ( \.{\\unhbox}, \.{\\unhcopy} )*/
+"un_vbox", /*unglue a box ( \.{\\unvbox}, \.{\\unvcopy} \dots )*/
+
+"remove_item", /*nullify last item ( \.{\\unpenalty}, \dots )*/
+"hskip", /*horizontal glue ( \.{\\hskip}, \.{\\hfil}, etc.~)*/
+"vskip", /*vertical glue ( \.{\\vskip}, \.{\\vfil}, etc.~)*/
+"mskip", /*math glue ( \.{\\mskip} )*/
+"kern", /*fixed space ( \.{\\kern} )*/
+"mkern", /*math kern ( \.{\\mkern} )*/
+"leaders/shipout", /*use a box ( \.{\\shipout}, \.{\\leaders}, etc.~)*/
+"halign", /*horizontal table alignment ( \.{\\halign} )*/
+"valign", /*vertical table alignment ( \.{\\valign} )*/
+"no_align", /*temporary escape from alignment ( \.{\\noalign} )*/
+"vrule", /*vertical rule ( \.{\\vrule} )*/
+"hrule", /*horizontal rule ( \.{\\hrule} )*/
+"insert", /*vlist inserted in box ( \.{\\insert} )*/
+"vadjust", /*vlist inserted in enclosing paragraph ( \.{\\vadjust} )*/
+"ignore_spaces", /*gobble |spacer| tokens ( \.{\\ignorespaces} )*/
+"after_assignment", /*save till assignment is done ( \.{\\afterassignment} )*/
+"after_group", /*save till group is done ( \.{\\aftergroup} )*/
+"break_penalty", /*additional badness ( \.{\\penalty} )*/
+"start_par", /*begin paragraph ( \.{\\indent}, \.{\\noindent} )*/
+"ital_corr", /*italic correction ( \.{\\/} )*/
+"accent", /*attach accent in text ( \.{\\accent} )*/
+"math_accent", /*attach accent in math ( \.{\\mathaccent} )*/
+"discretionary", /*discretionary texts ( \.{\\-}, \.{\\discretionary} )*/
+"eq_no", /*equation number ( \.{\\eqno}, \.{\\leqno} )*/
+"left_right", /*variable delimiter ( \.{\\left}, \.{\\right} ) \dots )*/
+"math_comp", /*component of formula ( \.{\\mathbin}, etc.~)*/
+"limit_switch", /*diddle limit conventions ( \.{\\displaylimits}, etc.~)*/
+"above", /*generalized fraction ( \.{\\above}, \.{\\atop}, etc.~)*/
+"math_style", /*style specification ( \.{\\displaystyle}, etc.~)*/
+"math_choice", /*choice specification ( \.{\\mathchoice} )*/
+"non_script", /*conditional math glue ( \.{\\nonscript} )*/
+"vcenter", /*vertically center a vbox ( \.{\\vcenter} )*/
+"case_shift", /*force specific case ( \.{\\lowercase}, \.{\\uppercase}~)*/
+"message", /*send to user ( \.{\\message}, \.{\\errmessage} )*/
+"extension", /*extensions to \TeX\ ( \.{\\write}, \.{\\special}, etc.~)*/
+"in_stream", /*files for reading ( \.{\\openin}, \.{\\closein} )*/
+"begin_group", /*begin local grouping ( \.{\\begingroup} )*/
+"end_group", /*end local grouping ( \.{\\endgroup} )*/
+"omit", /*omit alignment template ( \.{\\omit} )*/
+"ex_space", /*explicit space ( \.{\\\ } )*/
+"no_boundary", /*suppress boundary ligatures ( \.{\\noboundary} )*/
+"radical", /*square root and similar signs ( \.{\\radical} )*/
+"end_cs_name", /*end control sequence ( \.{\\endcsname} )*/
+"min_internal/" /*the smallest code that can follow \.{\\the}*/
+ "char_given", /*character code defined by \.{\\chardef}*/
+"math_given", /*math code defined by \.{\\mathchardef}*/
+"last_item", /*most recent item ( \.{\\lastpenalty},\dots )*/
+
+"toks_register", /*token list register ( \.{\\toks} )*/
+"assign_toks", /*special token list ( \.{\\output}, \.{\\everypar}, etc.~)*/
+"assign_int", /*user-defined integer ( \.{\\tolerance}, \.{\\day}, etc.~)*/
+"assign_dimen", /*user-defined length ( \.{\\hsize}, etc.~)*/
+"assign_glue", /*user-defined glue ( \.{\\baselineskip}, etc.~)*/
+"assign_mu_glue", /*user-defined muglue ( \.{\\thinmuskip}, etc.~)*/
+"assign_font_dimen", /*user-defined font dimension ( \.{\\fontdimen} )*/
+"assign_font_int", /*user-defined font integer ( \.{\\hyphenchar}, \dots )*/
+"set_aux", /*specify state info ( \.{\\spacefactor}, \.{\\prevdepth} )*/
+"set_prev_graf", /*specify state info ( \.{\\prevgraf} )*/
+"set_page_dimen", /*specify state info ( \.{\\pagegoal}, etc.~)*/
+"set_page_int", /*specify state info ( \.{\\deadcycles}, \dots )*/
+"set_box_dimen", /*change dimension of box ( \.{\\wd}, \.{\\ht}, \.{\\dp} )*/
+"set_shape", /*specify fancy paragraph shape ( \.{\\parshape} ) \dots )*/
+"def_code", /*define a character code ( \.{\\catcode}, etc.~)*/
+"def_family", /*declare math fonts ( \.{\\textfont}, etc.~)*/
+"set_font", /*set current font ( font identifiers )*/
+"def_font", /*define a font file ( \.{\\font} )*/
+"internal_register", /*internal register ( \.{\\count}, \.{\\dimen}, etc.~)*/
+"advance", /*advance a register or parameter ( \.{\\advance} )*/
+"multiply", /*multiply a register or parameter ( \.{\\multiply} )*/
+"divide", /*divide a register or parameter ( \.{\\divide} )*/
+"prefix", /*qualify a definition ( \.{\\global}, \.{\\long}, \.{\\outer} etc.~) )*/
+"let", /*assign a command code ( \.{\\let}, \.{\\futurelet} )*/
+"shorthand_def", /*code definition ( \.{\\chardef}, \.{\\countdef}, etc.~)*/
+"read_to_cs", /*read into a control sequence ( \.{\\read}, etc.~)*/
+"def", /*macro definition ( \.{\\def}, \.{\\gdef}, \.{\\xdef}, \.{\\edef} )*/
+"set_box", /*set a box ( \.{\\setbox} )*/
+"hyph_data", /*hyphenation data ( \.{\\hyphenation}, \.{\\patterns} )*/
+"set_interaction", /*define level of interaction ( \.{\\batchmode}, etc.~)*/
+
+"system", /*system start*/
+"profile on", /*switching on profile ( \.{\\profileon} )*/
+"profile off", /*switching on profile ( \.{\\profileoff} )*/
+"call", /*macro call*/
+"pop", /*macro return*/
+"pop n", /*macro return*/
+"pop 0", /*macro return*/
+"pop 1", /*macro return*/
+"pop 2", /*macro return*/
+"pop 3", /*macro return*/
+"pop 4", /*macro return*/
+"pop 5", /*macro return*/
+"pop 6", /*macro return*/
+"pop 7", /*macro return*/
+"pop 8", /*macro return*/
+"pop 9", /*macro return*/
+"pop 10", /*macro return*/
+"unknown" /*should not happen*/
+};
+
+static const int cmd_num=sizeof(cmd_name)/sizeof(*cmd_name);
+
+#define CMD_NAME(N) ((N)<cmd_num?cmd_name[N]:"unknown")
+
+static char *line_name[]= {@/ /*for the system pseudo file*/
+"unknown", /*as it says*/
+"start", /*time before executing the first command*/
+"end", /*time after executing the last command*/
+"shipout", /*time spent writing the dvi file*/
+"linebrk", /*time spent on line breaking*/
+"initrie", /*time spent on setting up hyphenation*/
+"buildpg", /*time spent in |build_page|*/
+"inputln", /*time spent in |input_ln|*/
+"insert", /*time spent on tokens inserted by \TeX*/
+};
+
+static char *every_name[]={@/ /* names for output, everypar, etc. */
+"output", /*for output routines*/
+"everypar", /*for \.{\\everypar}*/
+"everymath", /*for \.{\\everymath}*/
+"everydisplay", /*for \.{\\everydisplay}*/
+"everyhbox", /*for \.{\\everyhbox}*/
+"everyvbox", /*for \.{\\everyvbox}*/
+"everyjob", /*for \.{\\everyjob}*/
+"everycr", /*for \.{\\everycr}*/
+"mark", /*for \.{\\topmark}, etc.*/
+"everyeof" /*for \.{\\everyeof}*/
+};
+
+static const int sys_line_num=sizeof(line_name)/sizeof(*line_name);
+@
+
+%\plainsection{References}
+
+%{\baselineskip=11pt
+%\def\bfblrm{\small\rm}%
+%\def\bblem{\small\it}%
+%\bibliography{../hint}
+%\bibliographystyle{plain}
+%}
+
+%\plainsection{Index}
+%{
+%\def\_{{\tt \UL}} % underline in a string
+%\catcode`\_=\active \let_=\_ % underline is a letter
+%\input texprofile.ind
+%}
+
+\write\cont{} % ensure that the contents file isn't empty
+% \write\cont{\catcode `\noexpand\@=12\relax} % \makeatother
+\closeout\cont% the contents information has been fully gathered
diff --git a/Build/source/texk/web2c/texprofdir/texprofilemac.tex b/Build/source/texk/web2c/texprofdir/texprofilemac.tex
new file mode 100644
index 00000000000..dbfce0880a4
--- /dev/null
+++ b/Build/source/texk/web2c/texprofdir/texprofilemac.tex
@@ -0,0 +1,1535 @@
+% Copyright 2017-2024 Martin Ruckert, Hochschule Muenchen, Lothstrasse 64, 80336 Muenchen
+%
+% 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
+% 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 holders 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 holders.
+
+\input idxmac.tex
+\let\displayverbatimfont=\tt
+
+% V E R B A T I M . T E X
+%
+% THIS SET OF MACROs IS TAKEN FROM D.E.K.'s TeXBook
+% AND PROVIDES AN EASY WAY OF TYPESETTING TEXTS VERBATIM:
+%
+% 1. \verbatim<character><text without that character><character>
+% causes the text to be set verbatim using \tt font;
+% 2. if the text uses up all the alphabet, \doubleverbatim macro
+% can be used instead; this is similar to the previous one but
+% a pair of characters is now used as a delimiter;
+% 3. in case of emergency \tripleverbatim macro may be of help...
+%
+%---------------------------------------------
+\def\uncatcodespecials % see D.E.K., pp. 344 and 380
+ {\def\do##1{\catcode`##1=12}\dospecials}%
+%---------------------------------------------
+{\catcode`\^^I=\active \gdef^^I{\ \ \ \ }% TAB character is replaced by
+ % 4 spaces; it is better than
+ % nothing, but it does not mimic
+ % true tabbing satisfactorily---maybe
+ % some nice day...
+ \catcode`\`=\active\gdef`{\relax\lq}}% this line inhibits Spanish
+ % ligatures ?` and !` of \tt font
+\def\setupverbatim % see D.E.K., p. 381
+ {\tt %
+ \spaceskip=0pt \xspaceskip=0pt % just in case...
+ \catcode`\^^I=\active %
+ \catcode`\`=\active %
+ \def\par{\leavevmode\endgraf}% this causes that empty lines aren't
+ % skipped
+ \obeylines \uncatcodespecials \obeyspaces}%
+{\obeyspaces \global\let =\ }% this causes that leading blanks aren't
+ % skipped; cf. also def's of \space, \endgraf,
+ % \lq, \obeyspaces, and \obeylines,
+ % D.E.K., pp. 351--352
+%---------------------------------------------
+% see D.E.K., p. 382
+\def\doverbatim#1{\def\next##1#1{##1\endgroup}\next}%
+\def\verbatim{\begingroup\setupverbatim\doverbatim}%
+%----------
+\def\dodoubleverbatim#1#2{\def\next##1#1#2{##1\endgroup}\next}%
+\def\doubleverbatim{\begingroup\setupverbatim\dodoubleverbatim}%
+%----------
+\def\dotripleverbatim#1#2#3{\def\next##1#1#2#3{##1\endgroup}\next}%
+\def\tripleverbatim{\begingroup\setupverbatim\dotripleverbatim}%
+%---------------------------------------------
+
+
+
+%%
+%% special treatment for @
+%%
+
+\def\makeatletter{\catcode`\@=11\relax}
+\def\makeatother{\catcode`\@=12\relax}
+\makeatletter
+
+%%
+%% Conditionals
+%%
+
+\newif\ifhint
+\newif\ifbook
+\newif\ifpdf
+
+% HINT
+\expandafter\ifx\csname HINTversion\endcsname\relax
+ \hintfalse
+\else
+ \hinttrue
+\fi
+
+% PDF
+\expandafter\ifx\csname pdfliteral\endcsname\relax
+ \pdffalse
+\else
+ \pdftrue
+\fi
+
+% Book
+\expandafter\ifx\csname book\endcsname\relax
+ \ifhint\bookfalse
+ \else\ifpdf\bookfalse % This might change
+ \else\bookfalse
+ \fi\fi
+\else
+\booktrue
+\hintfalse
+\pdffalse
+\fi
+
+\message{Book: \ifbook true \else false \fi}
+\message{PDF: \ifpdf true \else false \fi}
+\message{HINT: \ifhint true \else false \fi}
+
+%%
+%% Additional fonts
+%%
+
+\font\tenss=cmss10 % used for the HINT name
+\font\tenssbx=cmssbx10 % used for the bold HINT name
+\font\largess=cmss12 scaled\magstep1 % used for large HINT name
+\font\largessbx=cmssbx10 scaled\magstep2 % used for large bold HINT name
+\font\manual=manfnt % font used for the METAFONT logo, etc.
+\let\sf\tenss
+\def\bf{\let\sf\tenssbx\fam\bffam\tenbf}
+
+
+%% Large Font for sections
+%\font\largebf=cmb14 scaled\magstep0
+%\font\largebf=cmb12 scaled\magstep1
+\font\largebf=cmbx12 scaled\magstep1
+%\font\largebf=cmbx8 scaled\magstep3
+%\font\largeit=cmti14 scaled\magstep0
+\font\largeit=cmti12 scaled\magstep1
+%\font\largett=cmtt14 scaled\magstep0
+\font\largett=cmtt12 scaled\magstep1
+%\font\largerm=cmr14 scaled\magstep0
+\font\largerm=cmr12 scaled\magstep1
+% for mathbolditalic
+%\font\tenmib=cmmib10 scaled\magstep0
+\font\tenmib=cmmib10 scaled\magstep0
+\font\largemib=cmmib10 scaled\magstep1
+
+%% Small Fonts for Figure and Table descriptions
+\font\figbf=cmbx9 scaled\magstep0
+\font\figit=cmti9 scaled\magstep0
+\font\figmit=cmmi9 scaled\magstep0
+\font\figsy=cmsy9 scaled\magstep0
+\font\figtt=cmtt9 scaled\magstep0
+\font\figrm=cmr9 scaled\magstep0
+% for mathbolditalic
+\font\figmib=cmmib9 scaled\magstep0
+
+\def\largebold{\let\bf\largebf\let\sf\largessbx\bf}
+
+\def\large{\def\rm{\fam0\largerm}\let\bf\largebold\let\it\largeit\let\tt\largett\let\mib\largemib\let\sf\largess%
+\textfont0=\largerm\textfont1=\largeit
+\setbox\strutbox=\hbox{\vrule height9.5pt depth5.0pt width\z@}\rm}
+
+\def\small{\def\rm{\fam0\figrm}\let\bf\figbf\let\it\figit\let\tt\figtt\let\mib\figmib%
+\textfont0=\figrm\textfont1=\figmit\textfont2=\figsy%
+\setbox\strutbox=\hbox{\vrule height9.0pt depth4.5pt width\z@}\rm}
+
+\let\tiny\eightrm
+\let\mib\tenmib
+
+
+%%
+%% Logos
+%%
+
+%\def\Pascal/{{\mc P\kern-.05emascal\spacefactor1000}}
+\def\CEE{{\mc C\spacefactor1000}}
+\def\Pascal{\leavevmode\hbox{\mc P\kern-.05emascal}}
+\def\PDF{\leavevmode\hbox{\mc PDF}}
+\def\WEB{\leavevmode\hbox{\tt WEB\spacefactor1000}}
+\def\GNU{\leavevmode\hbox{\mc GNU}}
+\def\Prote{{\tenrm P\kern-0.1em R\kern-0.15em\raise.11ex\hbox{o}%
+ \kern-0.22em T\kern-0.05em E}}
+\def\eTeX{$\varepsilon$-\TeX}
+\def\HINT{\leavevmode\hbox{\sf HINT\spacefactor1000}}
+\def\cweb{\leavevmode\hbox{\tt cweb}}
+\def\web2w{\leavevmode\hbox{\tt web2w}}
+\def\LaTeX{L\kern-.36em\raise.3ex\hbox{\sc A}\kern-.15em\TeX}%
+\def\MF{{\manual META}\-{\manual FONT}}%
+\def\TL{\TeX~Live}
+\def\kpse{\leavevmode\hbox{\tt kpathsearch\spacefactor1000}}
+\def\TUB{{\sl TUGboat\/}}
+\def\TUG{\TeX\ \UG}
+\def\tug{\acro{TUG}}
+\def\UG{Users Group}
+\ifpdf
+\sanitizecommand{\TeX}{TeX}
+\sanitizecommand{\eTeX}{eTeX}
+\sanitizecommand{\Prote}{PRoTE}
+\fi
+%%
+%% Special controlsequences
+%%
+
+\def\abs#1{\left|#1\right|}
+\def\<#1>{$\langle\,$#1$\,\rangle$}
+\def\registered{{\ooalign{\hfil\raise.07ex\hbox{\sevenrm R}\hfil\crcr\Orb}}}
+\def\hair{\kern.05em\relax} % teeny tiny space
+\def\center#1{\bigskip\line{\hfil#1\hfil}\bigbreak}
+\chardef\VB=`\| % vertical bar character in a string
+\def\^{\ifmmode\mathchar"222 \else\char`^ \fi}%from webmac.tex: pointer or hat
+\ifhint
+\def\_{{\tt\UL}} % make underline an ordinary character for better searching
+\fi
+
+% Bibtex needs this
+\def\mbox#1{\leavevmode\hbox{#1}}
+
+% URLs
+{\gdef\urldot{.}\catcode`.=13
+ \gdef\urlslash{/}\catcode`/=13
+ \gdef\urlspecials{\def.{\urldot\penalty 0}\def/{\urlslash\penalty 0}}
+}
+\def\url{\bgroup\hskip 1cm plus 5cm\penalty -100\hskip -1cm plus -5cm\tt
+ \catcode`_=12\catcode`\~=12\catcode`.=13\catcode`/=13\urlspecials}
+\def\endurl{\egroup}
+
+
+% for the index
+\def\see#1#2{\hskip 0pt plus 100pt\penalty 0\hskip 0 pt plus -100pt{\it see\/} #1}
+
+% typewriter type for strings
+\def\.#1{\leavevmode\hbox{\tt
+ \let\\=\BS % backslash in a string
+ \let\{=\LB % left brace in a string
+ \let\}=\RB % right brace in a string
+ \let\~=\TL % tilde in a string
+ \let\ =\SP % space in a string
+ \let\_=\UL % underline in a string
+ \let\&=\AM % ampersand in a string
+ \let\^=\CF % circumflex in a string
+ \let\|=\VB % vertical bar in a string
+ #1\kern.05em}}
+
+%{\rm\gdef\strutdepth{\dp\strutbox}}
+
+
+%%
+%% Environments
+%%
+
+% Quotations (form the standard)
+\def\beginquote{\bgroup\narrower\noindent\sl}
+\def\endquote{\egroup}
+
+% Enumerate
+\newcount\enum
+%other styles use \medskip instead of \par
+\def\enumerate{\par\bgroup\advance\leftskip by\parindent\enum=0%
+ \def\item{\advance\enum by 1\par\enumdest\smallskip\noindent\hbox to 0pt{\hss\the\enum.~}\ignorespaces}}
+\def\endenumerate{\medskip\egroup\noindent}
+
+%Itemize
+\def\itemize{\par\bgroup\advance\leftskip by\parindent\relax%
+ \def\item{\par\smallskip\noindent\llap{$\bullet$\enspace}\ignorespaces}}
+\def\enditemize{\medskip\egroup\noindent}
+
+%Float
+\long\def\float#1{\midinsert\parindent 0pt\relax#1\endinsert}
+%\def\float#1{\midinsert
+%\hsize\pagewidth
+%\parindent 0pt\vbox{\hbox{\kern-\marginwidth\vbox{#1}}}\endinsert}
+
+%%
+%% Redefining cwebmac.tex macros
+%%
+
+%defining how to output terminals and nonterminals
+\def\ts#1{\ifmmode\hbox{\sc #1}\else{\sc #1}\fi}% terminal symbol
+\def\nts#1{{\sl #1\/}}% nonterminal symbol
+% redefining the box around verbatim code
+\def\vb#1{\hbox{\strut\.{#1}}}
+% regular expressions and actions
+\newbox\rebox
+\def\re#1{\leavevmode\setbox\rebox\hbox{#1\hfill}\ifdim\wd\rebox<7em\wd\rebox=7em\fi\box\rebox\quad}
+\def\ac{\global\ind=10\hangindent=\ind em\relax}
+\def\eac{\global\ind=3\hangindent=\ind em\relax}
+% yskip is usually a smallskip = 3pt pus 1pt minus 1pt
+% I give it some extra stretchability
+%\def\yskip{\vskip 3pt plus 3pt minus 1pt}
+\let\yskip\smallbreak
+
+% redefine \5 for an optional break without the 2.5em reduced indentation
+%\def\5{\hskip 0pt plus 2.5em\penalty-1\hskip 0pt plus -2.5em\kern0.5em\ignorespaces}% optional break
+%redefine \5 to give some shrinkability
+% from hitex and hint style:
+%\def\5{\hfil\penalty-1\hskip 0pt minus 6pt\hfilneg\kern2.5em\copy\bakk\ignorespaces}% optional break
+\def\5{\hfil\penalty10\hfilneg\hskip 0pt minus 6pt\kern2.5em\copy\bakk\ignorespaces}% optional break
+\def\J{}% dont show @& in the tex output
+
+
+\def\Y{\par\setsecstartref\yskip}
+
+% start sections
+\def\stsec{\rightskip=0pt % get out of C mode (cf. \B)
+ \sfcode`;=1500 \pretolerance 200 \hyphenpenalty 50 \exhyphenpenalty 50 %
+}
+
+% the box for the points to operator "->" in C
+\setbox\MGbox=\hbox{$\rightarrow$}
+
+% how to display NULL
+\def\NULL{\tt NULL}
+
+%other styles make this a \par
+\def\note#1#2.{}
+%\Y\noindent{\hfill%
+% \baselineskip10pt\tiny#1~\ifacro{\pdfnote#2.}\else#2\fi.\par}}
+
+% put the section number to the right
+\newif\ifshowsecstartref \showsecstartreffalse
+\newif\ifsetsecstartref \setsecstartreffalse
+
+\def\secstartref{% used in \MN
+ \ifshowsecstartref
+ \vadjust{\vtop to 0pt{\vss\hbox to \hsize{\hfill\tiny(\secstar)}\kern 0pt}}
+ \global\showsecstartreffalse
+ \fi
+}
+
+\def\setsecstartref{% used in \Y
+ \ifsetsecstartref
+ \codedest
+ \global\setsecstartreffalse
+ \fi
+}
+
+
+%other styles make this minus 10pt
+\def\B{\rightskip=0pt plus 100pt minus 0pt % go into C mode
+ \sfcode`;=3000
+ \pretolerance 10000
+ \hyphenpenalty 1000 % so strings can be broken (discretionary \ is inserted)
+ \exhyphenpenalty 1000
+ \global\ind=2 \1\ \unskip
+}
+
+% formating of C comments
+\def\C#1{\5\hfill$/\ast\,${\cmntfont #1}$\,\ast/$}
+
+% section begin
+\def\M#1{\MN{#1}\ifon\stsec\smallskip
+%\everypar{{\setbox0=\lastbox}\everypar{}}% no indentation in the next paragraph
+\noindent\ignorespaces}% beginning of section
+
+\def\N#1#2#3.{\MN{#1}\ifon\stsec\smallskip
+%\everypar{{\setbox0=\lastbox}\everypar{}}% no indentation in the next paragraph
+\noindent{\bf#3\quad}\ignorespaces}% beginning of section
+
+\def\MN#1{\smallskip
+ {\xdef\secstar{#1}\let\*=\empty\xdef\secno{#1}}%
+ \gdef\thecode{#1}% common code for \M, \N
+ \ontrue\showsecstartreftrue\setsecstartreftrue}
+
+% Used in section text
+\def\U{\rightnote{Used in}} % xref for use of a section
+\def\Us{\rightnote{Used in}} % xref for uses of a section
+% replacing \note in \U and \Us by \rightnote
+%\def\rightnote#1#2.{\vskip-\baselineskip\vtop to 0pt{\vss\hbox to \hsize{\hfill
+% \tiny#1~\ifacro{\pdfnote#2.}\else#2\fi.}\kern 0pt}}
+%\def\rightnote#1#2.{\vskip-\baselineskip\hbox to \hsize{\hfill
+% \tiny#1~\ifacro{\pdfnote#2.}\else#2\fi.}}
+%\def\rightnote#1#2.{\penalty1000\discretionary{}{\hbox{}}{\kern 2em}\penalty1000\hfill
+% \hbox{\tiny#1~\ifacro{\pdfnote#2.}\else#2\fi.}}
+\def\rightnote#1#2.{%
+ \penalty1000\discretionary{}{\hbox{}}{\kern 4em}\penalty1000\hfill
+ \hskip -4em plus 4em\hbox{\tiny #1~\codenote#2.}.}
+
+\def\X#1:#2\X{\setsecstartref
+ \ifmmode\gdef\XX{\null$\null}\else\gdef\XX{}\fi %$% section name
+ \XX$\langle\,${\let\I=\ne#2\kern.5em\coderef{#1}$\rangle$\XX}%
+ \secstartref}
+
+
+% how to display hex numbers
+\def\hex{\hbox{$^{\scriptstyle\#}$\tt\aftergroup}} % CWEB style
+
+\def\(#1){} % this is used to make section names sort themselves better
+
+
+%%
+%% Dimensions to achive the desired layout
+%%
+
+
+% the other variables are defined in cwebmac.tex
+\newdimen\headheight
+\newdimen\footheight
+\newdimen\leftmargin
+\newdimen\rightmargin
+\newdimen\topmargin
+\newdimen\bottommargin
+\newdimen\marginwidth
+
+\parskip 0pt plus .8pt
+
+\leftmargin=22mm
+\rightmargin=22mm
+\topmargin=15mm
+\bottommargin=27mm
+\marginwidth=0pt % we do not have notes in the margin
+
+\headheight=12pt % Text in Head
+\advance\headheight+13pt % Abstand 1pt, Linie 0.47pt und Abstand zum Text 11.53pt
+\footheight=0pt % Text in Footer
+
+\pagewidth=6.69in
+\advance\pagewidth-\leftmargin % left margin/offset
+\advance\pagewidth-\rightmargin % left margin/offset
+
+% 6.69in = 169.926
+% - 2*22mm = 125.926mm
+%
+% 9.61in = 244.094
+% - 15mm -27 mm = 202.094
+% (12+13)pt = 8.786 mm
+% -8.786 = 193.308 mm
+
+\fullpageheight=9.61in
+\advance\fullpageheight-\topmargin% topmargin
+\advance\fullpageheight-\bottommargin% bottommargin
+
+\pageheight=\fullpageheight
+\advance\pageheight-\headheight
+\advance\pageheight-\footheight
+
+\def\setpage{\hsize\pagewidth\advance\hsize-\marginwidth\vsize\pageheight} % use after changing page size
+
+\setpage
+
+% setting the papersize for postscript and pdf
+
+\ifbook
+ %\overfullrule=0pt
+ \pdfpageheight=9.61in
+ \pdfpagewidth=6.69in
+ \pdfhorigin=\leftmargin
+ \pdfvorigin=\topmargin
+ \pdfcompresslevel=9
+ \pdfdecimaldigits=4
+ \pdfpkresolution=1200
+ \pdfimageresolution=1200
+\else\ifpdf
+ \pdfpageheight=9.61in
+ \pdfpagewidth=6.69in
+ \pdfhorigin=\leftmargin
+ \pdfvorigin=\topmargin
+ \pdfcompresslevel=9
+ \pdfdecimaldigits=4
+ \pdfpkresolution=1200
+ \pdfimageresolution=1200
+\else
+ \special{papersize=6.69in,9.61in}% postscript und ps2pdf
+\fi\fi
+
+
+
+%%
+%% Page layout
+%%
+
+\long\def\leftmark#1#2{#1}
+\long\def\subsectionmark#1#2{#2}
+\long\def\rightmark#1#2{\if0#1\else\expandafter\subsectionmark\botmark\fi}
+
+\def\lheader{\mainfont\strut
+\thepageno\hfill\expandafter\leftmark\firstmark} % top line on left-hand pages
+
+\def\rheader{\mainfont\strut
+\expandafter\rightmark\firstmark\hfill\thepageno} % top line on right-hand pages
+
+\let\page=\pagebody
+%\raggedbottom
+\normalbottom
+%\def\page{\box255 }\normalbottom % faster, but loses plain TeX footnotes
+\def\normaloutput#1#2#3{\ifodd\pageno\hoffset=\pageshift\fi
+ \shipout\vbox{
+ \vbox to\fullpageheight{\pagelabel
+ \iftitle\global\titlefalse
+ \else\hbox{\vbox to \headheight{
+ \hbox to \pagewidth{\ifodd\pageno #3\else#2\fi}
+ \vskip 1pt\relax
+ \nointerlineskip
+ \hrule height .47pt
+% \hbox{\hrule}%fill\psfig{file=image/topline.eps}\hfil}%
+ \vfil}}%
+ \fi
+ \nointerlineskip
+ \hbox{\kern\marginwidth\vbox to \pageheight{#1}}}} % parameter #1 is the page itself
+ \global\advance\pageno by1}
+
+\def\nomarginoutput#1#2#3{%
+ \ifodd\pageno\hoffset=\pageshift\fi
+ \shipout\vbox to\fullpageheight{\pagelabel\nointerlineskip
+ \iftitle\global\titlefalse
+ \hbox{\vbox to \headheight{\vfil}}
+ \else
+ \hbox{\vbox to \headheight{\nointerlineskip
+ \hbox to \pagewidth{\ifodd\pageno#3\else#2\fi}%
+ \vskip 1 pt
+ \nointerlineskip
+ \hrule height .47pt
+% \hbox{\psfig{silent=,bbllx=0pt,bblly=0pt,bburx=368pt,bbury=2pt,file=image/topline.eps}\hfil}%
+ \vfil}}%
+ \fi\nointerlineskip #1 % parameter #1 is the page itself
+% \vss
+% \hbox to \pagewidth{\ifodd\pageno\else#2\fi}%
+% \nointerlineskip
+% \hbox{\psfig{file=image/topline.eps}\hfil}%
+}%
+\global\advance\pageno by1}
+
+%% switch to global nomargin
+\let\normaloutput\nomarginoutput
+\def\sectionbox#1{\hbox{#1}}
+\marginwidth=0mm
+\setpage
+
+
+%%
+%% Frontmatter and mainmatter
+%%
+\pageno=1
+\def\thepageno{\romannumeral\pageno}
+\def\frontmatter{\pageno=4\def\thepageno{\romannumeral\pageno}}
+\def\mainmatter{
+\mark{{\sectionname}{\subsectionname}}
+%\message{Mark {\sectionname}{\subsectionname}}
+\vfil\break\ifodd\pageno\pageno=1\else\pageno=0\fi
+\def\thepageno{\the\pageno}%
+\sectioncount=0
+\mark{{0}{0}}%
+%\hbox{Mark empty empty}%
+\gdef\sectionname{}
+\gdef\subsectionname{}
+\gdef\codetitle{}
+\gdef\subcodetitle{}
+}
+
+
+
+
+%%
+%% Sections
+%%
+
+\newcount\sectioncount
+\sectioncount=0
+\newcount\subsectioncount
+\subsectioncount=0
+\newcount\subsubsectioncount
+\subsubsectioncount=0
+
+\newskip\abovesecskip
+\newskip\belowsecskip
+\newskip\abovesubsecskip
+\newskip\belowsubsecskip
+
+%\abovesecskip= 6ex plus 1ex minus .2ex %space above the section
+%\belowsecskip=3.7ex plus .2ex% space after section
+\abovesecskip= 0.15\vsize plus 5ex minus 1ex %space above the section
+\belowsecskip=10pt plus 2pt% space after section
+\abovesubsecskip=9pt plus 4pt minus 2pt%space above the section
+\belowsubsecskip=3pt plus 1pt minus 0.5pt% space after section
+
+
+\newif\ifappendix
+\appendixfalse
+
+\mark{{0}{0}}\vskip 0pt minus 100pt
+%\hbox{Mark empty empty}%
+\def\thesection{\the\sectioncount}
+\def\topsection{\the\sectioncount}
+
+%\def\sectionbox#1{\hskip-\marginwidth\hbox to \marginwidth{#1\hfil}}
+
+\def\heading#1#2{%
+\def\secno{{\noindent\large\bf\strut{#1}#2}}%
+\par\ifhmode\unskip\fi% end paragraph and remove vertical space
+\penalty-500
+\vskip 0pt plus 72pt%allow some empty space at the bottom
+\penalty-500
+\vskip 0pt plus -72pt% room for stetching and a page break
+\hbox{}\vskip\abovesecskip% space above the section
+%%\titletrue % omits page header for section
+%\hbox{Mark null null}%
+\mark{{0}{0}}%
+\secno% The section title
+\mark{{\sectionname}{\sectionname}}%
+%\message{Mark heading {\sectionname}{\subsectionname}}
+\nobreak\vskip\belowsecskip% space after section
+\everypar{{\setbox0=\lastbox}\everypar{}}% no indentation in the next paragraph
+\ignorespaces}
+
+
+
+\newcount\plaincount
+\def\plainsection#1{%
+\mark{{\sectionname}{\subsectionname}}%
+%\message{Mark plain {\sectionname}{\subsectionname}}
+\subsubsectioncount=0\subsectioncount=0%
+\advance\plaincount by 1\relax
+\def\thesection{}
+\sectionpage%
+\gdef\sectionname{#1}%
+\gdef\subsectionname{#1}%
+\gdef\codetitle{#1}%
+\gdef\subcodetitle{#1}%
+\tocsection{0}{}{#1}
+\heading{}{#1}%
+}
+
+
+\gdef\sectionname{}
+\gdef\subsectionname{}
+\gdef\codetitle{}%
+\gdef\subcodetitle{}
+
+\def\sectionpage{%
+\vfil\break
+\ifodd\pageno\else
+\hbox{}%
+\titletrue
+\fi
+\vfil\break
+}
+
+
+\def\section#1{%
+\mark{{\sectionname}{\subsectionname}}%
+%\message{Mark section {\sectionname}{\subsectionname}}
+\subsubsectioncount=0\subsectioncount=0%
+\advance\sectioncount by 1%updating counts
+\let\thesection\topsection
+\sectionpage%
+\gdef\sectionname{\topsection\quad#1}%
+\gdef\subsectionname{\thesection\quad#1}%
+\gdef\codetitle{#1}%
+\gdef\subcodetitle{#1}%
+\ifnum\sectioncount=1\ifappendix\tocsection{0}{}{Appendix}\heading{}{Appendix}\fi\fi
+\tocsection{0}{\thesection}{#1}%
+\heading{\thesection~}{#1}%
+}
+
+
+\def\subsection#1{%
+\subsubsectioncount=0\advance\subsectioncount by 1%updating counts
+\def\thesection{\topsection.\the\subsectioncount}
+\def\secno{{\noindent\bf\strut\sectionbox{\thesection~}#1}}%
+\par\ifhmode\unskip\fi% end paragraph and remove vertical space
+\tocsection{1}{\thesection}{#1}%
+\penalty-200
+\vskip 0pt plus 36pt% allow some empty space at the bottom
+\penalty-200
+\vskip 0pt plus -36pt% room for stetching and a page break
+\gdef\subsectionname{\thesection\quad#1}%
+\mark{{\sectionname}{\subsectionname}}%
+%\message{Mark subsection {\sectionname}{\subsectionname}}
+\vskip\abovesubsecskip% space above the subsection
+\secno% The subsection title
+\gdef\subcodetitle{#1}%
+%\mark{{\sectionname}{\subsectionname}}%
+%\hbox{Mark {\sectionname}{\subsectionname}}%
+\nobreak\vskip\belowsubsecskip% space after subsection
+\everypar{{\setbox0=\lastbox}\everypar{}}% no indentation in the next paragraph
+\ignorespaces}
+
+
+\def\subsubsection#1{%
+%\mark{{\sectionname}{\subsectionname}}%
+%\hbox{Mark {\sectionname}{\subsectionname}}%
+ \advance\subsubsectioncount by 1%updating counts
+\def\thesection{\topsection.\the\subsectioncount.\the\subsubsectioncount}
+\def\secno{{\noindent\it\strut\sectionbox{\thesection~}#1}}%
+\par\ifhmode\unskip\fi% end paragraph and remove vertical space
+\tocsection{2}{\thesection}{#1}%
+\penalty-200
+\vskip 0pt plus 36pt% allow some empty space at the bottom
+\penalty-200
+\vskip 0pt plus -36pt% room for stetching and a page break
+\vskip\abovesubsecskip% space above the subsection
+\secno% The subsubsection title
+\nobreak\vskip\belowsubsecskip% space after subsection
+%\everypar{{\setbox0=\lastbox}\everypar{}}% no indentation in the next paragraph
+%\noindent
+\ignorespaces}
+
+%%
+%% Appendix
+%%
+
+\def\Alphanum#1{%
+ \ifcase#1\or A\or B\or C\or D\or E\or F\or G\or H\or I\or J\or
+ K\or L\or M\or N\or O\or P\or Q\or R\or S\or T\or U\or V\or W\or X\or
+ Y\or Z\else\number#1\fi}
+
+\def\appendix{%
+\mark{{\sectionname}{\subsectionname}}%
+%\message{Mark appendix {\sectionname}{\subsectionname}}
+\sectioncount=0
+\subsectioncount=0
+\gdef\thesection{\Alphanum\sectioncount}%
+\gdef\topsection{\Alphanum\sectioncount}%
+\appendixtrue
+}
+
+%%
+%% Crossreference of Identifiers and Sections
+%%
+\iftrue %% No Crossreference of identifiers
+ \def\inx{\def\lr{L}} % this tells whether the left or right column is next
+\else
+\def\inx{%
+ \hsize=\pagewidth
+ \def\page{\box255 } \normalbottom
+ \output{\ifpagesaved\normaloutput{\box\sbox}\lheader\rheader\fi
+ \global\setbox\sbox=\page \global\pagesavedtrue}
+ \pagesavedfalse
+ \plainsection{Crossreference of Identifiers}% we are beginning the index
+ \mark{{0}{0}}%
+%\hbox{Mark empty empty}%
+ \eject % eject the page-so-far and predecessors
+ \setbox\sbox\vbox{\unvbox\sbox} % take it out of its box
+ \vsize=\pageheight \advance\vsize by -\ht\sbox % the remaining height
+ \hsize=.5\pagewidth \advance\hsize by -10pt
+ % column width for the index (20pt between cols)
+ \parfillskip 0pt plus .6\hsize % try to avoid almost empty lines
+ \def\lr{L} % this tells whether the left or right column is next
+ \output{\if L\lr\global\setbox\lbox=\page \gdef\lr{R}
+ \else\nomarginoutput{\vbox to\pageheight{\box\sbox\vss
+ \hbox to\pagewidth{\box\lbox\hfil\page}}}\lheader\rheader
+ \global\vsize\pageheight\gdef\lr{L}\global\pagesavedfalse
+ \mark{{Crossreference of Identifiers}{Crossreference of Identifiers}}%
+ \fi}
+ \parfillskip 0pt plus 1fil
+ \let\topsecno=\nullsec
+ \message{Crossreference of Identifiers:}
+ \parskip 0pt plus .5pt
+ \outer\def\I##1, ##2.{\par\hangindent2em\noindent##1:\kern1em
+ \ifbook##2\else\ifpdf\pdfnote##2.\else##2\fi\fi.} % index entry
+ \def\[##1]{$\underline{##1}$} % underlined index item
+ \rm \rightskip0pt plus 2.5em \tolerance 10000 \let\*=\lapstar
+ \hyphenpenalty 10000 \parindent0pt
+ \small
+ \readindex
+}
+\fi
+
+\def\fin{%
+ \if L\lr\mark{{}{}}\fi
+ \vfill\eject % complete the current column.
+ \mark{{}{}}
+ \if R\lr\null\vfill\eject\fi % if necessarry add a right column
+ \setpage
+ \def\page{\box255 } \normalbottom
+ \output={\nomarginoutput\page\lheader\rheader}
+}
+\ifbook
+\let\crosssections=\relax
+\else
+\def\crosssections{
+ \plainsection{Crossreference of Code}% this is done when we are ending the index
+ \parindent 0pt
+ \parfillskip 0pt plus 1fil
+ \let\topsecno=\nullsec
+ \message{Crossreference of Code:}
+ \def\note##1##2.{\hfil\penalty-1\hfilneg\quad{\tiny##1~\ifbook##2\else
+ \ifpdf{\pdfnote##2.}\else
+ \ifhint\codenote##2.\else##2.\fi\fi\fi}}
+ \def\Q{\note{Cited in section}} % crossref for mention of a section
+ \def\Qs{\note{Cited in sections}} % crossref for mentions of a section
+ \def\U{\note{Used in}} % crossref for use of a section
+ \def\Us{\note{Used in}} % crossref for uses of a section
+ \def\I{\par\hangindent 2em}\let\*=*
+ \def\X##1:##2\X{\ifmmode\gdef\XX{\null$\null}\else\gdef\XX{}\fi %$% section name
+ \XX$\langle\,${\let\I=\ne##2}$\,\rangle$\XX\quad{\tiny Defined in~\codenote##1.}}
+ \readsections
+}
+\fi
+
+
+
+%%
+%% Table of Content
+%%
+
+% we separate the identification of a section used in links
+%from the identification on paper, because plain sections dont
+%have the latter.
+\newcount\toccount
+\toccount=0
+
+\def\tocsection#1#2#3{% depth sectionnumber sectiontitle
+\global\advance\toccount by 1%
+\newdest{SC.\the\toccount}%
+\ifhint
+ \immediate\write\cont{\noexpand\ZZ {#1}{#2}{#3}% write to contents file
+ {\noexpand\thepageno}{\the\toccount}}
+\else
+\edef\next{\write\cont{\noexpand\ZZ {#1}{#2}{#3}% write to contents file
+ {\noexpand\thepageno}{\the\toccount}}}\next % \tocline{depth}{sec}{title}{page}{toccount}
+\fi
+}
+
+\output{
+ \ifhint
+ \shipout\box255\relax% otherwise I loose open, write, or close commands
+ \else
+ \setbox0=\page % the first page is garbage
+ \fi
+% \openout\cont=\contentsfile
+% \write\cont{\catcode `\noexpand\@=11\relax} % \makeatletter
+ \global\output{\normaloutput\page\lheader\rheader}}
+
+
+\newbox\tocbox
+\def\maketoc{%
+ \typeout{Reading table of contents}
+ \setbox\tocbox\vbox{%
+ \let\ZZ=\tocline
+ \ifbook\else\ifpdf\startpdf\fi\fi
+ \readcontents\relax
+ }
+ \typeout{Writing \contentsfile}
+ \immediate\openout\cont=\contentsfile\relax
+ \immediate\write\cont{\catcode `\noexpand\@=11\relax}
+}
+
+\def\tableofcontent{\plainsection{Contents}\ifhint\HINTdest name {HINT.home}\fi
+\unvbox\tocbox
+}
+
+
+\def\tocline#1#2#3#4#5{% depth sectionnumber sectiontitle page toccount
+\ifnum#1=0 \smallskip\vskip 0pt plus 12pt\penalty -100\vskip 0pt plus -12pt\fi
+\noindent
+\line{%
+\ifnum#1=0%
+ \hbox to 2em{\bf#2\hfill}{\bf#3~}\hfill
+ \hbox to 2em{\hss\bf\tocref{#1}{#3}{#4}{#5}}%
+\else \ifnum#1=1%
+ \kern 2em\hbox to 3em{\rm#2\hfill}{\rm#3~}\leaders\hbox to .5em{.\hfil}\hfill
+ \hbox to 2em{\hss\tocref{#1}{#3}{#4}{#5}}%
+\else
+ \kern 2em\hbox to 5em{\rm#2\hfill}{\it#3~}\leaders\hbox to .5em{.\hfil}\hfill
+ \hbox to 2em{\hss\tocref{#1}{#3}{#4}{#5}}\fi\fi
+}}
+
+
+
+%%
+%% Figures and Tables
+%%
+
+\def\fig#1{\global\advance\figcount by 1%
+\ifvmode\noindent\fi
+\def\captype{Fig}%
+\capskip=1em%
+\vbox{\nointerlineskip\figdest
+\hbox{\noindent#1\hskip -2pt\hskip 0pt plus 2pt minus 2pt}%
+\medskip
+\rlap{\small\unhbox\capbox}%
+}}
+
+\def\tab#1{%
+\ifvmode\noindent\fi
+\def\captype{Tab}%
+\capskip=0pt%
+\vbox{#1\medskip\rlap{\small\unhbox\capbox}}}
+
+\def\table#1#2{\global\advance\tabcount by 1%
+\ifvmode\noindent\fi
+\def\captype{Tab}%
+\capskip=0pt%
+\vbox{\offinterlineskip\tabdest
+ \halign{\large\strut
+ \vrule##&&\quad\hfil##\hfil\quad\vrule\cr
+ \noalign{\hrule}#2\noalign{\hrule}}%
+ \medskip
+ \tabcaption{#1}%
+ \rlap{\small\unhbox\capbox}%
+ }%
+}
+
+\def\captype{}
+\newdimen\capskip
+\capskip=1em
+\newbox\capbox
+\setbox\capbox\hbox{}
+\newcount\figcount
+\figcount=0
+\newcount\tabcount
+\tabcount=0
+
+\def\nextfigcaption#1{\small\strut\it\hskip\capskip\figindex{\captype}{\the\figcount}{#1}\captype.~\the\figcount:}
+
+\def\caption#1{\global\setbox\capbox\hbox{\nextfigcaption{#1} #1}}
+
+\def\nexttabcaption#1{\small\strut\it\hskip\capskip\tabindex{\captype}{\the\tabcount}{#1}\captype.~\the\tabcount:}
+\def\tabcaption#1{\global\setbox\capbox\hbox{\nexttabcaption{#1} #1}}
+
+
+%\long\def\leftfig#1#2#3{%
+%\ifvmode\noindent\fi
+%\def\captype{Fig}%
+%\capskip=1em%
+%\vbox{%
+% \tabskip=0pt\halign to \hsize{%
+% \vtop{\vskip 0pt\parskip=0pt\hsize=62.5mm\relax
+% ##}\tabskip=5mm plus 2pt minus 2pt&
+% \vtop{\vskip 0pt\parskip=0pt\hsize=62.5mm\relax##}\tabskip=0pt\cr
+% #1&\capskip=0pt\nextfigcaption{#2} #3\cr}\medbreak}}
+
+\long\def\leftright#1#2#3#4{%
+\ifvmode\noindent\else\par\noindent\fi
+\vbox{\nointerlineskip
+ \advance\hsize by -\leftskip\advance\hsize by -\rightskip%
+ \leftskip=0pt\rightskip=0pt%
+ \hbox to \hsize{\strut
+ \raise\ht\strutbox
+ \vtop{\vskip 0pt\parskip=0pt\hsize=#1\hsize\advance\hsize by -2.5mm\relax
+ \noindent\strut\ignorespaces#3\vfill}\hfil
+ \raise\ht\strutbox
+ \vtop{\vskip 0pt\parskip=0pt\hsize=#2\hsize\advance\hsize by -2.5mm\relax
+ \noindent\strut\ignorespaces#4\vfill}}}}
+
+
+\long\def\leftfig#1#2#3{%
+\def\captype{Fig}%
+\capskip=0pt%
+\leftright{0.5}{0.5}{#1\kern -2pt\hskip 2pt plus 2pt minus 2pt}{\nextfigcaption{#2}\strut #3}}
+
+
+\def\subindex#1{%
+ \par\ifhmode\unskip\fi% end paragraph and remove vertical space
+ \vskip\abovesubsecskip% space above the subsection
+ \vskip 0pt plus 72pt% allow some empty space at the bottom
+ \penalty-200\vskip 0pt plus -72pt % room for stetching and a page break
+ \noindent{\bf\strut#1}%
+ \nobreak\vskip\belowsubsecskip% space after subsection
+ \everypar{{\setbox0=\lastbox}\everypar{}}% no indentation in the next paragraph
+ \ignorespaces
+}
+
+\newwrite\@figfile
+\newwrite\@tabfile
+
+\def\makefigindex{%
+ \immediate\openout\@figfile=\jobname.fig\relax
+ \def\figindex{\@bsphack\begingroup\@sanitize\@wrfigindex\@figfile}%
+ \immediate\openout\@tabfile=\jobname.tab\relax
+ \def\tabindex{\@bsphack\begingroup\@sanitize\@wrfigindex\@tabfile}%
+ \typeout{Writing index files \jobname.fig \jobname.tab}
+}
+
+\newdimen\iboxsize
+\def\thefigindex{%
+
+ \immediate\closeout\@figfile\relax
+ \immediate\closeout\@tabfile\relax
+ %\small\baselineskip=11pt
+ \plainsection{List of Figures and Tables}
+ \makeatletter
+ \iboxsize=3.7em\relax
+ \subindex{Figures}
+ \def\indexentry##1##2##3##4{% captype, fig/tab count, caption, page
+ \noindent
+ \line{\hbox to \iboxsize{%
+ \rm##1.~##2:\hfil}{\rm ##3}\ \leaders\hbox to .5em{.\hfil}%
+ \hfill\hbox to 1.4em{\hss
+ \ifbook##4\else
+ \ifpdf
+ \pagelnk{##4}%
+ \else\ifhint\figlink{##2}
+ \else##4\fi\fi\fi
+ }}\par
+ }%
+ \input\jobname.fig\relax
+ \iboxsize=3.9em\relax
+ \subindex{Tables}
+ \def\indexentry##1##2##3##4{% captype, fig/tab count, caption, page
+ \noindent
+ \line{\hbox to \iboxsize{%
+ \rm##1.~##2:\hfil}{\rm ##3}\ \leaders\hbox to .5em{.\hfil}%
+ \hfill\hbox to 1.4em{\hss
+ \ifbook##4\else
+ \ifpdf
+ \pagelnk{##4}%
+ \else\ifhint\tablink{##2}
+ \else##4\fi\fi\fi
+ }}\par
+ }%
+ \input\jobname.tab\relax
+ \makefigindex
+}
+
+
+\def\@wrfigindex#1#2#3#4{% file, captype, fig/tab count, caption
+ \xdef\gtempa{\write#1{\string\indexentry{#2}{#3}{#4}{\noexpand\thepageno}}}\endgroup\gtempa
+ \if@nobreak\ifvmode\@nobreak\fi\fi\@esphack}
+
+%%
+%% Format Definitions
+%%
+
+%\newbox\codebox
+\newwrite\@getfile
+\newwrite\@putfile
+\newwrite\@redfile
+\newwrite\@wrtfile
+
+\def\makecode{%
+ \immediate\openout\@getfile=\jobname.get\relax
+ \immediate\openout\@putfile=\jobname.put\relax
+ \immediate\openout\@redfile=\jobname.red\relax
+ \immediate\openout\@wrtfile=\jobname.wrt\relax
+ \typeout{Writing index files \jobname.get \jobname.put \jobname.red \jobname.wrt}
+}
+
+\def\thecodeindex{%
+ \immediate\closeout\@getfile\relax
+ \immediate\closeout\@putfile\relax
+ \immediate\closeout\@redfile\relax
+ \immediate\closeout\@wrtfile\relax
+ \def\indexentry##1##2##3##4{% codecount, depth, pagenumber, section
+ \ifnum##2=0 \smallskip\vskip 0pt plus 12pt\penalty -100\vskip 0pt plus -12pt\fi
+ \noindent
+ \line{%
+ \ifnum##2=0##4\hfill
+ \else\qquad\rm##4\ \leaders\hbox to .5em{.\hfil}\hfill
+ \hbox to 1.4em{\hss\pagelnk{##3}}
+ \fi
+ }\par
+ }%
+ \section{Format Definitions}
+ \makeatletter
+ \subsection{Reading the Long Format}\label{codeindex}
+ \input\jobname.red.srt\relax
+ \subsection{Writing the Long Format}
+ \input\jobname.wrt.srt\relax
+ \subsection{Reading the Short Format}
+ \input\jobname.get.srt\relax
+ \subsection{Writing the Short Format}
+ \input\jobname.put.srt\relax
+}
+
+
+\newcount\codecount
+\codecount=0
+
+\ifhint
+\def\@wrcodeindex#1#2#3#4{% file, depth, pagenumber, [sub]sectionname
+ \global\advance\indexcount by 1\indexdest
+ \xdef\gtempa{\write#1{\string\indexentry{#3}{#2}{\the\indexcount}{#4}}}\endgroup\gtempa\@esphack
+}
+\else
+\def\@wrcodeindex#1#2#3#4{% file, depth, pagenumber, [sub]sectionname
+ \xdef\gtempa{\write#1{\string\indexentry{#3}{#2}{\noexpand\thepageno}{#4}}}\endgroup\gtempa\@esphack
+}
+\fi
+
+\def\getindex{\@bsphack\begingroup\@sanitize\@wrcodeindex\@getfile}%
+\def\putindex{\@bsphack\begingroup\@sanitize\@wrcodeindex\@putfile}%
+\def\redindex{\@bsphack\begingroup\@sanitize\@wrcodeindex\@redfile}%
+\def\wrtindex{\@bsphack\begingroup\@sanitize\@wrcodeindex\@wrtfile}%
+
+
+\def\codelabel#1#2#3{
+ \getindex{#1}{#2}{#3}%
+ \putindex{#1}{#2}{#3}%
+ \redindex{#1}{#2}{#3}%
+ \wrtindex{#1}{#2}{#3}}
+\def\getsymbol{$\cdots\Longrightarrow$}
+\def\putsymbol{$\Longrightarrow\cdots$}
+\def\redsymbol{$-{}-{}-{}\Longrightarrow$}
+\def\wrtsymbol{$\Longrightarrow{}-{}-{}-$}
+\def\getcode{\codesection{\getsymbol}{Reading the short format}\getindex{1}{\the\codecount}{\subcodetitle}}
+\def\putcode{\codesection{\putsymbol}{Writing the short format}\putindex{1}{\the\codecount}{\subcodetitle}}
+\def\writecode{\codesection{\wrtsymbol}{Writing the long format}\wrtindex{1}{\the\codecount}{\subcodetitle}}
+\def\readcode{\codesection{\redsymbol}{Reading the long format}\redindex{1}{\the\codecount}{\subcodetitle}}
+\newdimen\coderule
+\def\codesection#1#2{%
+ \par\ifhmode\unskip\fi% end paragraph and remove vertical space
+ \penalty-200
+ \vskip 0pt plus 72pt% allow some empty space at the bottom
+ \penalty-200
+ \vskip 0pt plus -72pt% room for stetching and a page break
+\vskip\abovesubsecskip% space above the subsection
+%\line{\setbox0=\hbox{#1}%
+%\coderule=\hsize\advance\coderule by -\wd0\advance\coderule by -2em%
+%$\vcenter{\hrule width \coderule}$\hss\box0}
+\line{{\it #2\/}:\hfill#1}
+\nobreak\vskip\belowsubsecskip\nobreak% space after subsection
+%\everypar{{\setbox0=\lastbox}\everypar{}}% no indentation in the next paragraph
+%\noindent
+\ignorespaces}
+
+\def\hascode{\global\advance\codecount by 1%
+\codelabel{0}{\the\codecount}{\codetitle}}%
+
+
+%%
+%% Makeindex and the Index
+%%
+
+\newwrite\@indexfile
+
+% new jobname for index file
+\def\makeindex{
+ \immediate\openout\@indexfile=\jobname.inx%
+ \def\index{\@bsphack\begingroup\@sanitize\@wrindex\@indexfile}%
+ \typeout{Writing index file \jobname.inx }%
+}
+
+\def\@idxitem{\par\hangindent 40pt}
+
+\newif\ifnextindex
+\nextindexfalse
+
+\def\beginindex{%
+\ifhint\relax\else
+ \def\page{\box255} \normalbottom
+ \output{\ifpagesaved\normaloutput{\box\sbox}\lheader\rheader\fi
+ \global\setbox\sbox=\page \global\pagesavedtrue}
+ \pagesavedfalse \eject % eject the page-so-far and predecessors
+ \setbox\sbox\vbox{\unvbox\sbox} % take it out of its box
+ \vsize=\pageheight \advance\vsize by -\ht\sbox % the remaining height
+ \hsize=.5\pagewidth \advance\hsize by -10pt
+ % column width for the index (20pt between cols)
+ \parfillskip 0pt plus .6\hsize % try to avoid almost empty lines
+ \def\lr{L} % this tells whether the left or right column is next
+ \output{\if L\lr\global\setbox\lbox=\page \gdef\lr{R}
+ \else\nomarginoutput{\vbox to\pageheight{\box\sbox\vss
+ \hbox to\pagewidth{\box\lbox\hfil\page}}}\lheader\rheader
+ \global\nextindextrue
+ \global\vsize\pageheight\gdef\lr{L}\global\pagesavedfalse\fi}
+\fi
+\message{Index:}
+\mark{{0}{0}}
+\noindent\par
+\begingroup
+ \rightskip 0pt plus 12pt
+ \parskip 0pt plus .5pt
+ \hyphenpenalty 10000 \parindent0pt
+ \small\baselineskip=11pt
+\def\item{\par\hangindent 40pt\relax\ifnextindex\mark{{Index}{Index}}\fi}%
+\def\subitem{\par\hangindent 40pt\hskip 20pt\relax}%
+\def\subsubitem{\par\hangindent 40pt\hskip 30pt\relax}%
+\def\indexspace{\par\vskip 12pt plus 6pt minus 4pt\vskip 12pt\goodbreak\vskip -12pt\relax}%
+\def\indexheading##1{{\tenbf ##1}\nobreak\vskip 3pt\relax}
+}
+\def\endindex{%
+\endgroup
+ \vfill\eject % complete the current column.
+\ifhint\relax\else
+ \if R\lr\null\vfill\eject\fi % if necessarry add a right column
+\fi
+}
+
+
+%%
+%% Label page, section, toccount, figure, table, code, enum
+%%
+\newcount\cdcount
+\cdcount=0
+
+\newdimen\labelskip
+\def\label#1{% writes \labeldef{name}{{page}{section}{toccount}{figure}{table}{code}{enum}} to aux
+ \ifhint
+ \global\advance\indexcount by 1\indexdest
+ \begingroup\@readauxfile
+ \edef\next{\@writeaux{\string\labeldef{#1}{{\the\indexcount}%
+ {\thesection}{\the\toccount}{\the\figcount}{\the\tabcount}{\the\cdcount}{\the\enum}}}\endgroup}%
+ \else
+ \begingroup\@readauxfile
+ \edef\next{\@writeaux{\string\labeldef{#1}{{\noexpand\thepageno}%
+ {\thesection}{\the\toccount}{\the\figcount}{\the\tabcount}{\the\cdcount}{\the\enum}}}\endgroup}%
+ \fi
+ \ifvmode %make \removelastskip work after \label
+ \labelskip=\lastskip
+ \vskip-\labelskip
+ \next
+ \vskip\labelskip
+ \else
+ \next
+ \fi
+}
+
+
+\def\m@kelabel#1{label@#1}% this makes the controlseqence from the name
+
+\def\labeldef#1#2{% #1 is name #2 is page section subsection figure table code
+\expandafter\gdef\csname\m@kelabel{#1}\endcsname{#2}}%
+
+\def\@setref#1#2#3{%
+ \ifx#1\relax
+ \message{Undefined reference: #3}
+ \else
+ \expandafter#2#1%
+ \fi}
+
+\def\@iofvii#1#2#3#4#5#6#7{#1}
+\def\@iiofvii#1#2#3#4#5#6#7{#2}
+\def\@iiiofvii#1#2#3#4#5#6#7{#3}
+\def\@ivofvii#1#2#3#4#5#6#7{#4}
+\def\@vofvii#1#2#3#4#5#6#7{#5}
+\def\@viofvii#1#2#3#4#5#6#7{#6}
+\def\@viiofvii#1#2#3#4#5#6#7{#7}
+
+
+\def\pageref#1{\@readauxfile
+ \pagelnk{\expandafter\@setref\csname\m@kelabel{#1}\endcsname\@iofvii{#1}}}
+\def\secref#1{\@readauxfile
+ \sectionlink
+ {\expandafter\@setref\csname\m@kelabel{#1}\endcsname\@iiofvii{#1}}%
+ {\expandafter\@setref\csname\m@kelabel{#1}\endcsname\@iiiofvii{#1}}%
+}
+\def\figref#1{\@readauxfile
+\figlink{\expandafter\@setref\csname\m@kelabel{#1}\endcsname\@ivofvii{#1}}}
+\def\tabref#1{\@readauxfile
+\tablink{\expandafter\@setref\csname\m@kelabel{#1}\endcsname\@vofvii{#1}}}
+\def\cdref#1{\@readauxfile
+\expandafter\@setref\csname\m@kelabel{#1}\endcsname\@viofvii{#1}}
+\def\enumref#1{\@readauxfile
+\enumlink{\expandafter\@setref\csname\m@kelabel{#1}\endcsname\@viiofvii{#1}}}
+
+%%
+%% Links and Destinations
+%%
+
+\ifbook
+ \def\pdflinkcolor{0 0 0} % the RGB values for hyperlink color
+ \def\linkcolor{\Black}
+ \def\pdflink#1#2{#1}
+ \def\pdfnote#1.{#1}
+ \message{No PDF Links}
+\else
+ \def\pdflinkcolor{0 0 1} % the RGB values for hyperlink color
+ \def\linkcolor{\Blue}
+ \message{PDF Links are blue}
+\fi
+
+\def\@esphack{\relax\ifhmode\spacefactor\@savsf
+ {}\ifdim \@savsk >\z@ \ignorespaces
+ \fi \fi}
+
+%
+% Code destinations: by number
+%
+
+%The number is set be \M and \N and stored as \secno
+%The destination is set in \X when we go into C mode
+\def\codedest{%
+\ifbook\else
+\ifhint\HINTdest num \thecode top\relax
+\else\ifpdf\pdfdest num \thecode fith\relax
+\else\relax
+\fi\fi\fi
+}
+\def\coderef#1{% used in \X
+\ifbook${}_{#1}$\else
+\ifhint\HINTstartlink goto num #1\lower 1.5pt\hbox{\sevenrm #1}\,\HINTendlink
+\else\ifpdf{\eightrm\pdfnote#1.}
+\else${}_{#1}$\fi\fi\fi
+}
+
+\def\codenote#1.{% replaces \pdfnote
+ \setbox0=\hbox{\toksA={#1.}\toksB={}\maketoks}\the\toksA}
+% define the following like for pdf
+\def\firstsecno#1.{\setbox0=\hbox{\toksA={#1.}\toksB={}%
+ \def\makenote{\addtokens\toksB{\the\toksC}\def\makenote{\toksD={}
+ \toksC={}\let\space\empty}\makenote}\maketoks}}
+\def\addtokens#1#2{\edef\addtoks{\noexpand#1={\the#1#2}}\addtoks}
+\def\poptoks#1#2|ENDTOKS|{\let\first=#1\toksD={#1}%
+ \ifcat\noexpand\first0\countB=`#1\else\countB=0\fi\toksA={#2}}
+\def\maketoks{\expandafter\poptoks\the\toksA|ENDTOKS|%
+ \ifnum\countB>`9 \countB=0 \fi
+ \ifnum\countB<`0
+ \ifnum0=\countC\else\makenote\fi
+ \ifx\first.\let\next=\maketoksdone\else
+ \let\next=\maketoks
+ \addtokens\toksB{\the\toksD}
+ \ifx\first,\addtokens\toksB{\space}\fi
+ \fi
+ \else \addtokens\toksC{\the\toksD}\global\countC=1\let\next=\maketoks
+ \fi
+ \next
+}
+\def\makenote{\addtokens\toksB
+ {\noexpand\codelink{\the\toksC}{\romannumeral\the\toksC}}\toksC={}\global\countC=0}
+\def\maketoksdone{\edef\st{\global\noexpand\toksA={\the\toksB}}\st}
+
+\def\codelink#1#2{
+\ifbook#1\else
+\ifhint\HINTstartlink goto num #1 #1\HINTendlink
+\else\ifpdf\pdflink{#1}{#2}%
+\else#1%
+\fi\fi\fi}
+
+%
+% new destinations and links: by name
+%
+
+\def\newdest#1{% used to make a new destination
+%\message{New destination #1}%
+\ifbook
+\else\ifpdf
+ \pdfdest name {#1} fith%\message{Defining pdf label #1}%
+\else\ifhint
+ \HINTdest name {#1}%\message{Defining HINT label #1}%
+\fi\fi\fi}
+
+\def\newlink#1#2{%
+ \ifbook #2\else
+ \ifpdf
+ \pdfstartlink goto name {#1}\linkcolor#2\Black\pdfendlink
+ \else\ifhint
+ \HINTstartlink goto name {#1}#2\HINTendlink
+ \else
+ #2%
+ \fi\fi\fi
+}
+
+
+%SC.-\the\plaincount for plain sections
+%SC.\thesection for regular sections, with subsections and letters for the appendix
+
+\def\tocref#1#2#3#4{% depth, title, page, label used in tocline
+ \ifbook#3\else
+ \ifpdf
+ \pdfstartlink goto name {SC.#4}\relax \linkcolor#3\Black\pdfendlink
+ \ifnum#1=0\pdfoutline goto name {SC.#4} {#2}\fi
+ \else\ifhint
+ \HINTstartlink goto name {SC.#4}\relax $\rightarrow$\HINTendlink
+ \HINToutline goto name {SC.#4} depth #1 {#2}%
+ \else
+ #3%
+ \fi\fi\fi
+}%
+
+%
+% Page references from the index
+%
+
+% \index{keyword} -> \@wrindex{file}{keyword} writes the nectessary information
+% makeindex produces the index and pages are enclosed in \hyperref{pagenumber}
+% Problems:
+% pagenumbers may be roman numerals in the frontmatter
+% hyperpage has to cope with 6 different formats
+% \hyperpage{50} a single page number
+% \hyperpage{51, 52} two numbers
+% \hyperpage{53--59} a page range
+% \hyperpage{xi} a roman numeral
+% \hyperpage{xi, xii} two numbers
+% \hyperpage{xi--xx} a page range
+
+
+% Writing the index file: in HINT replacing the page number by the index count
+\newcount\indexcount
+\def\theindexcount{\the\indexcount}
+
+\ifhint
+ \def\@wrindex#1#2{% file, keyword
+ \global\advance\indexcount by 1\indexdest
+ \immediate\write#1{\string
+ \indexentry{#2:hyperpage}{\the\indexcount}}\endgroup
+ \if@nobreak \ifvmode\@nobreak\fi\fi\@esphack}
+ \else
+ \def\@wrindex#1#2{%
+ \xdef\gtempa{\write#1{\string
+ \indexentry{#2:hyperpage}{\noexpand\thepageno}}}\endgroup\gtempa
+ \if@nobreak \ifvmode\@nobreak\fi\fi\@esphack}
+\fi
+
+
+% for HINT and PDF we used hyperrange and hypercomma to split the
+% arguments into index/page numbers and then use pagelink
+\ifbook
+ \def\hyperpage#1{#1}\else
+\ifhint
+ \def\hyperpage#1{\hyperrange#1----+\relax}
+\else\ifpdf
+ \def\hyperpage#1{\hyperrange#1----+\relax}
+\else
+ \def\hyperpage#1{#1}
+\fi\fi\fi
+
+%\def\indexlink#1{#1}
+
+\def\hyperrange#1--#2--#3+{%
+ \ifx\empty#2\empty%
+ \hypercomma#1, ,+%
+ \else\ifhint
+ \pagelnk{#1}--\pagelink{#2}{$\leftarrow$}%
+ \else
+ \pagelnk{#1}--\pagelnk{#2}%
+ \fi\fi}
+
+\def\hypercomma#1, #2,#3+{%
+ \ifx\empty#2\empty%
+ \pagelnk{#1}%
+ \else\ifhint%
+ \pagelink{#1}{$\longrightarrow$}%
+ \else
+ \pagelnk{#1}%
+% I omit the second page number \ifpdf, \pagelnk{#2}\fi
+ \fi\fi
+}
+
+
+%
+% Labels
+%
+
+% section references from \secref use \sectionlink
+% for plainsections, we display the label name because there
+%is no section number.
+
+\def\sectionlink#1#2{% \message{Sectionlink #1 : #2}%
+ \ifbook#1\else
+ \ifpdf
+ \pdfstartlink goto name {SC.#2} \linkcolor#1\Black\pdfendlink
+ \else\ifhint
+ \HINTstartlink goto name {SC.#2}#1\HINTendlink
+ \else
+ #1%
+ \fi\fi\fi
+}
+
+
+% page references from \label and \pageref use \pagelink as before
+\def\indexdest{\newdest{page.\the\indexcount}}
+\def\pagelnk#1{%
+ \ifhint
+ \pagelink{#1}{$\rightarrow$}%
+ \else
+ \pagelink{#1}{#1}%
+ \fi
+}
+\def\pagelink#1#2{\newlink{page.#1}{#2}}
+% For hint the destination is generated with each \index and \label
+% Because of the roman numerals, for pdf files
+% the pdf destination is generated in the header using \pagelabel
+
+
+\ifbook\let\pagelabel=\relax\else
+\ifpdf
+\def\pagelabel{%\message{pagelabel=\thepageno}
+\pdfdest name {page.\thepageno} fitv\relax}
+\else
+\let\pagelabel=\relax
+\fi\fi
+
+% references to Figures, Tables, Enumerations
+\def\figdest{\newdest{FI.\the\figcount}}
+\def\figlink#1{\newlink{FI.#1}{#1}}
+\def\tabdest{\newdest{TA.\the\tabcount}}
+\def\tablink#1{\newlink{TA.#1}{#1}}
+\def\enumdest{\newdest{EN.\the\enum}}
+\def\enumlink#1{\newlink{EN.#1}{#1}}
+
+%
+% References to the bibliography
+%
+\def\citedest#1{\newdest{CI.#1}}
+\def\citelink#1{%\message{Citelink #1}%
+ \newlink{CI.#1}{#1}}
+
+\def\bblhook{%defined before reading the .bbl file
+ \def\biblabelprint##1{%
+ \citedest{##1}%
+ \noindent
+ \hbox to \biblabelwidth{%
+ \biblabelprecontents
+ \biblabelcontents{##1}%
+ \biblabelpostcontents
+ }%
+ \kern\biblabelextraspace
+ }%
+}
+\def\@onecitation#1\@@{%called for each citation
+ \if@notfirstcitation
+ \printbetweencitations
+ \fi
+ %
+ \expandafter \ifx \csname\@citelabel{#1}\endcsname \relax
+ \if@citewarning
+ \message{\@linenumber Undefined citation `#1'.}%
+ \fi
+ % Give it a dummy definition:
+ \expandafter\gdef\csname\@citelabel{#1}\endcsname{%
+ {\tt
+ \escapechar = -1
+ \nobreak\hskip0pt
+ \expandafter\string\csname#1\endcsname
+ \nobreak\hskip0pt
+ }%
+ }%
+ \fi
+ % Now produce the text, whether it was undefined or not.
+ \citelink{\csname\@citelabel{#1}\endcsname}%
+ \@notfirstcitationtrue
+}%
+%%
+%% Images
+%%
+
+\ifbook
+\def\includefig#1{\leavevmode\hbox{\immediate\pdfximage{image/#1.pdf}\pdfrefximage\pdflastximage}}
+\else\ifpdf
+\def\includefig#1{\leavevmode\hbox{\immediate\pdfximage{image/#1.pdf}\pdfrefximage\pdflastximage}}
+\else\ifhint
+ \def\includefig#1{\HINTimage=image/#1.png\relax}
+\else
+ \input epsf.tex
+ \def\includefig#1{\epsfbox{image/#1.eps}}% this is a \leavevmode\hbox{...}
+\fi\fi\fi
+
+\ifhint\eject\fi% hitex will remove empty pages that were introduced by cwebmac
+\makeatother
diff --git a/Build/source/texk/web2c/texprofdir/texprofileman.tex b/Build/source/texk/web2c/texprofdir/texprofileman.tex
new file mode 100644
index 00000000000..4a6f5c2e2b0
--- /dev/null
+++ b/Build/source/texk/web2c/texprofdir/texprofileman.tex
@@ -0,0 +1,442 @@
+% Copyright 2017-2024 Martin Ruckert, Hochschule Muenchen, Lothstrasse 64, 80336 Muenchen
+%
+% 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
+% 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 holders 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 holders.
+\documentclass[a4paper,english]{article}
+\usepackage{a4wide}
+\usepackage{latex2man}
+
+\setVersion{1.0}
+\setDate{6-9-2024}
+
+\begin{document}
+\begin{Name}{1}{texprofile}{Martin Ruckert}{Displaying TeX profiles}{TeXprofile: Displaying TeX profiles}
+
+ \Prog{texprofile} - Displaying the profile data collected by \Prog{texprof}.
+\end{Name}
+
+\section{Synopsis}
+
+\Prog{texprofile} [Options] \Arg{inputfile}
+
+\section{Description}
+\Prog{texprofile} reads the binary \Arg{inputfile} as produced by
+\Prog{texprof}. The extension \File{.tprof} of the input file can be
+omitted. It analyses the data and presents the results in a variety of
+tables, either optimized for human readability or as CSV (comma
+separated values) files for further processing.
+
+The output is written to the standard output stream where it can be redirected
+to a file or viewed immediately.
+
+\section{Options}
+There are four types of options: general options, table options,
+selection options, and formatting options.
+
+The general options:
+\begin{description}
+\item[\Opt{-?} \Opt{-h} \Opt{--help}]
+ Display a short help text and exit the program.
+\item[\Opt{--version}]
+ Display the version information and exit.
+\end{description}
+
+The table options determine the tables that will be displayed.
+These options all use upper case letters.
+If no table option is given, only some global information is shown.
+
+\begin{description}
+\item[\Opt{-T}]
+ Show the table of the top 10 input lines.
+\item[\Opt{-G}]
+ Show the table of the macro call graph.
+\item[\Opt{-C}] Show the table of times per TeX command.
+\item[\Opt{-L}] Show the table of times per input line.
+\item[\Opt{-R}] Show the table of raw time stamps.
+\item[\Opt{-S}] Show the table of macro stack changes.
+\item[\Opt{-F}] Show the table of all input files.
+\item[\Opt{-M}] Show the table of all macros called.
+\item[\Opt{-A}] Show important tables (equal to -TGFC) tables.
+\item[\Opt{-N}] Do not show the table of global information.
+\end{description}
+
+The selection options allow to reduce the amount of data that is shown
+in the table by omitting table entries that contribute little to the
+overall runtime.
+
+\begin{description}
+\item[\OptArg{-p}{n}] Do not show information for table entries
+ with cumulative time below \Arg{n} percent. The default is 1.0 percent.
+\item[\OptArg{-t}{n}] Limit the number of input lines shown in the
+ table of the ``Top Ten'' to \Arg{n}. This option is only useful
+ with the \Opt{-T} option.
+\end{description}
+
+The formatting option determine the formatting of the information in the tables
+as well as the selection of information that is shown in the tables.
+
+\begin{description}
+\item[\Opt{-i}]
+ Add the macros file and line numbers after the macro name.
+ This option is useful to distinguish two macros that share
+ the same name.
+\item[\Opt{-m}]
+ Optimize output for machine readability. When optimized for human readability,
+ times are rounded and given in a short form adding a unit identifier:
+ s for seconds, ms for milliseconds, us for micro seconds, and ns for
+ nanoseconds. This format is inconvenient if you want to import the data
+ for example into a spread-sheet for further processing.
+ With the \Opt{-m} option times are simply given in nano seconds without units.
+ Similar the combined time for the total time used for a macro and the
+ time used as a sub-macro from the call graph is given in a single column
+ with a slash separating both numbers is nice for human readers.
+ With the \Opt{-m} option both numbers will simply be given in separate columns.
+
+\item[\Opt{-n}]
+ Show the time stamp numbers. This option is useful together with
+ the \Opt{-R} option, if you import the table into a database or spread-sheet.
+ If you sort the data by various properties into different
+ categories, the time stamp number can be used to keeping the entries
+ within a category sorted by time.
+
+\item[\Opt{-s}]
+ Show the changes of the macro stack.
+ This option is useful with the \Opt{-R} option if you want to see the changes
+ in the macro stack (see \Opt{-S}) together with the timing information in
+ a single table.
+\end{description}
+
+
+\section{Data Formats}
+\subsection{File Numbers}
+While \Prog{texprof} is running, every TeX input file is given a
+unique file number. The file name alone is often not unique because
+two files in different directories might have the same file
+name. Displaying the full file name with the complete path is however often
+not very
+convenient. So if you observe the same file name together with
+different file numbers, you can use the option \Opt{-F} to get a table of all
+file numbers and their full file names.
+
+But even within the same
+directory, TeX can read different files with the same name during one
+run: TeX can open a file for writing, write content to the file, close
+it, open it for reading, read it and close it again. Then TeX might
+repeat this process a second time, or multiple times, reusing the same
+file name over and over again. \Prog{texprof} will assign a new file
+number to this file each time it is opened for reading. So you can
+tell from the sequence of file numbers which one was the first, the
+second, or the third.
+
+Last not least, there are some special file
+numbers:
+\begin{description}
+\item[0 - unknown]
+ If the file is unknown, which should rarely happen,
+ the file number 0 is used.
+\item[1- system]
+ \Prog{texprof} will map
+ time intervals that are used to execute certain system routines
+ to the ``system'' pseudo file
+using the line numbers to identify the specific routine
+like producing the output DVI file (shipout), breaking
+a paragraph into lines (linebrk), or breaking the
+document into pages (buildpg). These times do not depend on the
+use of macros but simply on the size of the document.
+\item[2 - terminal]
+ TeX commands entered on the command line or interactively are mapped
+ to file number 2.
+\end{description}
+
+\subsection{Time}
+If the option \Opt{-m} is given times are given as nanoseconds.
+Otherwise, times are rounded to at most 4 digits precission and
+displayed with a unit: seconds (s), milliseconds (ms), microseconds
+(um), or nanoseconds (ns).
+
+\subsection{Macro Names}
+Macro names are shown with the leading backslash. Since macro names
+are often not unique, the command line option \Opt{-i} can be used to
+show after the macro name in square brackets the file number and the
+line number where the macro is defined. Unless two macros with the
+same name are defined in the same file and line this is sufficient to
+uniquely identify a macro. A macro defined with ``let'' results in a
+complet copy of the original macro. Therefore it will not reference
+the file and line of the ``let'' command but the same file and line as
+the original macro.
+
+\section{Examples}
+Let's assume that you issue the command \Prog{texprof} \Opt{-prof}
+\Arg{hello.tex}. This will run \Prog{texprof} on the input file
+\Arg{hello.tex} with option \Opt{-prof}. The program \Prog{texprof}
+will load the plain TeX format and then process \File{hello.tex} to
+produce \File{hello.log} and \File{hello.dvi}. It will execute
+exactly the same steps that TeX would execute if you had issued the
+command \Prog{tex} \Arg{hello.tex}.
+
+In addition to \File{hello.log} and \File{hello.dvi}, \Prog{texprof}
+will also produce the file \File{hello.tprof} containing time
+measurements made while \Prog{texprof} was running. The option
+\Opt{-prof} will switch on the gathering of timing data as soon as
+\Prog{texprof} enters its main control procedure; The file
+\File{hello.tprof} will contain a time measurement, called a time
+stamp, for every command that TeX executed while processing the input.
+A binary format is used to store all that data in a simple and compact
+form. Still the file \File{hello.tprof} might become very big.
+
+The program \Prog{texprofile} is used to extract and analyse the data
+contained in \File{hello.tprof} and produce useful output. Here are
+some examples:
+
+\begin{description}
+\item[\Prog{texprofile} \Arg{hello}]
+ Without further options \Prog{texprofile} will write some general
+ information to the standard output, like the total time measured,
+ the number of samples, the average time per sample, etc.
+ The general information is always given unless explicitely disabled
+ with the \Opt{-N} option or with the \Opt{-m} option.
+
+\item[\Prog{texprofile} \Opt{-T} \Arg{hello}]
+ With the option \Opt{-T}, \Prog{texprofile} will map each time stamp
+ to a specific line of input, add up the time intervalls for each input
+ line separately, and output a table showing the ten lines that have
+ the highest cummulative time.
+ The table has the following seven columns:
+ \begin{description}
+ \item[1. file]
+ The first column contains the input file number as explained above.
+ The input file name if shown in column 7.
+ \item[2. line]
+ The line number.
+ \item[3. percent]
+ The time spent in this line and file as a percentage of the total
+ time measured as given as part of the general information.
+ \item[4. absolute]
+ The absolute time spent in this line and file.
+ \item[5. count]
+ The number of times the execution path entered this line.
+ Note, that a
+ macro call usually redirects the execution path to another line
+ from where the execution will return after the macro call has completed.
+ Reentering the line after such an excursion to an other line will
+ cause this counter to be incremented. In summary, this counter might
+ reflect the number of partial executions of a line not the number
+ of full executions of the entire line.
+ \item[6. average]
+ The average time spent in the line is simply computed by dividing the
+ value in column 4 by the value in column 5.
+ \item[7. file]
+ The input file name. The corresponding file number is shown in column 1.
+ \end{description}
+
+\item[\Prog{texprofile} \Opt{-G} \Arg{hello}]
+ With the option \Opt{-G}, \Prog{texprofile} will map each time stamp
+ either to file input or to a macro body.
+ The table shown is divided into several sections, the first section
+ is devoted to file input, each of the following sections is devoted
+ to a specific macro.
+ \\
+ Each section starts with a header. The header of the
+ first section is ``File'' the header of a later section is the macro name.
+ \\
+ The first line after the header gives the total time spent in the section
+ in two different formats:
+ \begin{description}
+ \item[1. time]
+ Column 1 gives it as an absolut time.
+ \item[3. percentage]
+ Column 3 gives it as a percentage of the total time measured.
+ \end{description}
+ For the first section with the header ``File'', the absolute time
+ will be equal to the total time measured because \Prog{texprof}
+ did spent all the time processing the file \Arg{hello}.
+ And consequently the value in column 3 will be 100\%.
+ \\
+ To accomplish a task, a macro usually calls other
+ macros that we call child macros in the following.
+ The following lines in the table will give a breakdown
+ of the time shown in the first line.
+ The breakdown starts with a line showing the time spent in the section
+ excluding the time spent in child macros. It shows in column 4 the number
+ of times the macro was called.
+ \begin{description}
+ \item[1. time]
+ Column 1 gives the absolut time spent in the section excluding the time
+ spent in child macros
+ \item[3. percentage]
+ Column 3 gives the time from column 1 as a percentage of the total time
+ spent in this section as given in the previous line.
+ \item[4. count/total]
+ Column 4 gives the number of times the section was called.
+ \end{description}
+ The lines that follow in the table show the time spent in one of
+ the child macros. Column 2, 4, and 5 need some explanation.
+ \begin{description}
+ \item[2. loop]
+ The only case where column 2 is not empty is the case of a
+ recursive macro. A recursive macro is a macro that along the
+ chain of macro calls eventually calls itself creating a
+ recursive loop. At this point, a macro becomesits own descendant
+ and at the same time its own ancestor.
+ \\
+ Therefore \Prog{texprofile} maintains for each child macro two
+ accumulators for the elapsed time: For the time shown in
+ column 2 labeled ``loop'', \Prog{texprofile} adds up the time
+ differences observed at the return of a child macro. For the
+ time shown in the column labeled ``time'' and ``percent'', it
+ subtracts from the time differences observed at the return of a
+ child macro all those time differences that were already added
+ to one of the other lines in the time breakdown: the macro itself
+ or one of the other child macros.
+ So the times shown in column 1 of line 2 and the following lines
+ will add up to the time shown in column 1 of the line 1;
+ and the percentages shown in column 3 of line 2 and the following lines
+ will add up to 100\%.
+ The time shown in column 3 will show show the total time needed
+ to accomplish the sub task assigned to the respective child macro.
+ \item[4. count/total]
+ Column 4 shows two counts $n/m$ for the macro named in column 5.
+ $m$ is the total number of calls to the macro and $n$ is the number of
+ calls as a child macro in the current section. The number $n$ will
+ always be less or equal to $m$.
+
+ \item[5. child]
+ Column 5 shows the name of the child macro as explained above.
+ \end{description}
+\item[\Prog{texprofile} \Opt{-G} \Opt{-m} \Arg{hello}]
+ This table will contain the same data as the previous example
+ but this time the \Opt{-m} option will optimize the output for
+ machine readability.
+ \begin{itemize}
+ \item There are no column headers.
+ \item The times in column 1 ``time'' and column 2 ``loop''
+ are given in nanoseconds without a unit identifier.
+ \item The numbers $n/m$ in column 4 are now shown in two separate
+ columns 4 and 5; the macro name moves from column 5 to column 6.
+ \end{itemize}
+
+\item[\Prog{texprofile} \Opt{-F} \Arg{hello}]
+ With the option \Opt{-F}, \Prog{texprofile} outputs the table
+ of all of TeX's input files using 5 columns:
+ \begin{description}
+ \item[1. file] The file number as explained above.
+ \item[2. lines] The difference between the highest and the lowest
+ line number found in the profile data for this file. This is
+ usually only a subset of all the lines of the file.
+ \item[3. percent]
+ The percentage of the total time measured that is attibuted to the file.
+ \item[4. time]
+ The absolute time that is attibuted to the file.
+ \item[5. name]
+ The full file name of the file.
+ \end{description}
+\item[\Prog{texprofile} \Opt{-C} \Arg{hello}]
+ With the option \Opt{-C}, \Prog{texprofile} outputs the table
+ of all TeX commands executed while profiling
+ using 6 columns:
+ \begin{description}
+ \item[1. cmd] The command code used internaly by TeX.
+ Usually the same number is used by TeX for several
+ closely related tasks.
+ There is a special command code 101 which is used to
+ for the time that is spend on system routines that are
+ mapped to the system file as explained above.
+ \item[2. time]
+ The total time used for the command.
+ \item[3. percent]
+ The percentage of the total time measured used for the command.
+ \item[4. count]
+ The number of times this command was executed.
+ \item[5. average]
+ The average time needed to execute the command.
+ This is simpy the value in column 2 divided by the value in column 4.
+ \item[6. name]
+ A verbal description of the command or commands that share this
+ command code.
+ \end{description}
+\item[\Prog{texprofile} \Opt{-R} \Opt{-m} \Opt{-n} \Arg{hello}]
+ With the options \Opt{-R} \Opt{-m}, \Prog{texprofile} outputs the table
+ of raw time measurements as observed by \Prog{texprof} optimized for machine
+ readability. The option \Opt{-n} adds a column for the number
+ of each time measurements.
+ Because of the \Opt{-m} option, the table has no column headers.
+ The table has 7 columns:
+ \begin{description}
+ \item[1. number] The number of the time measurment.
+ These numbers are strictly increasing but not necessarily consecutive.
+ They can be used to keep the measurements sorted in the order in which
+ they were made.
+ \item[2. file] The file number as explained above.
+ \item[3. line] The line number as explained above.
+ \item[4. time] The time interval in nano seconds.
+ \item[5. command] The command name as given in column 6 of the previous example.
+ \item[6. level ] The nesting level of the macro call stack.
+ \item[7. macro] The macro name.
+ \end{description}
+ This table contains all the timing information gathered during the run
+ of \Prog{texprof}. Some information about macro calls is contained in
+ column 6. More information about macro calls could be added
+ by using the \Opt{-s} option. The information about macro returns
+ can be obtained from column 6.
+ The table can be imported to a spread-sheet or a database for further
+ analysis.
+\end{description}
+
+
+\section{Bugs}
+If the last command in a macro body is a macro call, we call this a tail call.
+If such a tail call reads ahead to scan the following input
+for possible arguments, the look-ahead mechaism of TeX might push further
+macros or new input files on TeX's input stack.
+These entries will remain on top of TeXprof's macro nesting stack,
+even if TeX backs up all these tokens on its input stack.
+This can cause an attribution of runtime to those entries as sub entries
+of the tail call. If this explanation sounds complicated to you, it is
+because the situation is indeed complicated.
+
+\section{See Also}
+ \Cmd{texprof}{1}
+
+
+\section{Version}
+Version: \Version\ of \Date
+\begin{description}
+\item[Copyright] \copyright\ 2024, Martin Ruckert,\\
+ Hochschule München, Lothstrasse 64, D-80335 München
+
+\item[Distribution]
+ \Prog{texprofile} is distributed with TeX Live.
+ More recent versions might be found on GitHub
+ \URL{https://github.com/ruckertm/HINT}.
+
+\item[License] This program can be redistributed and/or modified under the
+ terms of the MIT/X11 license.
+\end{description}
+
+\section{Author}
+\noindent
+Martin Ruckert \\
+Munich University of Applied Sciences \\
+Email: \Email{martin.ruckert@hm.edu} \\
+
+\end{document}
diff --git a/Build/source/texk/web2c/tftopl.ch b/Build/source/texk/web2c/tftopl.ch
index 445ba8e3ab1..c26137b6958 100644
--- a/Build/source/texk/web2c/tftopl.ch
+++ b/Build/source/texk/web2c/tftopl.ch
@@ -15,6 +15,12 @@
% 01/16/90 (SR) Version 3.1.
% (more recent changes in the ChangeLog)
+@x [0] l.25
+\def\(#1){} % this is used to make section names sort themselves better
+@y
+\def\({} % this is used to make section names sort themselves better
+@z
+
@x [0] l.28
\def\title{TF\lowercase{to}PL}
@y
@@ -64,7 +70,7 @@ label @<Labels in the outer block@>@/
procedure initialize; {this procedure gets things started properly}
begin print_ln(banner);@/
@y
-@<Define |parse_arguments|@>
+@<Define \(|parse_arguments|@>
procedure initialize; {this procedure gets things started properly}
begin
kpse_set_program_name (argv[0], my_name);
@@ -186,6 +192,18 @@ strcat (ASCII_all, '`abcdefghijklmnopqrstuvwxyz{|}~');@/
MBL_string:=' MBL'; RI_string:=' RI '; RCE_string:=' RCE';
@z
+@x [36] l.680
+while l>0 do @<Reduce \(1)|l| by one, preserving the invariants@>;
+@y
+while l>0 do @<Reduce \(|l| by one, preserving the invariants@>;
+@z
+
+@x [37] l.687
+@ @<Reduce \(1)|l|...@>=
+@y
+@ @<Reduce \(|l|...@>=
+@z
+
% [38] How we output the character code depends on |charcode_format|.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@x
@@ -228,6 +246,18 @@ else begin tfm[0]:=c; out_octal(0,1);
put_byte(RCE_string[1+(b div 3)], pl_file);
@z
+@x [40] l.751
+if a>@'3777 then @<Reduce \(2)negative to positive@>;
+@y
+if a>@'3777 then @<Reduce negative to positive@>;
+@z
+
+@x [43] l.775
+@ @<Reduce \(2)negative to positive@>=
+@y
+@ @<Reduce negative to positive@>=
+@z
+
% [78] No progress reports unless verbose.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
@x
@@ -338,7 +368,7 @@ Parse a Unix-style command line.
@d argument_is (#) == (strcmp (long_options[option_index].name, #) = 0)
-@<Define |parse_arguments|@> =
+@<Define \(|parse_arguments|@> =
procedure parse_arguments;
const n_options = 4; {Pascal won't count array lengths for us.}
var @!long_options: array[0..n_options] of getopt_struct;
diff --git a/Build/source/texk/web2c/tiedir/ChangeLog b/Build/source/texk/web2c/tiedir/ChangeLog
index c584f11eef0..b3a98e85a86 100644
--- a/Build/source/texk/web2c/tiedir/ChangeLog
+++ b/Build/source/texk/web2c/tiedir/ChangeLog
@@ -1,3 +1,7 @@
+2023-09-01 Andreas Scherer <https://ascherer.github.io>
+
+ * tie-w2c.ch: Fix sorting order of names of sections.
+
2023-09-03 TANAKA Takuji <ttk@t-lab.opal.ne.jp>
* tie.test: Make easier to test on Windows.
diff --git a/Build/source/texk/web2c/tiedir/tie-w2c.ch b/Build/source/texk/web2c/tiedir/tie-w2c.ch
index fb09e05060f..d29e3f77967 100644
--- a/Build/source/texk/web2c/tiedir/tie-w2c.ch
+++ b/Build/source/texk/web2c/tiedir/tie-w2c.ch
@@ -252,6 +252,12 @@ replacement part of a change file, or in an incomplerte check if the
replacement part of a change file, or in an incomplete check if the
@z
+@x l.663
+ @<Check |c| for |EOF|, |return| if line was empty, otherwise
+@y
+ @<Check \9{c}|c| for |EOF|, |return| if line was empty, otherwise
+@z
+
@x l.667
if (c!=@' ' && c!=tab_mark)
@y
@@ -272,6 +278,12 @@ final line. Of the current line is empty, we just can \&{return}.
final line. If the current line is empty, we just can \&{return}.
@z
+@x l.696
+@<Check |c| for |EOF|...@>=
+@y
+@<Check \9{c}|c| for |EOF|...@>=
+@z
+
Section 31
@x l.742 -- print errors on 'stderr'
diff --git a/Build/source/texk/web2c/uptexdir/ChangeLog b/Build/source/texk/web2c/uptexdir/ChangeLog
index a68dbcce2f4..30a38016535 100644
--- a/Build/source/texk/web2c/uptexdir/ChangeLog
+++ b/Build/source/texk/web2c/uptexdir/ChangeLog
@@ -1,3 +1,33 @@
+2024-09-30 TANAKA Takuji <ttk@t-lab.opal.ne.jp>
+
+ * uptex-m.ch: kcatcode (latin_ucs(14)) is removed
+ since it will be supported in upTeX-2.00.
+
+2024-09-28 TANAKA Takuji <ttk@t-lab.opal.ne.jp>
+
+ * uptex-m.ch, upbibtex.ch, kanji.c:
+ Update reference of Unicode blocks to
+ "Blocks-16.0.0.txt Date: 2024-02-22".
+ Also add a new block
+ "CJK Unified Ideographs Extension J"
+ which is approved in Unicode 17.0, 2025.
+ * tests/unibib.{tex,bib,bbl}: Update a test.
+
+2024-09-22 TANAKA Takuji <ttk@t-lab.opal.ne.jp>
+
+ * ptex-base.ch, ptex_version.h: Version p4.1.2.
+
+2024-09-21 TANAKA Takuji <ttk@t-lab.opal.ne.jp>
+
+ * ptex-base.ch, uptex-m.ch:
+ Fix a bug in initialization of inhibit_xsp/kinsoku tables
+ (Bug introduced on commit 2022-12-12 r65248).
+
+2024-09-16 TANAKA Takuji <ttk@t-lab.opal.ne.jp>
+
+ * ptex-base.ch: Use constants enc_jis and enc_ucs.
+ * am/uptex.am: Tweak.
+
2024-05-18 Hironori Kitagawa <h_kitagawa2001@yahoo.co.jp>
* ptex-base.ch: Enable to get character codes
diff --git a/Build/source/texk/web2c/uptexdir/am/uptex.am b/Build/source/texk/web2c/uptexdir/am/uptex.am
index 1a4ee00d4c0..bf71b77f5c7 100644
--- a/Build/source/texk/web2c/uptexdir/am/uptex.am
+++ b/Build/source/texk/web2c/uptexdir/am/uptex.am
@@ -263,12 +263,12 @@ DISTCLEANFILES += uptests/ygk256*.*
DISTCLEANFILES += uptests/xskipjfmp.*
## uptexdir/uptriptest
EXTRA_DIST += uptexdir/uptrip/uptrip.diffs uptexdir/uptrip/texmf.cnf
+DISTCLEANFILES += uptrip.diffs
+
uptrip.diffs: uptex$(EXEEXT) dvitype$(EXEEXT) pltotf$(EXEEXT) tftopl$(EXEEXT)
$(triptrap_diffs) $@
.PHONY: uptrip-clean
uptrip-clean:
rm -rf uptripdir
-DISTCLEANFILES += uptrip.diffs
-
# (end of uptex.am)
diff --git a/Build/source/texk/web2c/uptexdir/kanji.c b/Build/source/texk/web2c/uptexdir/kanji.c
index ddeca5a535c..1dca2a340e4 100644
--- a/Build/source/texk/web2c/uptexdir/kanji.c
+++ b/Build/source/texk/web2c/uptexdir/kanji.c
@@ -91,8 +91,8 @@ integer ktoken_to_chr(integer c)
}
/* Ref. http://www.unicode.org/Public/UNIDATA/Blocks.txt */
-/* # Blocks-15.1.0.txt */
-/* # Date: 2023-07-28, 15:47:20 GMT */
+/* # Blocks-16.0.0.txt */
+/* # Date: 2024-02-22 */
static long ucs_range[]={
0x0000, /* Basic Latin */ /* 0x00 */
0x0080, /* Latin-1 Supplement */
@@ -279,14 +279,15 @@ static long ucs_range[]={
0x10500, /* Elbasan */
0x10530, /* Caucasian Albanian */
0x10570, /* Vithkuqi */
+ 0x105C0, /* Todhri */
0x10600, /* Linear A */
0x10780, /* Latin Extended-F */
0x10800, /* Cypriot Syllabary */
0x10840, /* Imperial Aramaic */
0x10860, /* Palmyrene */
0x10880, /* Nabataean */
- 0x108E0, /* Hatran */
- 0x10900, /* Phoenician */ /* 0xc0 */
+ 0x108E0, /* Hatran */ /* 0xc0 */
+ 0x10900, /* Phoenician */
0x10920, /* Lydian */
0x10980, /* Meroitic Hieroglyphs */
0x109A0, /* Meroitic Cursive */
@@ -301,8 +302,9 @@ static long ucs_range[]={
0x10C00, /* Old Turkic */
0x10C80, /* Old Hungarian */
0x10D00, /* Hanifi Rohingya */
+ 0x10D40, /* Garay */ /* 0xd0 */
0x10E60, /* Rumi Numeral Symbols */
- 0x10E80, /* Yezidi */ /* 0xd0 */
+ 0x10E80, /* Yezidi */
0x10EC0, /* Arabic Extended-C */
0x10F00, /* Old Sogdian */
0x10F30, /* Sogdian */
@@ -316,26 +318,29 @@ static long ucs_range[]={
0x11150, /* Mahajani */
0x11180, /* Sharada */
0x111E0, /* Sinhala Archaic Numbers */
- 0x11200, /* Khojki */
+ 0x11200, /* Khojki */ /* 0xe0 */
0x11280, /* Multani */
- 0x112B0, /* Khudawadi */ /* 0xe0 */
+ 0x112B0, /* Khudawadi */
0x11300, /* Grantha */
+ 0x11380, /* Tulu-Tigalaria */
0x11400, /* Newa */
0x11480, /* Tirhuta */
0x11580, /* Siddham */
0x11600, /* Modi */
0x11660, /* Mongolian Supplement */
0x11680, /* Takri */
+ 0x116D0, /* Myanmar Extended-C */
0x11700, /* Ahom */
0x11800, /* Dogra */
0x118A0, /* Warang Citi */
0x11900, /* Dives Akuru */
- 0x119A0, /* Nandinagari */
+ 0x119A0, /* Nandinagari */ /* 0xf0 */
0x11A00, /* Zanabazar Square */
0x11A50, /* Soyombo */
0x11AB0, /* Unified Canadian Aboriginal Syllabics Extended-A */
- 0x11AC0, /* Pau Cin Hau */ /* 0xf0 */
+ 0x11AC0, /* Pau Cin Hau */
0x11B00, /* Devanagari Extended-A */
+ 0x11BC0, /* Sunuwar */
0x11C00, /* Bhaiksuki */
0x11C70, /* Marchen */
0x11D00, /* Masaram Gondi */
@@ -345,20 +350,23 @@ static long ucs_range[]={
0x11FB0, /* Lisu Supplement */
0x11FC0, /* Tamil Supplement */
0x12000, /* Cuneiform */
- 0x12400, /* Cuneiform Numbers and Punctuation */
+ 0x12400, /* Cuneiform Numbers and Punctuation */ /* 0x100 */
0x12480, /* Early Dynastic Cuneiform */
0x12F90, /* Cypro-Minoan */
0x13000, /* Egyptian Hieroglyphs */
0x13430, /* Egyptian Hieroglyph Format Controls */
- 0x14400, /* Anatolian Hieroglyphs */ /* 0x100 */
+ 0x13460, /* Egyptian Hieroglyphs Extended-A */
+ 0x14400, /* Anatolian Hieroglyphs */
+ 0x16100, /* Gurung Khema */
0x16800, /* Bamum Supplement */
0x16A40, /* Mro */
0x16A70, /* Tangsa */
0x16AD0, /* Bassa Vah */
0x16B00, /* Pahawh Hmong */
+ 0x16D40, /* Kirat Rai */
0x16E40, /* Medefaidrin */
0x16F00, /* Miao */
- 0x16FE0, /* Ideographic Symbols and Punctuation */
+ 0x16FE0, /* Ideographic Symbols and Punctuation */ /* 0x110 */
0x17000, /* Tangut */
0x18800, /* Tangut Components */
0x18B00, /* Khitan Small Script */
@@ -366,14 +374,15 @@ static long ucs_range[]={
0x1AFF0, /* Kana Extended-B */
0x1B000, /* Kana Supplement */
0x1B100, /* Kana Extended-A */
- 0x1B130, /* Small Kana Extension */ /* 0x110 */
+ 0x1B130, /* Small Kana Extension */
0x1B170, /* Nushu */
0x1BC00, /* Duployan */
0x1BCA0, /* Shorthand Format Controls */
+ 0x1CC00, /* Symbols for Legacy Computing Supplement */
0x1CF00, /* Znamenny Musical Notation */
0x1D000, /* Byzantine Musical Symbols */
0x1D100, /* Musical Symbols */
- 0x1D200, /* Ancient Greek Musical Notation */
+ 0x1D200, /* Ancient Greek Musical Notation */ /* 0x120 */
0x1D2C0, /* Kaktovik Numerals */
0x1D2E0, /* Mayan Numerals */
0x1D300, /* Tai Xuan Jing Symbols */
@@ -382,13 +391,14 @@ static long ucs_range[]={
0x1D800, /* Sutton SignWriting */
0x1DF00, /* Latin Extended-G */
0x1E000, /* Glagolitic Supplement */
- 0x1E030, /* Cyrillic Extended-D */ /* 0x120 */
+ 0x1E030, /* Cyrillic Extended-D */
0x1E100, /* Nyiakeng Puachue Hmong */
0x1E290, /* Toto */
0x1E2C0, /* Wancho */
0x1E4D0, /* Nag Mundari */
+ 0x1E5D0, /* Ol Onal */
0x1E7E0, /* Ethiopic Extended-B */
- 0x1E800, /* Mende Kikakui */
+ 0x1E800, /* Mende Kikakui */ /* 0x130 */
0x1E900, /* Adlam */
0x1EC70, /* Indic Siyaq Numbers */
0x1ED00, /* Ottoman Siyaq Numbers */
@@ -398,13 +408,13 @@ static long ucs_range[]={
0x1F0A0, /* Playing Cards */
0x1F100, /* Enclosed Alphanumeric Supplement */
0x1F200, /* Enclosed Ideographic Supplement */
- 0x1F300, /* Miscellaneous Symbols and Pictographs */ /* 0x130 */
+ 0x1F300, /* Miscellaneous Symbols and Pictographs */
0x1F600, /* Emoticons */
0x1F650, /* Ornamental Dingbats */
0x1F680, /* Transport and Map Symbols */
0x1F700, /* Alchemical Symbols */
0x1F780, /* Geometric Shapes Extended */
- 0x1F800, /* Supplemental Arrows-C */
+ 0x1F800, /* Supplemental Arrows-C */ /* 0x140 */
0x1F900, /* Supplemental Symbols and Pictographs */
0x1FA00, /* Chess Symbols */
0x1FA70, /* Symbols and Pictographs Extended-A */
@@ -414,12 +424,13 @@ static long ucs_range[]={
0x2B740, /* CJK Unified Ideographs Extension D */
0x2B820, /* CJK Unified Ideographs Extension E */
0x2CEB0, /* CJK Unified Ideographs Extension F */
- 0x2EBF0, /* CJK Unified Ideographs Extension I */ /* 0x140 */
+ 0x2EBF0, /* CJK Unified Ideographs Extension I */
0x2F800, /* CJK Compatibility Ideographs Supplement */
0x30000, /* CJK Unified Ideographs Extension G */
0x31350, /* CJK Unified Ideographs Extension H */
- 0x323B0, /* reserved */
- 0x40000, /* reserved */
+ 0x323B0, /* CJK Unified Ideographs Extension J (Unicode 17.0) */
+ 0x33480, /* reserved */
+ 0x40000, /* reserved */ /* 0x150 */
0x50000, /* reserved */
0x60000, /* reserved */
0x70000, /* reserved */
@@ -430,14 +441,14 @@ static long ucs_range[]={
0xC0000, /* reserved */
0xD0000, /* reserved */
0xE0000, /* Tags */
- 0xE0100, /* Variation Selectors Supplement */ /* 0x150 */
+ 0xE0100, /* Variation Selectors Supplement */
0xE01F0, /* reserved */
0xF0000, /* Supplementary Private Use Area-A */
0x100000, /* Supplementary Private Use Area-B */
/* Value over 0x10FFFF is illegal under Unicode,
- They are for some special use. *** experimental *** */
+ We use for some special application. */
0x110000, /* Reserved */
- 0x120000, /* Reserved */
+ 0x120000, /* Reserved */ /* 0x160 */
0x130000, /* Reserved */
0x140000, /* Reserved */
0x150000, /* Reserved */
@@ -448,20 +459,20 @@ static long ucs_range[]={
0x1A0000, /* Reserved */
0x1B0000, /* Reserved */
0x1C0000, /* Reserved */
- 0x1D0000, /* Reserved */ /* 0x160 */
+ 0x1D0000, /* Reserved */
0x1E0000, /* Reserved */
0x1F0000, /* Reserved */
0x200000, /* Reserved */
0x210000, /* Reserved */
- 0x220000, /* Kana with Voiced Sound Mark */
+ 0x220000, /* Kana with Voiced Sound Mark */ /* 0x170 */
0x240000, /* Kana with Semi-Voiced Sound Mark */
0x25E6E6, /* Emoji Flag Sequence */
0x260000, /* Emoji with Modifier Fitzpatrick */
0x300000, /* Reserved */
0x400000, /* Standardized Variation Sequence */
0x800000, /* Emoji Keycap Sequence */
- 0x800080, /* Ideographic Variation Sequence */ /* 0x16C */
- CJK_CHAR_LIMIT, /* Ideographic Variation Sequence, VS49..VS256 */
+ 0x800080, /* Ideographic Variation Sequence, VS17..VS48 */
+ CJK_CHAR_LIMIT, /* Ideographic Variation Sequence, VS49..VS256 */ /* 0x178 */
IVS_CHAR_LIMIT
};
@@ -544,11 +555,11 @@ integer kcatcodekey(integer c)
if ( COMBINING_ENCLOSING_KEYCAP==c )
return 0x1FA;
break;
- case 0x12e: /* Block : Enclosed Alphanumeric Supplement */
+ case 0x138: /* Block : Enclosed Alphanumeric Supplement */
if ( REGIONAL_INDICATOR_SYMBOL_LETTER_A <=c && c<= REGIONAL_INDICATOR_SYMBOL_LETTER_Z )
return 0x1FB;
break;
- case 0x130: /* Block : Miscellaneous Symbols and Pictographs */
+ case 0x13a: /* Block : Miscellaneous Symbols and Pictographs */
if ( EMOJI_MODIFIER_FITZPATRIC_TYPE1_2 <=c && c<= EMOJI_MODIFIER_FITZPATRIC_TYPE6 )
return 0x1FC;
break;
diff --git a/Build/source/texk/web2c/uptexdir/ptex-base.ch b/Build/source/texk/web2c/uptexdir/ptex-base.ch
index 6b77387d58e..36496b2e4b2 100644
--- a/Build/source/texk/web2c/uptexdir/ptex-base.ch
+++ b/Build/source/texk/web2c/uptexdir/ptex-base.ch
@@ -74,6 +74,8 @@
% (2022-10-24) HY pTeX p4.1.0 Add new syntax \font [in jis/ucs].
% New primitives: \tojis, \ptextracingfonts and \ptexfontname.
% (2023-09-17) HY pTeX p4.1.1 Support more than 256 different glue/kern.
+% (2024-09-22) HK pTeX p4.1.2 Enable to get character codes
+% from control symbols by using backquote.
@x
% Here is TeX material that gets inserted after \input webmac
@@ -88,8 +90,8 @@
@y
@d pTeX_version=4
@d pTeX_minor_version=1
-@d pTeX_revision==".1"
-@d pTeX_version_string=='-p4.1.1' {current \pTeX\ version}
+@d pTeX_revision==".2"
+@d pTeX_version_string=='-p4.1.2' {current \pTeX\ version}
@#
@d pTeX_banner=='This is pTeX, Version 3.141592653',pTeX_version_string
@d pTeX_banner_k==pTeX_banner
@@ -1305,10 +1307,11 @@ for k:=0 to 255 do
begin cat_code(k):=other_char; kcat_code(k):=other_kchar;
math_code(k):=hi(k); sf_code(k):=1000;
auto_xsp_code(k):=0;
+ inhibit_xsp_type(k):=0; kinsoku_type(k):=0;
end;
for k:=0 to 1023 do
- begin inhibit_xsp_code(k):=0; inhibit_xsp_type(k):=0;
- kinsoku_code(k):=0; kinsoku_type(k):=0;
+ begin inhibit_xsp_code(k):=0;
+ kinsoku_code(k):=0;
end;
@z
@x [17.232] l.4971 - pTeX: initialize cat_code, cur_jfont, cur_tfont
@@ -1523,6 +1526,9 @@ primitive("ptextracingfonts",assign_int,int_base+ptex_tracing_fonts_code);@/
@d v_offset==dimen_par(v_offset_code)
@d t_baseline_shift==dimen_par(t_baseline_shift_code)
@d y_baseline_shift==dimen_par(y_baseline_shift_code)
+@#
+@d enc_jis=1
+@d enc_ucs=2
@z
@x l.5542 - pTeX:
@@ -1660,8 +1666,8 @@ procedure print_font_dir_and_enc(f:internal_font_number);
begin
if font_dir[f]=dir_tate then print("/TATE")
else if font_dir[f]=dir_yoko then print("/YOKO");
- if font_enc[f]=2 then print("+Unicode")
- else if font_enc[f]=1 then print("+JIS");
+ if font_enc[f]=enc_ucs then print("+Unicode")
+ else if font_enc[f]=enc_jis then print("+JIS");
end;
@z
@@ -3324,9 +3330,9 @@ if jfm_flag<>dir_default then
for k:=ctype_base[f] to ctype_base[f]+nt-1 do
begin
fget; read_twentyfourx(cx);
- if jfm_enc=2 then {Unicode TFM}
+ if jfm_enc=enc_ucs then {Unicode TFM}
font_info[k].hh.rh:=toDVI(fromUCS(cx))
- else if jfm_enc=1 then {JIS-encoded TFM}
+ else if jfm_enc=enc_jis then {JIS-encoded TFM}
font_info[k].hh.rh:=toDVI(fromJIS(cx))
else
font_info[k].hh.rh:=tokanji(cx); {|kchar_code|}
@@ -3584,9 +3590,9 @@ continue:
end;
p:=link(p);
jc:=KANJI(info(p));
- if font_enc[f]=2 then {Unicode TFM}
+ if font_enc[f]=enc_ucs then {Unicode TFM}
jc:=toUCS(jc)
- else if font_enc[f]=1 then {JIS-encoded TFM}
+ else if font_enc[f]=enc_jis then {JIS-encoded TFM}
begin if toJIS(jc)=0 then char_warning_jis(f,jc);
jc:=toJIS(jc); end
else
@@ -7075,8 +7081,8 @@ exit:end;
@ @<Scan the font encoding specification@>=
begin jfm_enc:=0;
if scan_keyword_noexpand("in") then
- if scan_keyword_noexpand("jis") then jfm_enc:=1
- else if scan_keyword_noexpand("ucs") then jfm_enc:=2
+ if scan_keyword_noexpand("jis") then jfm_enc:=enc_jis
+ else if scan_keyword_noexpand("ucs") then jfm_enc:=enc_ucs
else begin
print_err("Unknown TFM encoding");
@.Unknown TFM encoding@>
diff --git a/Build/source/texk/web2c/uptexdir/ptex_version.h b/Build/source/texk/web2c/uptexdir/ptex_version.h
index 13e0f1467b3..247aabb113d 100644
--- a/Build/source/texk/web2c/uptexdir/ptex_version.h
+++ b/Build/source/texk/web2c/uptexdir/ptex_version.h
@@ -1 +1 @@
-#define PTEX_VERSION "p4.1.1"
+#define PTEX_VERSION "p4.1.2"
diff --git a/Build/source/texk/web2c/uptexdir/tests/unibib.bbl b/Build/source/texk/web2c/uptexdir/tests/unibib.bbl
index e61fc1c3474..b65e9ec58b9 100644
--- a/Build/source/texk/web2c/uptexdir/tests/unibib.bbl
+++ b/Build/source/texk/web2c/uptexdir/tests/unibib.bbl
@@ -1,4 +1,4 @@
-\begin{thebibliography}{1}
+\begin{thebibliography}{10}
\bibitem{björk2020}
Tomas Björk.
@@ -22,6 +22,11 @@ Dustin Boswell and Trevor Foucher.
\newblock 구운몽.
\newblock 高麗書林, 1975.
+\bibitem{imahashi}
+𫝆𫞎姬, 𡈽𪧦, 𠮷田真.
+\newblock CJK統合漢字拡張🄑--🄓のテスト --- その🄰.
+\newblock 冬𠘨𫝷, 2024.
+
\bibitem{nikos}
Νίκος Καζαντζάκης.
\newblock {\em Συμπόσιον}.
diff --git a/Build/source/texk/web2c/uptexdir/tests/unibib.bib b/Build/source/texk/web2c/uptexdir/tests/unibib.bib
index 11e6baa22a8..d4c45d08e12 100644
--- a/Build/source/texk/web2c/uptexdir/tests/unibib.bib
+++ b/Build/source/texk/web2c/uptexdir/tests/unibib.bib
@@ -79,3 +79,25 @@
publisher = "Snowman commedian Press",
year = 2020,
}
+
+%% CJK統合漢字拡張B-Dのテスト
+@book{imahashi,
+ author = "𫝆𫞎 姬 and 𡈽 𪧦 and 𠮷田 真",
+ yomi = "Hime Imahashi",
+ title = "CJK統合漢字拡張🄑--🄓のテスト --- その🄰",
+ publisher = "冬𠘨𫝷",
+ year = 2024,
+}
+% 𫝆 U+2B746 CJK Unified Ideographs Extension D AJ:13780
+% 𫞎 U+2B78E CJK Unified Ideographs Extension D AJ:13724
+% 姬 U+2F862 CJK Compatibility Ideographs Supplement AJ:13998
+% 𡈽 U+2123D CJK Unified Ideographs Extension B AJ:13953 J3-2F42
+% 𪧦 U+2A9E6 CJK Unified Ideographs Extension C AJ:14145
+% 𠮷 U+20BB7 CJK Unified Ideographs Extension B AJ:13706
+% 真 U+2F947 CJK Compatibility Ideographs Supplement AJ:13854
+% 冬 U+2F81A CJK Compatibility Ideographs Supplement AJ:13954
+% 𠘨 U+20628 CJK Unified Ideographs Extension B AJ:14105 J4-2332
+% 𫝷 U+2B777 CJK Unified Ideographs Extension D AJ:13782
+% 🄑 U+1F111 Parenthesized Latin Capital Letter B
+% 🄓 U+1F113 Parenthesized Latin Capital Letter B
+% 🄰 U+1F130 Squared Latin Capital Letter A
diff --git a/Build/source/texk/web2c/uptexdir/tests/unibib.tex b/Build/source/texk/web2c/uptexdir/tests/unibib.tex
index 918d11f37b2..73e6a744d7a 100644
--- a/Build/source/texk/web2c/uptexdir/tests/unibib.tex
+++ b/Build/source/texk/web2c/uptexdir/tests/unibib.tex
@@ -82,6 +82,15 @@
publisher = "Snowman commedian Press",
year = 2020,
}
+
+%% CJK統合漢字拡張B-Dのテスト
+@book{imahashi,
+ author = "𫝆𫞎 姬 and 𡈽 𪧦 and 𠮷田 真",
+ yomi = "Hime Imahashi",
+ title = "CJK統合漢字拡張🄑--🄓のテスト --- その🄰",
+ publisher = "冬𠘨𫝷",
+ year = 2024,
+}
\end{filecontents}
\documentclass{ltjsarticle}
diff --git a/Build/source/texk/web2c/uptexdir/upbibtex.ch b/Build/source/texk/web2c/uptexdir/upbibtex.ch
index 36c44471552..4c21ee3efcd 100644
--- a/Build/source/texk/web2c/uptexdir/upbibtex.ch
+++ b/Build/source/texk/web2c/uptexdir/upbibtex.ch
@@ -968,7 +968,7 @@ function is_char_kanji_upbibtex(@!c:integer):boolean;
label exit;
var k:integer;
begin
- { based on upTeX-1.30 kcatcode status: 16,17,19->true / 15,18->false }
+ { based on upTeX-1.35 kcatcode status: 16,17,19->true / 15,18->false }
is_char_kanji_upbibtex := true;
if (is_internalUPTEX) then begin { should be in sync with |kcat_code| of uptex-m.ch }
k := kcatcodekey(c);
@@ -985,8 +985,8 @@ begin
else if k=@"93 then return { Hangul Syllables }
else if k=@"94 then return { Hangul Jamo Extended-B }
else if k=@"99 then return { CJK Compatibility Ideographs }
- else if (k>=@"10D)and(k<=@"110) then return { Kana Extended-B .. Small Kana Extension }
- else if (k>=@"13B)and(k<=@"143) then return { CJK Unified Ideographs Extension B .. H }
+ else if (k>=@"115)and(k<=@"118) then return { Kana Extended-B .. Small Kana Extension }
+ else if (k>=@"145)and(k<=@"14F) then return { CJK Unified Ideographs Extension B .. J }
else if k=@"1FE then return { Fullwidth digit and latin alphabet }
else if k=@"1FF then return; { Halfwidth katakana }
end
diff --git a/Build/source/texk/web2c/uptexdir/uptex-m.ch b/Build/source/texk/web2c/uptexdir/uptex-m.ch
index f4b7c7272d9..91a774162c6 100644
--- a/Build/source/texk/web2c/uptexdir/uptex-m.ch
+++ b/Build/source/texk/web2c/uptexdir/uptex-m.ch
@@ -176,7 +176,6 @@ else if (kcode_pos=1)or((kcode_pos>=@'11)and(kcode_pos<=@'12))
@d other_kchar=18 {kanji codes}
@d max_char_code=18 {largest catcode for individual characters}
@y
-@d latin_ucs=14 {is not cjk characters in ucs code}
@d not_cjk=15 {is not cjk characters}
@d kanji=16 {kanji}
@d kana=17 {hiragana, katakana, alphabet}
@@ -239,6 +238,7 @@ for k:=0 to 255 do
begin cat_code(k):=other_char; kcat_code(k):=other_kchar;
math_code(k):=hi(k); sf_code(k):=1000;
auto_xsp_code(k):=0;
+ inhibit_xsp_type(k):=0; kinsoku_type(k):=0;
end;
@y
eqtb[auto_xspacing_code]:=eqtb[cat_code_base];
@@ -247,6 +247,7 @@ for k:=0 to 255 do
begin cat_code(k):=other_char;
math_code(k):=hi(k); sf_code(k):=1000;
auto_xsp_code(k):=0;
+ inhibit_xsp_type(k):=0; kinsoku_type(k):=0;
end;
for k:=0 to 511 do
begin kcat_code(k):=other_kchar;
@@ -281,12 +282,12 @@ if (isinternalUPTEX) then begin
@t\hskip10pt@>kcat_code(@"99):=kanji; { CJK Compatibility Ideographs }
@t\hskip10pt@>kcat_code(@"9C):=modifier; { Variation Selectors }
{ \hskip10pt|kcat_code(@"A2):=other_kchar;| Halfwidth and Fullwidth Forms }
- @+@t\1@>for k:=@"10D to @"110 do kcat_code(k):=kana; { Kana Extended-B .. Small Kana Extension }
- @+@t\1@>for k:=@"13B to @"143 do kcat_code(k):=kanji; { CJK Unified Ideographs Extension B .. H }
- @t\hskip10pt@>kcat_code(@"150):=modifier; { Variation Selectors Supplement }
- @+@t\1@>for k:=@"165 to @"166 do kcat_code(k):=kana; { Kana with (Semi-)Voiced Sound Mark }
- @t\hskip10pt@>kcat_code(@"16A):=kanji; { Standardized Variation Sequence }
- @+@t\1@>for k:=@"16C to @"16D do kcat_code(k):=kanji; { Ideographic Variation Sequence }
+ @+@t\1@>for k:=@"115 to @"118 do kcat_code(k):=kana; { Kana Extended-B .. Small Kana Extension }
+ @+@t\1@>for k:=@"145 to @"14F do kcat_code(k):=kanji; { CJK Unified Ideographs Extension B .. J }
+ @t\hskip10pt@>kcat_code(@"15B):=modifier; { Variation Selectors Supplement }
+ @+@t\1@>for k:=@"170 to @"171 do kcat_code(k):=kana; { Kana with (Semi-)Voiced Sound Mark }
+ @t\hskip10pt@>kcat_code(@"175):=kanji; { Standardized Variation Sequence }
+ @+@t\1@>for k:=@"177 to @"178 do kcat_code(k):=kanji; { Ideographic Variation Sequence }
@+@t\1@>for k:=@"1F9 to @"1FC do kcat_code(k):=modifier;
{ Combining Katakana-Hiragana (Semi-)Voiced Sound Mark .. Emoji Modifier Fitzpatrick }
@t\hskip10pt@>kcat_code(@"1FD):=not_cjk; { Latin-1 Letters }
@@ -426,7 +427,6 @@ if ((kcp mod @'10)>0)and(nrestmultichr(kcp)>0) then p:=p-(kcp mod @'10);
cur_chr:=fromBUFF(ustringcast(buffer), limit+1, loc);
cur_cmd:=kcat_code(kcatcodekey(cur_chr));
if (multistrlen(ustringcast(buffer), limit+1, loc)>1) and check_kcat_code(cur_cmd,cur_chr) then begin
- if (cur_cmd=latin_ucs) then cur_cmd:=cat_code(cur_chr);
if (cur_cmd=not_cjk) then cur_cmd:=other_kchar;
for l:=loc to loc-1+multistrlen(ustringcast(buffer), limit+1, loc) do
buffer2[l]:=1;
@@ -477,7 +477,6 @@ else begin k:=loc;
cur_chr:=fromBUFF(ustringcast(buffer), limit+1, k);
cat:=kcat_code(kcatcodekey(cur_chr));
if (multistrlen(ustringcast(buffer), limit+1, k)>1) and check_kcat_code(cat,cur_chr) then begin
- if (cat=latin_ucs) then cat:=cat_code(cur_chr);
if (cat=not_cjk) then cat:=other_kchar;
for l:=k to k-1+multistrlen(ustringcast(buffer), limit+1, k) do
buffer2[l]:=1;
@@ -520,7 +519,6 @@ begin repeat
cur_chr:=fromBUFF(ustringcast(buffer), limit+1, k);
cat:=kcat_code(kcatcodekey(cur_chr));
if (multistrlen(ustringcast(buffer), limit+1, k)>1) and check_kcat_code(cat,cur_chr) then begin
- if (cat=latin_ucs) then cat:=cat_code(cur_chr);
if (cat=not_cjk) then cat:=other_kchar;
for l:=k to k-1+multistrlen(ustringcast(buffer), limit+1, k) do
buffer2[l]:=1;
@@ -575,8 +573,6 @@ if cat=other_kchar then k:=k-multilenbuffchar(cur_chr)+1; {now |k| points to fir
cur_tok:=(kanji_ivs*max_cjk_val)+cur_chr
else
cur_tok:=(cur_cmd*max_cjk_val)+cur_chr
- else if (cur_cmd=latin_ucs) then
- cur_tok:=(cat_code(cur_chr)*max_cjk_val)+cur_chr
else cur_tok:=(cur_cmd*max_char_val)+cur_chr
@z
@@ -610,8 +606,6 @@ if cat=other_kchar then k:=k-multilenbuffchar(cur_chr)+1; {now |k| points to fir
cur_tok:=(kanji_ivs*max_cjk_val)+cur_chr
else
cur_tok:=(cur_cmd*max_cjk_val)+cur_chr
- else if (cur_cmd=latin_ucs) then
- cur_tok:=(cat_code(cur_chr)*max_cjk_val)+cur_chr
else cur_tok:=(cur_cmd*max_char_val)+cur_chr
@z
@@ -625,8 +619,6 @@ if cat=other_kchar then k:=k-multilenbuffchar(cur_chr)+1; {now |k| points to fir
cur_tok:=(kanji_ivs*max_cjk_val)+cur_chr
else
cur_tok:=(cur_cmd*max_cjk_val)+cur_chr
- else if (cur_cmd=latin_ucs) then
- cur_tok:=(cat_code(cur_chr)*max_cjk_val)+cur_chr
else cur_tok:=(cur_cmd*max_char_val)+cur_chr
@z
@@ -696,7 +688,6 @@ while k<pool_ptr do
begin t:=so(str_pool[k]);
if t>=@"180 then { there is no |wchar_token| whose code is 0--127. }
begin t:=fromBUFFshort(str_pool, pool_ptr, k); cc:=kcat_code(kcatcodekey(t));
- if (cc=latin_ucs) then cc:=other_char;
if (cc=not_cjk) then cc:=other_kchar;
if (cc=kanji)and(t>=max_cjk_val) then cc:=kanji_ivs;
t:=t+cc*max_cjk_val;
@@ -1186,7 +1177,7 @@ kchar_given: begin print_esc("kchar"); print_hex(chr_code);
if cur_chr=kcat_code_base then m:=kanji else m:=0
@y
@ @<Let |m| be the minimal...@>=
-if cur_chr=kcat_code_base then m:=latin_ucs else m:=0
+if cur_chr=kcat_code_base then m:=not_cjk else m:=0
@z
@x
@@ -1330,8 +1321,6 @@ begin if is_char_node(link(p)) then
info(main_p):=KANJI(cur_chr)+cur_cmd*max_cjk_val
else if cur_cmd=not_cjk then
info(main_p):=KANJI(cur_chr)+other_kchar*max_cjk_val
- else if cur_cmd=latin_ucs then
- info(main_p):=KANJI(cur_chr)+cat_code(cur_chr)*max_cjk_val
else { Does this case occur? }
info(main_p):=KANJI(cur_chr)+kcat_code(kcatcodekey(KANJI(cur_chr)))*max_cjk_val;
@z
@@ -1426,7 +1415,7 @@ end;
function check_kcat_code(@!ct:integer;@!cx:integer):integer;
begin
-if (((ct>=kanji)or((ct=latin_ucs)and(cx<max_ucs_val)))and(enable_cjk_token=0))or(enable_cjk_token=2)then
+if ((ct>=kanji)and(enable_cjk_token=0))or(enable_cjk_token=2)then
check_kcat_code:=1
else check_kcat_code:=0;
end;
diff --git a/Build/source/texk/web2c/vftovp.ch b/Build/source/texk/web2c/vftovp.ch
index 6b423e33408..052f8be743c 100644
--- a/Build/source/texk/web2c/vftovp.ch
+++ b/Build/source/texk/web2c/vftovp.ch
@@ -49,7 +49,7 @@ procedure initialize; {this procedure gets things started properly}
var @!k:integer; {all-purpose index for initialization}
begin print_ln(banner);@/
@y
-@<Define |parse_arguments|@>
+@<Define \(p)|parse_arguments|@>
procedure initialize; {this procedure gets things started properly}
var @!k:integer; {all-purpose index for initialization}
begin
@@ -546,7 +546,7 @@ Parse a Unix-style command line.
@d argument_is (#) == (strcmp (long_options[option_index].name, #) = 0)
-@<Define |parse_arguments|@> =
+@<Define \(p)|parse_arguments|@> =
procedure parse_arguments;
const n_options = 4; {Pascal won't count array lengths for us.}
var @!long_options: array[0..n_options] of getopt_struct;
diff --git a/Build/source/texk/web2c/vptovf.ch b/Build/source/texk/web2c/vptovf.ch
index d9ec8935155..2d15663617e 100644
--- a/Build/source/texk/web2c/vptovf.ch
+++ b/Build/source/texk/web2c/vptovf.ch
@@ -1,5 +1,11 @@
% vptovf.ch for C compilation with web2c.
+@x [0] l.19
+\def\(#1){} % this is used to make section names sort themselves better
+@y
+\def\({} % this is used to make section names sort themselves better
+@z
+
@x [0] l.22
\def\title{VP\lowercase{to}VF}
@y
@@ -34,7 +40,7 @@ procedure initialize; {this procedure gets things started properly}
var @<Local variables for initialization@>@/
begin print_ln(banner);@/
@y
-@<Define |parse_arguments|@>
+@<Define \(|parse_arguments|@>
procedure initialize; {this procedure gets things started properly}
var @<Local variables for initialization@>@/
begin kpse_set_program_name (argv[0], my_name);
@@ -237,7 +243,7 @@ Parse a Unix-style command line.
@d argument_is (#) == (strcmp (long_options[option_index].name, #) = 0)
-@<Define |parse_arguments|@> =
+@<Define \(|parse_arguments|@> =
procedure parse_arguments;
const n_options = 3; {Pascal won't count array lengths for us.}
var @!long_options: array[0..n_options] of getopt_struct;
diff --git a/Build/source/texk/web2c/weav-twill.ch b/Build/source/texk/web2c/weav-twill.ch
index 17c02718758..6678a053932 100644
--- a/Build/source/texk/web2c/weav-twill.ch
+++ b/Build/source/texk/web2c/weav-twill.ch
@@ -51,9 +51,11 @@ Limbo.
\def\title{TWILL for \TeX~Live}
@z
-@x 48c24
+@x 47c23
+ \def\rheader{\mainfont Appendix D\hfil \contentspagenumber}
\centerline{\titlefont The {\ttitlefont WEAVE} processor}
@y
+ \def\rheader{\mainfont Appendix D${}'$\hfil \contentspagenumber}
\centerline{\titlefont The {\ttitlefont TWILL} processor}
@z
@@ -249,6 +251,22 @@ Section 71.
@y
@z
+Section 75.
+
+@x 1323
+@<Move |buffer| and |limit| to |change_buffer| and |change_limit|@>;
+@y
+@<Move \(|buffer| and |limit| to |change_buffer| and |change_limit|@>;
+@z
+
+Section 78.
+
+@x 1357
+@ @<Move |buffer| and |limit| to |change_buffer| and |change_limit|@>=
+@y
+@ @<Move \(|buffer| and |limit| to |change_buffer| and |change_limit|@>=
+@z
+
Section 79.
@x 1371,1383d1401
@@ -277,6 +295,12 @@ if not changed_module[module_count] then
@y
@z
+@x 1405
+ @<Move |buffer| and |limit|...@>;
+@y
+ @<Move \(|buffer| and |limit|...@>;
+@z
+
Section 82.
@x 1450,1454c1463,1469
diff --git a/Build/source/texk/web2c/weave.ch b/Build/source/texk/web2c/weave.ch
index 5a932e576ab..9428be4bfd5 100644
--- a/Build/source/texk/web2c/weave.ch
+++ b/Build/source/texk/web2c/weave.ch
@@ -81,7 +81,7 @@ procedure initialize;
const @<Constants in the outer block@>@/
type @<Types in the outer block@>@/
var @<Globals in the outer block@>@/
-@<Define |parse_arguments|@>@/
+@<Define \(|parse_arguments|@>@/
@<Error handling procedures@>@/
procedure initialize;
var @<Local variables for initialization@>@/
@@ -159,7 +159,6 @@ for i:=@'200 to @'377 do xchr[i]:=chr(i);
@d term_out==stdout
@d print(#)==write(term_out,#) {`|print|' means write on the terminal}
@z
-
@x [3.20] l.521
@<Globals...@>=
@!term_out:text_file; {the terminal as an output file}
@@ -282,7 +281,6 @@ there are programs that need more than 65536 bytes; \TeX\ is one of these
(and the pdf\TeX\ variant even requires more than twice that amount when
its ``final'' change file is applied).
@z
-
@x [5.37] l.729
is either 0 or 1. (For generality, the first index is actually allowed to
run between 0 and |ww-1|, where |ww| is defined to be 2; the program will
@@ -290,7 +288,6 @@ run between 0 and |ww-1|, where |ww| is defined to be 2; the program will
is either 0, 1 or 2. (For generality, the first index is actually allowed to
run between 0 and |ww-1|, where |ww| is defined to be 3; the program will
@z
-
@x [5.37] l.734
@d ww=2 {we multiply the byte capacity by approximately this amount}
@y
@@ -340,7 +337,6 @@ command line), then we use alternative \TeX\ macros from `\.{\\input pwebmac}'.
@.\\input pwebmac@>
@.pwebmac@>
@z
-
@x [12.124] l.2204
out_ptr:=1; out_line:=1; out_buf[1]:="c"; write(tex_file,'\input webma');
@y
@@ -356,7 +352,6 @@ the break.
preceded by another backslash or a \TeX\ comment marker. In the latter case, a
|'%'| is output at the break.
@z
-
@x [12.127] l.2248 - deal with malign user input
if (d="\")and(out_buf[k-1]<>"\") then {in this case |k>1|}
@y
@@ -411,7 +406,6 @@ if cat[pp+1]=close then squash(pp,1,stmt,-2)(43)
@y
if cat[pp+1]=close then reduce(pp,0,stmt,-2)(43)
@z
-
@x [15.167] l.3277 - Apply 'squash(...,2,...)'.
begin app(force); app(backup); app2(pp); reduce(pp,2,intro,-3)(44);
@y
@@ -436,7 +430,6 @@ scrap list.
scrap list. This procedure takes advantage of the simplification that
occurs when |k=0|.
@z
-
@x [15.172] l.3330
begin cat[j]:=c; trans[j]:=text_ptr; freeze_text;
@y
@@ -446,7 +439,6 @@ if k>0 then
trans[j]:=text_ptr; freeze_text;
end;
@z
-
@x [15.172] l.3337 - Fix spacing.
@<Change |pp| to $\max(|scrap_base|,|pp+d|)$@>;
@y
@@ -466,7 +458,6 @@ procedure takes advantage of the simplification that occurs when |k=1|.
@ Similarly, the `|squash|' macro invokes a procedure called `|sq|', which
combines |app|${}_k$ and |red| for matching numbers~|k|.
@z
-
@x [15.174] l.3349
var i:0..max_scraps; {index into scrap memory}
begin if k=1 then
@@ -488,6 +479,56 @@ begin
red(j,k,c,d);
@z
+@x [16.185] l.3555
+string,verbatim: @<Append a \(string scrap@>;
+identifier: @<Append an identifier scrap@>;
+TeX_string: @<Append a \TeX\ string scrap@>;
+@y
+string,verbatim: @<Append \(a \(string scrap@>;
+identifier: @<Append \(an identifier scrap@>;
+TeX_string: @<Append \(a \TeX\ string scrap@>;
+@z
+
+@x [16.189] l.3652
+@<Append a \(string scrap@>=
+@y
+@<Append \(a \(string scrap@>=
+@z
+
+@x [16.190] l.3690
+@ @<Append a \TeX\ string scrap@>=
+@y
+@ @<Append \(a \TeX\ string scrap@>=
+@z
+
+@x [16.191] l.3697
+@ @<Append an identifier scrap@>=
+@y
+@ @<Append \(an identifier scrap@>=
+@z
+
+@x [16.193] l.3731
+else_like: begin @<Append |terminator| if not already present@>;
+@y
+else_like: begin @<Append \(|terminator| if not already present@>;
+@z
+@x [16.193] l.3734
+end_like: begin @<Append |term...@>;
+@y
+end_like: begin @<Append \(|term...@>;
+@z
+@x [16.193] l.3752
+until_like: begin @<Append |term...@>;
+@y
+until_like: begin @<Append \(|term...@>;
+@z
+
+@x [16.194] l.3762
+@<Append |termin...@>=
+@y
+@<Append \(|termin...@>=
+@z
+
@x [19.239] l.4537 - omit index and module names if no_xref set
@<Phase III: Output the cross-reference index@>=
@y
@@ -504,7 +545,6 @@ if no_xref then begin
end
else begin
@z
-
@x [19.239] l.4551
print('Done.');
@y
@@ -519,7 +559,6 @@ any error stop will set |debug_cycle| to zero.
@d term_in==stdin
@z
-
@x [20.258] l.4790
@!term_in:text_file; {the user's terminal as an input file}
@y
@@ -536,7 +575,6 @@ print_ln(banner); {print a ``banner line''}
print (banner); {print a ``banner line''}
print_ln (version_string);
@z
-
@x [21.261] l.4856
end_of_WEAVE:
stat @<Print statistics about memory usage@>;@+tats@;@/
@@ -562,7 +600,7 @@ Parse a Unix-style command line.
@d argument_is (#) == (strcmp (long_options[option_index].name, #) = 0)
-@<Define |parse_arguments|@> =
+@<Define \(|parse_arguments|@> =
procedure parse_arguments;
const n_options = 4; {Pascal won't count array lengths for us.}
var @!long_options: array[0..n_options] of getopt_struct;