summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKarl Berry <karl@freefriends.org>2021-09-21 16:46:46 +0000
committerKarl Berry <karl@freefriends.org>2021-09-21 16:46:46 +0000
commit307dd6b08adb9d64007e887bd99927bf55567f13 (patch)
tree7dceeefebd8ee7b2f593235e4454500194640763
parent659b02c307c05da212ec98a3069ed1c4d39ab58d (diff)
hitexdir: initial import, omitted from build by default
git-svn-id: svn://tug.org/texlive/trunk@60569 c570f23f-e606-0410-a88d-b1316a301751
-rwxr-xr-xBuild/source/configure17
-rw-r--r--Build/source/doc/tlbuild.info1535
-rw-r--r--Build/source/doc/tlbuild.texi36
-rwxr-xr-xBuild/source/libs/configure17
-rwxr-xr-xBuild/source/texk/configure17
-rw-r--r--Build/source/texk/web2c/ChangeLog6
-rw-r--r--Build/source/texk/web2c/Makefile.am6
-rw-r--r--Build/source/texk/web2c/Makefile.in809
-rw-r--r--Build/source/texk/web2c/ac/web2c.ac3
-rw-r--r--Build/source/texk/web2c/c-auto.in2
-rwxr-xr-xBuild/source/texk/web2c/configure30
-rw-r--r--Build/source/texk/web2c/configure.ac2
-rw-r--r--Build/source/texk/web2c/hitexdir/am/hitex.am100
-rw-r--r--Build/source/texk/web2c/hitexdir/format.w10509
-rw-r--r--Build/source/texk/web2c/hitexdir/hitex.w4753
-rw-r--r--Build/source/texk/web2c/hitexdir/htex.w29180
-rw-r--r--Build/source/texk/web2c/hitexdir/lex.sed5
-rw-r--r--Build/source/texk/web2c/hitexdir/yacc.sed3
-rwxr-xr-xBuild/source/utils/configure17
19 files changed, 46036 insertions, 1011 deletions
diff --git a/Build/source/configure b/Build/source/configure
index 8b6b36cad03..487b8d91af2 100755
--- a/Build/source/configure
+++ b/Build/source/configure
@@ -852,6 +852,7 @@ enable_uptex_synctex
enable_euptex
enable_euptex_synctex
enable_aleph
+enable_hitex
enable_pdftex
enable_pdftex_synctex
enable_luatex
@@ -1669,6 +1670,7 @@ Optional Features:
--disable-euptex do not compile and install e-upTeX
--disable-euptex-synctex build e-upTeX without SyncTeX support
--disable-aleph do not compile and install Aleph
+ --enable-hitex compile and install HiTeX
--disable-pdftex do not compile and install pdfTeX
--disable-pdftex-synctex build pdfTeX without SyncTeX support
--disable-luatex do not compile and install LuaTeX
@@ -5488,6 +5490,21 @@ case $enable_aleph in #(
*) :
enable_aleph=yes ;;
esac
+# Check whether --enable-hitex was given.
+if test ${enable_hitex+y}
+then :
+ enableval=$enable_hitex;
+fi
+case $enable_hitex in #(
+ yes | no) :
+ ;; #(
+ *) :
+ enable_hitex=no ;;
+esac
+
+test "x$enable_web2c:$enable_hitex" = xyes:yes && {
+ need_zlib=yes
+}
# Check whether --enable-pdftex was given.
if test ${enable_pdftex+y}
then :
diff --git a/Build/source/doc/tlbuild.info b/Build/source/doc/tlbuild.info
index 62698a46c0a..d33201125fc 100644
--- a/Build/source/doc/tlbuild.info
+++ b/Build/source/doc/tlbuild.info
@@ -1287,11 +1287,12 @@ will need the usual Automake build-related files ('configure.ac',
* Menu:
* Adding a new program module::
+* Adding a new engine::
* Adding a new generic library module::
* Adding a new TeX-specific library module::

-File: tlbuild.info, Node: Adding a new program module, Next: Adding a new generic library module, Up: Extending TeX Live
+File: tlbuild.info, Node: Adding a new program module, Next: Adding a new engine, Up: Extending TeX Live
6.6.1 Adding a new program module
---------------------------------
@@ -1394,16 +1395,40 @@ end, also make sure that the whole tree builds from scratch.
After final success, don't forget to commit. (Or email the TL
maintainers with the patch.)
- Caveat: adding a new TeX engine is not completely different, but it's
-not all that similar, either. In that case, the work is done inside a
-new subdirectory of 'texk/web2c/'. Many things are common to all the
-engines, other things need to be copied and possibly modified for each
-one, yet others are unique to each. No general recipe is possible.
+
+File: tlbuild.info, Node: Adding a new engine, Next: Adding a new generic library module, Prev: Adding a new program module, Up: Extending TeX Live
+
+6.6.2 Adding a new engine
+-------------------------
+
+Adding a new TeX engine is not completely different from adding a
+program, but it's not all that similar, either. In this case, the main
+work is done by creating a new subdirectory of 'texk/web2c/' for the
+engine. The subdirectory is conventionally named ending in 'dir', like
+'pdftexdir' and 'xetexdir'.
+
+ The source files for the new engine should be put in this
+'NEWENGINEdir' subdirectory. Also, a file
+'NEWENGINEdir/am/NEWENGINE.am' (e.g., 'pdftexdir/am/pdftex.am' is needed
+with the Makefile fragment needed to build it.
+
+ The overall 'web2c/Makefile.am' needs to have an 'include' statement
+added to insert that 'NEWENGINE.am' file.
+
+ In 'web2c/ac/web2c.ac', a line needs to be added in the definition of
+the 'kpse_tex_progs' variable to include it in the build. That line
+specifies whether the new engine is built by default, and the additional
+libraries requires.
+
+ For examples of building engines in CWEB, you can check the existing
+'hitexdir' and 'mplibdir' directories; these are somewhat simpler than
+LuaTeX. Of course, every engine will have its own unique features and
+requirements, so existing examples will only take you so far.

-File: tlbuild.info, Node: Adding a new generic library module, Next: Adding a new TeX-specific library module, Prev: Adding a new program module, Up: Extending TeX Live
+File: tlbuild.info, Node: Adding a new generic library module, Next: Adding a new TeX-specific library module, Prev: Adding a new engine, Up: Extending TeX Live
-6.6.2 Adding a new generic library module
+6.6.3 Adding a new generic library module
-----------------------------------------
A generic library module in a subdirectory 'libs/LIB' must not depend on
@@ -1445,7 +1470,7 @@ line:

File: tlbuild.info, Node: Adding a new TeX-specific library module, Prev: Adding a new generic library module, Up: Extending TeX Live
-6.6.3 Adding a new TeX-specific library module
+6.6.4 Adding a new TeX-specific library module
----------------------------------------------
A TeX-specific library module in a subdirectory 'texk/LIB' may depend on
@@ -5357,588 +5382,569 @@ Index
* Menu:
* --bindir configure option: --prefix --bindir ....
- (line 6)
+ (line 6)
* --bindir configure option <1>: --enable-multiplatform.
- (line 6)
-* --build=HOST: Cross configuring. (line 6)
-* --disable-all-packages: Build one package. (line 6)
-* --disable-all-pkgs: --disable-all-pkgs. (line 6)
+ (line 6)
+* --build=HOST: Cross configuring. (line 6)
+* --disable-all-packages: Build one package. (line 6)
+* --disable-all-pkgs: --disable-all-pkgs. (line 6)
* --disable-bibtex8: Configure options for texk/bibtex-x.
- (line 9)
+ (line 9)
* --disable-bibtexu: Configure options for texk/bibtex-x.
- (line 12)
+ (line 12)
* --disable-dump-share: Configure options for texk/web2c.
- (line 27)
+ (line 27)
* --disable-dvipdfmx: Configure options for texk/dvipdfm-x.
- (line 12)
+ (line 12)
* --disable-etex-synctex: Configure options for texk/web2c.
- (line 59)
+ (line 59)
* --disable-ipc: Configure options for texk/web2c.
- (line 31)
-* --disable-largefile: --disable-largefile. (line 6)
+ (line 31)
+* --disable-largefile: --disable-largefile. (line 6)
* --disable-linked-scripts: Configure options for texk/texlive.
- (line 6)
+ (line 6)
* --disable-mf-nowin: Configure options for texk/web2c.
- (line 34)
-* --disable-missing: --disable-missing. (line 6)
+ (line 34)
+* --disable-missing: --disable-missing. (line 6)
* --disable-native-texlive-build: --disable-native-texlive-build.
- (line 6)
+ (line 6)
* --disable-PROG: --enable-PROG --disable-PROG.
- (line 6)
+ (line 6)
* --disable-synctex: Configure options for texk/web2c.
- (line 64)
+ (line 64)
* --disable-tex: Configure options for texk/web2c.
- (line 37)
+ (line 37)
* --disable-web-progs: Configure options for texk/web2c.
- (line 41)
+ (line 41)
* --disable-xdvipdfmx: Configure options for texk/dvipdfm-x.
- (line 15)
+ (line 15)
* --enable-*win for Metafont window support: Configure options for texk/web2c.
- (line 55)
+ (line 55)
* --enable-auto-core: Configure options for texk/web2c.
- (line 45)
+ (line 45)
* --enable-compiler-warnings=LEVEL: --enable-compiler-warnings=LEVEL.
- (line 6)
-* --enable-cxx-runtime-hack: Macros for compilers.
- (line 29)
+ (line 6)
+* --enable-cxx-runtime-hack: Macros for compilers. (line 29)
* --enable-etex: Configure options for texk/web2c.
- (line 37)
+ (line 37)
* --enable-libtool-hack: Configure options for texk/web2c.
- (line 50)
-* --enable-maintainer-mode: Build system tools. (line 28)
+ (line 50)
+* --enable-maintainer-mode: Build system tools. (line 28)
* --enable-maintainer-mode <1>: --enable-maintainer-mode.
- (line 6)
-* --enable-missing to ignore dependencies: Build one package. (line 73)
-* --enable-mktextfm-default: kpathsea library. (line 18)
+ (line 6)
+* --enable-missing to ignore dependencies: Build one package. (line 73)
+* --enable-mktextfm-default: kpathsea library. (line 18)
* --enable-multiplatform: --enable-multiplatform.
- (line 6)
+ (line 6)
* --enable-PROG: --enable-PROG --disable-PROG.
- (line 6)
-* --enable-shared: --enable-shared. (line 6)
+ (line 6)
+* --enable-shared: --enable-shared. (line 6)
* --enable-silent-rules: --enable-silent-rules.
- (line 6)
+ (line 6)
* --enable-tex-synctex: Configure options for texk/web2c.
- (line 59)
+ (line 59)
* --enable-texlive-build: --disable-native-texlive-build.
- (line 15)
+ (line 15)
* --enable-xi2-scrolling: Configure options for texk/xdvik.
- (line 13)
+ (line 13)
* --enable-xindy-docs: Configure options for utils/xindy.
- (line 10)
+ (line 10)
* --enable-xindy-rules: Configure options for utils/xindy.
- (line 6)
-* --host=HOST: Cross configuring. (line 6)
+ (line 6)
+* --host=HOST: Cross configuring. (line 6)
* --libdir configure option: --enable-multiplatform.
- (line 6)
-* --no-clean Build option: Build problems. (line 6)
+ (line 6)
+* --no-clean Build option: Build problems. (line 6)
* --prefix configure option: --prefix --bindir ....
- (line 6)
+ (line 6)
* --with-banner-add=STR: Configure options for texk/web2c.
- (line 6)
+ (line 6)
* --with-clisp-runtime=FILENAME: Configure options for utils/xindy.
- (line 14)
+ (line 14)
* --with-editor=CMD: Configure options for texk/web2c.
- (line 11)
+ (line 11)
* --with-fontconfig-includes=DIR: Configure options for texk/web2c.
- (line 16)
+ (line 16)
* --with-fontconfig-libdir=DIR: Configure options for texk/web2c.
- (line 16)
+ (line 16)
* --with-gs=FILENAME: Configure options for texk/xdvik.
- (line 6)
+ (line 6)
* --with-LIB-includes=DIR, -libdir: Library-specific configure options.
- (line 16)
+ (line 16)
* --with-libgs-includes, -libdir: Configure options for texk/dvisvgm.
- (line 17)
-* --with-system-kpathsea: kpathsea library. (line 13)
+ (line 17)
+* --with-system-kpathsea: kpathsea library. (line 13)
* --with-system-LIB: Adding a new generic library module.
- (line 34)
+ (line 34)
* --with-system-LIB <1>: Library-specific configure options.
- (line 9)
+ (line 9)
* --with-system-libgs: Configure options for texk/dvisvgm.
- (line 6)
-* --with-xdvi-x-toolkit: xdvik package. (line 21)
+ (line 6)
+* --with-xdvi-x-toolkit: xdvik package. (line 21)
* --with-xdvi-x-toolkit=KIT: Configure options for texk/xdvik.
- (line 9)
+ (line 9)
* --without-libgs: Configure options for texk/dvisvgm.
- (line 12)
-* --without-ln-s: --without-ln-s. (line 6)
-* --without-x: --without-x. (line 6)
-* -C configure option: Build in parallel. (line 11)
-* -j make option: Build in parallel. (line 6)
+ (line 12)
+* --without-ln-s: --without-ln-s. (line 6)
+* --without-x: --without-x. (line 6)
+* -C configure option: Build in parallel. (line 11)
+* -j make option: Build in parallel. (line 6)
* ac/withenable.ac: Adding a new program module.
- (line 42)
-* adapting TeX Live for distros: Distro builds. (line 54)
+ (line 42)
+* adapting TeX Live for distros: Distro builds. (line 54)
+* adding a new engine: Adding a new engine. (line 6)
* adding a new generic library: Adding a new generic library module.
- (line 6)
+ (line 6)
* adding a new program: Adding a new program module.
- (line 6)
+ (line 6)
* adding a new TeX-specific library: Adding a new TeX-specific library module.
- (line 6)
-* adding to TeX Live: Extending TeX Live. (line 6)
+ (line 6)
+* adding to TeX Live: Extending TeX Live. (line 6)
* am/ top-level directory: Top-level directories.
- (line 14)
+ (line 14)
* ANSI C: Declarations and definitions.
- (line 6)
+ (line 6)
* ApplicationServices Mac framework, required by xetex: Prerequisites.
- (line 31)
-* asymptote: Linked scripts. (line 23)
-* asymptote <1>: asymptote. (line 6)
+ (line 31)
+* asymptote: Linked scripts. (line 23)
+* asymptote <1>: asymptote. (line 6)
* Autoconf: Overview of build system.
- (line 6)
-* autoconf macros: Autoconf macros. (line 6)
+ (line 6)
+* autoconf macros: Autoconf macros. (line 6)
* Automake: Overview of build system.
- (line 6)
+ (line 6)
* autoreconf, for new program: Adding a new program module.
- (line 76)
-* biber: Linked scripts. (line 23)
+ (line 76)
+* biber: Linked scripts. (line 23)
* bibtex-x: Configure options for texk/bibtex-x.
- (line 6)
+ (line 6)
* bibtex8: Configure options for texk/bibtex-x.
- (line 6)
+ (line 6)
* bibtexu: Configure options for texk/bibtex-x.
- (line 6)
-* BSD distro: Distro builds. (line 6)
-* build directory, required: Building. (line 17)
-* build iteration: Build iteration. (line 6)
-* build on demand: Build one package. (line 6)
-* build one engine: Build one engine. (line 6)
-* build one package: Build one package. (line 6)
-* build problems: Build problems. (line 6)
-* Build script: Building. (line 6)
+ (line 6)
+* BSD distro: Distro builds. (line 6)
+* build directory, required: Building. (line 17)
+* build iteration: Build iteration. (line 6)
+* build on demand: Build one package. (line 6)
+* build one engine: Build one engine. (line 6)
+* build one package: Build one package. (line 6)
+* build problems: Build problems. (line 6)
+* Build script: Building. (line 6)
* build system, design of: Overview of build system.
- (line 6)
+ (line 6)
* build-aux/ top-level directory: Top-level directories.
- (line 30)
-* BUILDCC, BUILDCFLAGS, ...: Cross configuring. (line 42)
-* building: Building. (line 6)
-* building a distribution: Build distribution. (line 6)
-* building in parallel: Build in parallel. (line 6)
-* C++11, removing dependent sources: Build one package. (line 64)
-* C++11, required: Prerequisites. (line 15)
+ (line 30)
+* BUILDCC, BUILDCFLAGS, ...: Cross configuring. (line 42)
+* building: Building. (line 6)
+* building a distribution: Build distribution. (line 6)
+* building in parallel: Build in parallel. (line 6)
+* C++11, removing dependent sources: Build one package. (line 64)
+* C++11, required: Prerequisites. (line 15)
* C, ANSI, required: Declarations and definitions.
- (line 6)
+ (line 6)
* C99, avoided: Declarations and definitions.
- (line 6)
-* cache file, for configure: Build in parallel. (line 11)
-* cache for configure: Build in parallel. (line 6)
-* callexe.c: Macros for Windows. (line 32)
+ (line 6)
+* cache file, for configure: Build in parallel. (line 11)
+* cache for configure: Build in parallel. (line 6)
+* callexe.c: Macros for Windows. (line 32)
* CC: Variables for configure.
- (line 10)
-* CC=C-COMPILER: Build one package. (line 81)
-* CC_BUILD: Cross problems. (line 13)
+ (line 10)
+* CC=C-COMPILER: Build one package. (line 81)
+* CC_BUILD: Cross problems. (line 13)
* ChangeLog: Adding a new program module.
- (line 73)
+ (line 73)
* chktex: Declarations and definitions.
- (line 18)
+ (line 18)
* clisp: Variables for configure.
- (line 18)
+ (line 18)
* CLISP: Variables for configure.
- (line 17)
+ (line 17)
* CLISP <1>: Configure options for utils/xindy.
- (line 14)
-* clisp, required by xindy: Prerequisites. (line 35)
-* Cocoa Mac framework, required by xetex: Prerequisites. (line 31)
-* coding conventions: Coding conventions. (line 6)
-* compilers, C and C++11: Prerequisites. (line 6)
+ (line 14)
+* clisp, required by xindy: Prerequisites. (line 35)
+* Cocoa Mac framework, required by xetex: Prerequisites. (line 31)
+* coding conventions: Coding conventions. (line 6)
+* compilers, C and C++11: Prerequisites. (line 6)
* config.guess, config.sub, ...: Top-level directories.
- (line 30)
-* configure options: Configure options. (line 6)
+ (line 30)
+* configure options: Configure options. (line 6)
* configure options, for bibtex-x: Configure options for texk/bibtex-x.
- (line 6)
+ (line 6)
* configure options, for dvipdfm-x: Configure options for texk/dvipdfm-x.
- (line 6)
+ (line 6)
* configure options, for dvisvgm: Configure options for texk/dvisvgm.
- (line 6)
+ (line 6)
* configure options, for kpathsea: Configure options for kpathsea.
- (line 6)
+ (line 6)
* configure options, for texk/texlive: Configure options for texk/texlive.
- (line 6)
+ (line 6)
* configure options, for web2c: Configure options for texk/web2c.
- (line 6)
+ (line 6)
* configure options, for xdvik: Configure options for texk/xdvik.
- (line 6)
+ (line 6)
* configure options, for xindy: Configure options for utils/xindy.
- (line 6)
+ (line 6)
* configure options, global: Global configure options.
- (line 6)
+ (line 6)
* configure options, library-specific: Library-specific configure options.
- (line 6)
+ (line 6)
* configure options, program-specific: Program-specific configure options.
- (line 6)
+ (line 6)
* configure variables: Variables for configure.
- (line 6)
+ (line 6)
* configure.ac: Adding a new program module.
- (line 45)
-* configuring, for cross compilation: Cross configuring. (line 6)
-* const: Const. (line 6)
+ (line 45)
+* configuring, for cross compilation: Cross configuring. (line 6)
+* const: Const. (line 6)
* continuous integration: Continuous integration.
- (line 6)
-* conventions, coding: Coding conventions. (line 6)
+ (line 6)
+* conventions, coding: Coding conventions. (line 6)
* CPPFLAGS: Variables for configure.
- (line 12)
-* cross compilation: Cross compilation. (line 6)
-* cross compilation configuring: Cross configuring. (line 6)
-* cross compilation problems: Cross problems. (line 6)
-* cross compilation, with host binary: xdvik package. (line 14)
-* ctangle: Cross problems. (line 26)
+ (line 12)
+* cross compilation: Cross compilation. (line 6)
+* cross compilation configuring: Cross configuring. (line 6)
+* cross compilation problems: Cross problems. (line 6)
+* cross compilation, with host binary: xdvik package. (line 14)
+* ctangle: Cross problems. (line 26)
* CXX: Variables for configure.
- (line 11)
-* CXX=C++-COMPILER: Build one package. (line 81)
+ (line 11)
+* CXX=C++-COMPILER: Build one package. (line 81)
* declarations and definitions, in source code: Declarations and definitions.
- (line 6)
+ (line 6)
* declarations before statements, avoiding: Declarations and definitions.
- (line 6)
-* dependencies, with several output files: Build in parallel. (line 6)
+ (line 6)
+* dependencies, with several output files: Build in parallel. (line 6)
* DESTDIR: --prefix --bindir ....
- (line 9)
+ (line 9)
* directories, for installation: Installation directories.
- (line 6)
+ (line 6)
* directories, top-level: Top-level directories.
- (line 6)
-* discards qualifiers warning: Const. (line 30)
-* dist and distcheck targets for make: Build distribution. (line 6)
-* distribution tarball, making: Build distribution. (line 6)
-* distro, building for: Distro builds. (line 6)
+ (line 6)
+* discards qualifiers warning: Const. (line 30)
+* dist and distcheck targets for make: Build distribution. (line 6)
+* distribution tarball, making: Build distribution. (line 6)
+* distro, building for: Distro builds. (line 6)
* dvipdfm-x: Configure options for texk/dvipdfm-x.
- (line 6)
+ (line 6)
* dvipdfmx: Configure options for texk/dvipdfm-x.
- (line 6)
+ (line 6)
* dvisvgm: Configure options for texk/dvisvgm.
- (line 6)
-* engine, adding new: Adding a new program module.
- (line 104)
-* engine, building one: Build one engine. (line 6)
-* environment variables, for configure: Configure options. (line 16)
+ (line 6)
+* engine, adding new: Adding a new engine. (line 6)
+* engine, building one: Build one engine. (line 6)
+* environment variables, for configure: Configure options. (line 16)
* exec_prefix: --enable-multiplatform.
- (line 6)
-* extending TeX Live: Extending TeX Live. (line 6)
+ (line 6)
+* extending TeX Live: Extending TeX Live. (line 6)
* extern functions: Declarations and definitions.
- (line 41)
-* failure to build: Build problems. (line 6)
+ (line 41)
+* failure to build: Build problems. (line 6)
* flags, macros for library and header: Macros for library and header flags.
- (line 6)
-* fontconfig library, required by xetex: Prerequisites. (line 31)
-* FreeType: Prerequisites. (line 11)
-* freetype cross compiling: Cross problems. (line 13)
-* freetype library: freetype library. (line 6)
-* freetype-config: freetype library. (line 13)
+ (line 6)
+* fontconfig library, required by xetex: Prerequisites. (line 31)
+* FreeType: Prerequisites. (line 11)
+* freetype cross compiling: Cross problems. (line 13)
+* freetype library: freetype library. (line 6)
+* freetype-config: freetype library. (line 13)
* freetype-config <1>: Variables for configure.
- (line 24)
+ (line 24)
* FT2_CONFIG: Variables for configure.
- (line 21)
-* gcc, default compilers: Build one package. (line 81)
-* general setup macros: General setup macros.
- (line 6)
+ (line 21)
+* gcc, default compilers: Build one package. (line 81)
+* general setup macros: General setup macros. (line 6)
* generic library module, adding: Adding a new generic library module.
- (line 6)
+ (line 6)
* Ghostscript location for Xdvik: Configure options for texk/xdvik.
- (line 6)
+ (line 6)
* git-svn: Transfer from Subversion to Github.
- (line 6)
+ (line 6)
* global configure options: Global configure options.
- (line 6)
-* gmake, required: Prerequisites. (line 11)
-* GNU make, required: Prerequisites. (line 11)
-* GNU tools, needed for building: Build system tools. (line 6)
-* GNU/Linux distro: Distro builds. (line 6)
+ (line 6)
+* gmake, required: Prerequisites. (line 11)
+* GNU make, required: Prerequisites. (line 11)
+* GNU tools, needed for building: Build system tools. (line 6)
+* GNU/Linux distro: Distro builds. (line 6)
* Gnulib, used for common files: Top-level directories.
- (line 30)
-* ICU cross compiling: Cross problems. (line 20)
+ (line 30)
+* ICU cross compiling: Cross problems. (line 20)
* ICU libraries: Variables for configure.
- (line 24)
+ (line 24)
* icu-config: Variables for configure.
- (line 24)
+ (line 24)
* ICU_CONFIG: Variables for configure.
- (line 22)
-* infrastructure, tools needed for: Build system tools. (line 6)
+ (line 22)
+* infrastructure, tools needed for: Build system tools. (line 6)
* inst/ top-level directory: Top-level directories.
- (line 39)
-* install-tl, TeX Live installer: Installing. (line 8)
+ (line 39)
+* install-tl, TeX Live installer: Installing. (line 8)
* installation directories: Installation directories.
- (line 6)
-* installing: Installing. (line 6)
+ (line 6)
+* installing: Installing. (line 6)
* interprocess communication: Configure options for texk/web2c.
- (line 31)
-* introduction: Introduction. (line 6)
+ (line 31)
+* introduction: Introduction. (line 6)
* iteration through sources, by configure and make: Build iteration.
- (line 6)
-* kpathsea library: kpathsea library. (line 6)
-* kpathsea.ac: kpathsea library. (line 18)
-* kpse-libpng-flags.m4: png library. (line 45)
+ (line 6)
+* kpathsea library: kpathsea library. (line 6)
+* kpathsea.ac: kpathsea library. (line 18)
+* kpse-libpng-flags.m4: png library. (line 45)
* kpse-pkgs.m4: Overview of build system.
- (line 30)
-* kpse-zlib-flags.m4: zlib library. (line 6)
+ (line 30)
+* kpse-zlib-flags.m4: zlib library. (line 6)
* kpsewhich: Variables for configure.
- (line 30)
+ (line 30)
* KPSEWHICH: Variables for configure.
- (line 29)
+ (line 29)
* KPSE_ADD_FLAGS: Macros for library and header flags.
- (line 20)
+ (line 20)
* KPSE_ALL_SYSTEM_FLAGS: Adding a new generic library module.
- (line 34)
-* KPSE_BASIC: General setup macros.
- (line 8)
-* KPSE_CHECK_LATEX: Macros for programs. (line 8)
-* KPSE_CHECK_PDFLATEX: Macros for programs. (line 15)
-* KPSE_CHECK_PERL: Macros for programs. (line 18)
-* KPSE_CHECK_WIN32: Macros for Windows. (line 9)
-* KPSE_COMMON: General setup macros.
- (line 16)
-* KPSE_COMPILER_VISIBILITY: Macros for compilers.
- (line 18)
-* KPSE_COMPILER_WARNINGS: Macros for compilers.
- (line 8)
-* KPSE_COND_MINGW32: Macros for Windows. (line 19)
-* KPSE_COND_WIN32: Macros for Windows. (line 15)
-* KPSE_COND_WIN32_WRAP: Macros for Windows. (line 24)
-* kpse_cv_cxx_hack: Macros for compilers.
- (line 33)
-* kpse_cv_have_win32: Macros for Windows. (line 10)
-* kpse_cv_visibility_c[xx]flags: Macros for compilers.
- (line 25)
-* kpse_cv_warning_cflags: Macros for compilers.
- (line 15)
-* KPSE_CXX_HACK: Macros for compilers.
- (line 28)
+ (line 34)
+* KPSE_BASIC: General setup macros. (line 8)
+* KPSE_CHECK_LATEX: Macros for programs. (line 8)
+* KPSE_CHECK_PDFLATEX: Macros for programs. (line 15)
+* KPSE_CHECK_PERL: Macros for programs. (line 18)
+* KPSE_CHECK_WIN32: Macros for Windows. (line 9)
+* KPSE_COMMON: General setup macros. (line 16)
+* KPSE_COMPILER_VISIBILITY: Macros for compilers. (line 18)
+* KPSE_COMPILER_WARNINGS: Macros for compilers. (line 8)
+* KPSE_COND_MINGW32: Macros for Windows. (line 19)
+* KPSE_COND_WIN32: Macros for Windows. (line 15)
+* KPSE_COND_WIN32_WRAP: Macros for Windows. (line 24)
+* kpse_cv_cxx_hack: Macros for compilers. (line 33)
+* kpse_cv_have_win32: Macros for Windows. (line 10)
+* kpse_cv_visibility_c[xx]flags: Macros for compilers. (line 25)
+* kpse_cv_warning_cflags: Macros for compilers. (line 15)
+* KPSE_CXX_HACK: Macros for compilers. (line 28)
* KPSE_ENABLE_PROG: Adding a new program module.
- (line 14)
-* KPSE_LARGEFILE: Macros for libraries.
- (line 8)
+ (line 14)
+* KPSE_LARGEFILE: Macros for libraries. (line 8)
* KPSE_LIBPNG_FLAGS: Macros for library and header flags.
- (line 10)
-* KPSE_LIBPNG_FLAGS <1>: png library. (line 45)
+ (line 10)
+* KPSE_LIBPNG_FLAGS <1>: png library. (line 45)
* kpse_libs_pkgs: Adding a new generic library module.
- (line 6)
+ (line 6)
* KPSE_LIB_FLAGS: Macros for library and header flags.
- (line 6)
+ (line 6)
* KPSE_LIB_FLAGS <1>: Adding a new generic library module.
- (line 26)
+ (line 26)
* KPSE_LIB_SYSTEM_FLAGS: Adding a new generic library module.
- (line 34)
-* KPSE_PROG_LEX: Macros for programs. (line 21)
+ (line 34)
+* KPSE_PROG_LEX: Macros for programs. (line 21)
* KPSE_RESTORE_FLAGS: Macros for library and header flags.
- (line 24)
+ (line 24)
* kpse_texk_pkgs: Adding a new program module.
- (line 6)
+ (line 6)
* kpse_texlibs_pkgs: Adding a new TeX-specific library module.
- (line 11)
-* KPSE_TRY_LIB: png library. (line 17)
+ (line 11)
+* KPSE_TRY_LIB: png library. (line 17)
* KPSE_TRY_LIB <1>: Adding a new generic library module.
- (line 20)
-* KPSE_TRY_LIBXX: png library. (line 30)
+ (line 20)
+* KPSE_TRY_LIBXX: png library. (line 30)
* KPSE_TRY_LIBXX <1>: Adding a new generic library module.
- (line 20)
+ (line 20)
* kpse_utils_pkgs: Adding a new program module.
- (line 10)
-* KPSE_WIN32_CALL: Macros for Windows. (line 31)
+ (line 10)
+* KPSE_WIN32_CALL: Macros for Windows. (line 31)
* KPSE_WITH_LIB: Adding a new generic library module.
- (line 11)
+ (line 11)
* KPSE_WITH_TEXLIB: Adding a new TeX-specific library module.
- (line 14)
-* large file support: --disable-largefile. (line 6)
+ (line 14)
+* large file support: --disable-largefile. (line 6)
* LATEX: Variables for configure.
- (line 40)
+ (line 40)
* layout of sources: Layout and infrastructure.
- (line 6)
-* LFS (large file support): --disable-largefile. (line 6)
+ (line 6)
+* LFS (large file support): --disable-largefile. (line 6)
* libexpat, dependency of libfontconfig: Configure options for texk/web2c.
- (line 50)
+ (line 50)
* libfontconfig, hack for avoiding linking dependencies: Configure options for texk/web2c.
- (line 50)
+ (line 50)
* libfreetype: Variables for configure.
- (line 24)
-* libfreetype, and const: Const. (line 21)
-* libpng library: png library. (line 6)
+ (line 24)
+* libfreetype, and const: Const. (line 21)
+* libpng library: png library. (line 6)
* library module, generic, adding: Adding a new generic library module.
- (line 6)
+ (line 6)
* library module, TeX-specific, adding: Adding a new TeX-specific library module.
- (line 6)
-* library modules: Library modules. (line 6)
+ (line 6)
+* library modules: Library modules. (line 6)
* library-specific configure options: Library-specific configure options.
- (line 6)
-* libsigsegv, required by xindy: Prerequisites. (line 35)
-* libstc++, statically linking: Macros for compilers.
- (line 29)
+ (line 6)
+* libsigsegv, required by xindy: Prerequisites. (line 35)
+* libstc++, statically linking: Macros for compilers. (line 29)
* Libtool: Overview of build system.
- (line 6)
+ (line 6)
* libtool, hack for avoiding excessive linking: Configure options for texk/web2c.
- (line 50)
+ (line 50)
* libXt: Configure options for texk/web2c.
- (line 22)
-* linked scripts: Linked scripts. (line 6)
-* linking C++ libraries statically: Macros for compilers.
- (line 29)
+ (line 22)
+* linked scripts: Linked scripts. (line 6)
+* linking C++ libraries statically: Macros for compilers. (line 29)
* lisp.run, lisp.exe: Configure options for utils/xindy.
- (line 14)
+ (line 14)
* LittleEndian architectures: Configure options for texk/web2c.
- (line 27)
+ (line 27)
* m4/ top-level directory: Top-level directories.
- (line 14)
-* macros, for compilers: Macros for compilers.
- (line 6)
-* macros, for libraries: Macros for libraries.
- (line 6)
+ (line 14)
+* macros, for compilers: Macros for compilers. (line 6)
+* macros, for libraries: Macros for libraries. (line 6)
* macros, for library and header flags: Macros for library and header flags.
- (line 6)
-* macros, for programs: Macros for programs. (line 6)
-* macros, for Windows: Macros for Windows. (line 6)
-* macros, general setup: General setup macros.
- (line 6)
+ (line 6)
+* macros, for programs: Macros for programs. (line 6)
+* macros, for Windows: Macros for Windows. (line 6)
+* macros, general setup: General setup macros. (line 6)
* MAKE: Variables for configure.
- (line 33)
-* make -t: Build system tools. (line 43)
+ (line 33)
+* make -t: Build system tools. (line 43)
* make rules, verbose vs. silent: --enable-silent-rules.
- (line 6)
+ (line 6)
* Makefile.am: Adding a new program module.
- (line 46)
+ (line 46)
* mf-nowin: Configure options for texk/web2c.
- (line 34)
-* mingw32: Cross configuring. (line 27)
-* MINGW32, Automake conditional: Macros for Windows. (line 20)
-* mktex.ac: kpathsea library. (line 18)
-* mktextfm: kpathsea library. (line 18)
-* modules, for libraries: Library modules. (line 6)
-* modules, for programs: Program modules. (line 6)
+ (line 34)
+* mingw32: Cross configuring. (line 27)
+* MINGW32, Automake conditional: Macros for Windows. (line 20)
+* mktex.ac: kpathsea library. (line 18)
+* mktextfm: kpathsea library. (line 18)
+* modules, for libraries: Library modules. (line 6)
+* modules, for programs: Program modules. (line 6)
* motif: Configure options for texk/xdvik.
- (line 9)
-* native cross compilation: Cross compilation. (line 10)
+ (line 9)
+* native cross compilation: Cross compilation. (line 10)
* NEWPROG-SRC, original source subdirectory: Adding a new program module.
- (line 33)
-* OBJCXX=OBJC-COMPILER: Build one package. (line 81)
-* one engine, building: Build one engine. (line 6)
-* one package, building: Build one package. (line 6)
-* OpenGL, required for Asymptote: asymptote. (line 6)
-* operating system distribution, building for: Distro builds. (line 6)
-* otangle: Cross problems. (line 26)
-* overall build process: Building. (line 6)
-* parallel build: Build in parallel. (line 6)
+ (line 33)
+* OBJCXX=OBJC-COMPILER: Build one package. (line 81)
+* one engine, building: Build one engine. (line 6)
+* one package, building: Build one package. (line 6)
+* OpenGL, required for Asymptote: asymptote. (line 6)
+* operating system distribution, building for: Distro builds. (line 6)
+* otangle: Cross problems. (line 26)
+* overall build process: Building. (line 6)
+* parallel build: Build in parallel. (line 6)
* paths, for installation: Installation directories.
- (line 6)
-* PDF files, size of: --disable-largefile. (line 11)
+ (line 6)
+* PDF files, size of: --disable-largefile. (line 11)
* PDFLATEX: Variables for configure.
- (line 41)
+ (line 41)
* PERL: Variables for configure.
- (line 39)
-* perl, required by web2c, etc.: Prerequisites. (line 23)
+ (line 39)
+* perl, required by web2c, etc.: Prerequisites. (line 23)
* PKG_CONFIG: Variables for configure.
- (line 23)
-* plain.tex, not in source tree: Installing. (line 8)
-* png library: png library. (line 6)
-* PostScript files, size of: --disable-largefile. (line 11)
-* Preining, Norbert: Distro builds. (line 54)
+ (line 23)
+* plain.tex, not in source tree: Installing. (line 8)
+* png library: png library. (line 6)
+* PostScript files, size of: --disable-largefile. (line 11)
+* Preining, Norbert: Distro builds. (line 54)
* preloaded binaries: Configure options for texk/web2c.
- (line 45)
-* prerequisites for building: Prerequisites. (line 6)
-* problems with build: Build problems. (line 6)
+ (line 45)
+* prerequisites for building: Prerequisites. (line 6)
+* problems with build: Build problems. (line 6)
* program module, adding: Adding a new program module.
- (line 6)
-* program modules: Program modules. (line 6)
+ (line 6)
+* program modules: Program modules. (line 6)
* program-specific configure options: Program-specific configure options.
- (line 6)
-* proxy build system: png library. (line 35)
-* reautoconf: Build system tools. (line 28)
+ (line 6)
+* proxy build system: png library. (line 35)
+* reautoconf: Build system tools. (line 28)
* reautoconf, for new program: Adding a new program module.
- (line 89)
-* requirements for building: Prerequisites. (line 6)
-* runscript.exe: Macros for Windows. (line 25)
-* scripts, linked and not maintained: Linked scripts. (line 6)
+ (line 89)
+* requirements for building: Prerequisites. (line 6)
+* runscript.exe: Macros for Windows. (line 25)
+* scripts, linked and not maintained: Linked scripts. (line 6)
* scrolling, smooth: Configure options for texk/xdvik.
- (line 13)
+ (line 13)
* SED: Variables for configure.
- (line 34)
-* setup macros, general: General setup macros.
- (line 6)
-* shared libraries, using vs. avoiding: Distro builds. (line 11)
-* size of PDF and PS files: --disable-largefile. (line 11)
-* size of source tree: Build one package. (line 53)
+ (line 34)
+* setup macros, general: General setup macros. (line 6)
+* shared libraries, using vs. avoiding: Distro builds. (line 11)
+* size of PDF and PS files: --disable-largefile. (line 11)
+* size of source tree: Build one package. (line 53)
* source code declarations: Declarations and definitions.
- (line 6)
-* source directory building, not supported: Building. (line 17)
+ (line 6)
+* source directory building, not supported: Building. (line 17)
* source tree: Layout and infrastructure.
- (line 6)
-* squeeze: Cross problems. (line 13)
-* squeeze/configure.ac: xdvik package. (line 14)
+ (line 6)
+* squeeze: Cross problems. (line 13)
+* squeeze/configure.ac: xdvik package. (line 14)
* static functions: Declarations and definitions.
- (line 35)
-* static linking for C++: Macros for compilers.
- (line 29)
+ (line 35)
+* static linking for C++: Macros for compilers. (line 29)
* stpcpy: Declarations and definitions.
- (line 18)
-* Subversion repository: Build system tools. (line 38)
-* support files, separate from build: Installing. (line 8)
-* symlinks, used for scripts: Linked scripts. (line 6)
+ (line 18)
+* Subversion repository: Build system tools. (line 38)
+* support files, separate from build: Installing. (line 8)
+* symlinks, used for scripts: Linked scripts. (line 6)
* synctex: Configure options for texk/web2c.
- (line 59)
+ (line 59)
* synctex <1>: Configure options for texk/web2c.
- (line 64)
-* system distribution, building for: Distro builds. (line 6)
-* t1utils package: t1utils package. (line 6)
-* tangle: Cross problems. (line 26)
+ (line 64)
+* system distribution, building for: Distro builds. (line 6)
+* t1utils package: t1utils package. (line 6)
+* tangle: Cross problems. (line 26)
* tests, running: Overview of build system.
- (line 6)
+ (line 6)
* TeX-specific library module, adding: Adding a new TeX-specific library module.
- (line 6)
-* texlive.tlpdb, TeX Live database: Installing. (line 8)
-* tie: Cross problems. (line 26)
-* timestamps, in repository: Build system tools. (line 38)
+ (line 6)
+* texlive.tlpdb, TeX Live database: Installing. (line 8)
+* tie: Cross problems. (line 26)
+* timestamps, in repository: Build system tools. (line 38)
* TLpatches/patch-...: Adding a new program module.
- (line 68)
+ (line 68)
* TLpatches/TL-Changes: Adding a new program module.
- (line 64)
-* tools, for building: Build system tools. (line 6)
+ (line 64)
+* tools, for building: Build system tools. (line 6)
* top-level directories: Top-level directories.
- (line 6)
-* touching files to avoid rerunning: Build system tools. (line 43)
+ (line 6)
+* touching files to avoid rerunning: Build system tools. (line 43)
* Travis-CI: Continuous integration.
- (line 6)
+ (line 6)
* travis.yml: CI testing on Travis-CI.
- (line 6)
-* type cast from const, avoiding: Const. (line 38)
-* use-commit-times, Subversion: Build system tools. (line 38)
+ (line 6)
+* type cast from const, avoiding: Const. (line 38)
+* use-commit-times, Subversion: Build system tools. (line 38)
* variable declarations, in source code: Declarations and definitions.
- (line 52)
+ (line 52)
* variables for configure: Variables for configure.
- (line 6)
-* warning, discards qualifiers: Const. (line 30)
-* WARNING_C[XX]FLAGS: Macros for compilers.
- (line 9)
-* web2c program: Cross problems. (line 13)
+ (line 6)
+* warning, discards qualifiers: Const. (line 30)
+* WARNING_C[XX]FLAGS: Macros for compilers. (line 9)
+* web2c program: Cross problems. (line 13)
* web2c.ac: Configure options for texk/web2c.
- (line 37)
-* wget: Linked scripts. (line 23)
-* WIN32, Automake conditional: Macros for Windows. (line 16)
-* WIN32_WRAP, Automake conditional: Macros for Windows. (line 25)
-* Windows, invoking scripts on: Linked scripts. (line 6)
-* Windows, macros for: Macros for Windows. (line 6)
+ (line 37)
+* wget: Linked scripts. (line 23)
+* WIN32, Automake conditional: Macros for Windows. (line 16)
+* WIN32_WRAP, Automake conditional: Macros for Windows. (line 25)
+* Windows, invoking scripts on: Linked scripts. (line 6)
+* Windows, macros for: Macros for Windows. (line 6)
* withenable.ac, for new modules: Adding a new program module.
- (line 14)
+ (line 14)
* Work/ top-level directory: Top-level directories.
- (line 39)
-* wrapper binary for scripts on Windows: Linked scripts. (line 6)
-* wrapper build system: freetype library. (line 6)
+ (line 39)
+* wrapper binary for scripts on Windows: Linked scripts. (line 6)
+* wrapper build system: freetype library. (line 6)
* X toolkit: Configure options for texk/web2c.
- (line 22)
-* X11 development, required by X clients: Prerequisites. (line 27)
-* X11 headers, and const: Const. (line 21)
-* xasy: asymptote. (line 6)
+ (line 22)
+* X11 development, required by X clients: Prerequisites. (line 27)
+* X11 headers, and const: Const. (line 21)
+* xasy: asymptote. (line 6)
* xaw: Configure options for texk/xdvik.
- (line 9)
-* xdvik: xdvik package. (line 6)
+ (line 9)
+* xdvik: xdvik package. (line 6)
* xdvik <1>: Configure options for texk/xdvik.
- (line 6)
+ (line 6)
* xdvipdfmx: Configure options for texk/dvipdfm-x.
- (line 6)
-* xindy: Linked scripts. (line 23)
-* xindy <1>: xindy package. (line 6)
+ (line 6)
+* xindy: Linked scripts. (line 23)
+* xindy <1>: xindy package. (line 6)
* xindy <2>: Configure options for utils/xindy.
- (line 6)
-* xindy cross compiling requires clisp: Cross problems. (line 33)
+ (line 6)
+* xindy cross compiling requires clisp: Cross problems. (line 33)
* XInput: Configure options for texk/xdvik.
- (line 13)
+ (line 13)
* Xlib: Configure options for texk/web2c.
- (line 22)
-* xz: Linked scripts. (line 23)
-* zlib library: zlib library. (line 6)
+ (line 22)
+* xz: Linked scripts. (line 23)
+* zlib library: zlib library. (line 6)

@@ -5982,363 +5988,364 @@ Node: xindy package48957
Node: xdvik package50073
Node: asymptote51132
Node: Extending TeX Live51628
-Node: Adding a new program module52411
-Node: Adding a new generic library module57544
-Node: Adding a new TeX-specific library module59773
-Node: Configure options60471
-Node: Global configure options61852
-Node: --disable-native-texlive-build62394
-Node: --prefix --bindir ...63356
-Node: --disable-largefile63896
-Node: --disable-missing64438
-Node: --enable-compiler-warnings=LEVEL64839
-Node: --enable-cxx-runtime-hack65495
-Node: --enable-maintainer-mode65915
-Node: --enable-multiplatform66444
-Node: --enable-shared67017
-Node: --enable-silent-rules67388
-Node: --without-ln-s67840
-Node: --without-x68187
-Node: Program-specific configure options68375
-Node: --enable-PROG --disable-PROG69018
-Node: --disable-all-pkgs69291
-Node: Configure options for texk/web2c70074
-Node: Configure options for texk/bibtex-x72612
-Node: Configure options for texk/dvipdfm-x73155
-Node: Configure options for texk/dvisvgm73931
-Node: Configure options for texk/texlive74812
-Node: Configure options for texk/xdvik75233
-Node: Configure options for utils/xindy75854
-Node: Library-specific configure options76744
-Node: Configure options for kpathsea77705
-Node: Variables for configure78409
-Node: Coding conventions79835
-Node: Declarations and definitions80550
-Node: Const82724
-Node: Continuous integration84588
-Node: Transfer from Subversion to Github85252
-Node: Automatic update of the Git mirror87414
-Node: CI testing on Travis-CI87996
-Node: Releases on Github88705
-Node: install-tl89145
-Node: install-tl NAME89514
-Node: install-tl SYNOPSIS89672
-Node: install-tl DESCRIPTION89930
-Node: install-tl REFERENCES90990
-Node: install-tl OPTIONS91514
-Ref: install-tl *-gui* [[=]_module_]91855
-Ref: install-tl text92063
-Ref: install-tl tcl (or "perltk" or "wizard" or "expert" or nothing)92248
-Ref: install-tl *-no-gui*92880
-Ref: install-tl *-lang* _llcode_92970
-Ref: install-tl *-repository* _url|path_93594
-Ref: install-tl *-select-repository*94483
-Ref: install-tl *-all-options*94901
-Ref: install-tl *-custom-bin* _path_95156
-Ref: install-tl *-debug-translation*95990
-Ref: install-tl *-force-platform* _platform_96266
-Ref: install-tl *-help*, *--help*, *-?*96510
-Ref: install-tl *-in-place*96923
-Ref: install-tl *-init-from-profile* _profile_file_97468
-Ref: install-tl *-logfile* _file_97737
-Ref: install-tl *-no-cls*98088
-Ref: install-tl *-no-persistent-downloads*98236
-Ref: install-tl *-persistent-downloads*98261
-Ref: install-tl *-no-verify-downloads*98879
-Ref: install-tl *-non-admin*99242
-Ref: install-tl *-portable*99335
-Ref: install-tl *-print-platform*99474
-Ref: install-tl *-profile* _profile_file_99672
-Ref: install-tl *-q*99893
-Ref: install-tl *-scheme* _scheme_99955
-Ref: install-tl *-v*100429
-Ref: install-tl *-version*, *--version*100584
-Node: install-tl PROFILES100718
-Ref: install-tl instopt_adjustpath (default 0 on Unix, 1 on Windows)103584
-Ref: install-tl instopt_adjustrepo (default 1)103660
-Ref: install-tl instopt_letter (default 0)103797
-Ref: install-tl instopt_portable (default 0)103888
-Ref: install-tl instopt_write18_restricted (default 1)103984
-Node: install-tl ENVIRONMENT VARIABLES105323
-Ref: install-tl TEXLIVE_DOWNLOADER105701
-Ref: install-tl TL_DOWNLOAD_PROGRAM105724
-Ref: install-tl TL_DOWNLOAD_ARGS105744
-Ref: install-tl TEXLIVE_INSTALL_ENV_NOCHECK105948
-Ref: install-tl TEXLIVE_INSTALL_NO_CONTEXT_CACHE106150
-Ref: install-tl TEXLIVE_INSTALL_NO_RESUME106259
-Ref: install-tl TEXLIVE_INSTALL_NO_WELCOME106411
-Ref: install-tl TEXLIVE_INSTALL_PAPER106532
-Ref: install-tl TEXLIVE_INSTALL_PREFIX106678
-Ref: install-tl TEXLIVE_INSTALL_TEXDIR106704
-Ref: install-tl TEXLIVE_INSTALL_TEXMFCONFIG106735
-Ref: install-tl TEXLIVE_INSTALL_TEXMFVAR106763
-Ref: install-tl TEXLIVE_INSTALL_TEXMFHOME106792
-Ref: install-tl TEXLIVE_INSTALL_TEXMFLOCAL106822
-Ref: install-tl TEXLIVE_INSTALL_TEXMFSYSCONFIG106856
-Ref: install-tl TEXLIVE_INSTALL_TEXMFSYSVAR106887
-Ref: install-tl NOPERLDOC107258
-Node: install-tl AUTHORS AND COPYRIGHT107322
-Node: tlmgr107735
-Node: tlmgr NAME108220
-Node: tlmgr SYNOPSIS108352
-Node: tlmgr DESCRIPTION108542
-Node: tlmgr EXAMPLES109641
-Ref: tlmgr tlmgr option repository ctan109892
-Ref: tlmgr tlmgr option repository https://mirror.ctan.org/systems/texlive/tlnet109965
-Ref: tlmgr tlmgr update --list110570
-Ref: tlmgr tlmgr update --all110663
-Ref: tlmgr tlmgr info _what_110820
-Node: tlmgr OPTIONS111082
-Ref: tlmgr *--repository* _url|path_111602
-Ref: tlmgr /some/local/dir112788
-Ref: tlmgr file:/some/local/dir112817
-Ref: tlmgr ctan112890
-Ref: tlmgr https://mirror.ctan.org/systems/texlive/tlnet112944
-Ref: tlmgr http://server/path/to/tlnet113285
-Ref: tlmgr https://server/path/to/tlnet113666
-Ref: tlmgr ftp://server/path/to/tlnet114134
-Ref: tlmgr user@machine:/path/to/tlnet114266
-Ref: tlmgr scp://user@machine/path/to/tlnet114307
-Ref: tlmgr ssh://user@machine/path/to/tlnet114348
-Ref: tlmgr *--gui* [_action_]114741
-Ref: tlmgr *--gui-lang* _llcode_115554
-Ref: tlmgr *--command-logfile* _file_116295
-Ref: tlmgr *--debug-translation*116561
-Ref: tlmgr *--machine-readable*116764
-Ref: tlmgr *--no-execute-actions*117032
-Ref: tlmgr *--package-logfile* _file_117225
-Ref: tlmgr *--pause*117479
-Ref: tlmgr *--persistent-downloads*117634
-Ref: tlmgr *--no-persistent-downloads*117662
-Ref: tlmgr *--pin-file*118156
-Ref: tlmgr *--usermode*118374
-Ref: tlmgr *--usertree* _dir_118494
-Ref: tlmgr *--verify-repo=[none|main|all]*118620
-Node: tlmgr ACTIONS119519
-Node: tlmgr help120380
-Node: tlmgr version120857
-Node: tlmgr backup121120
-Ref: tlmgr *backup [_option_...] --all*121291
-Ref: tlmgr *backup [_option_...] _pkg_...*121324
-Ref: tlmgr *--backupdir* _directory_122390
-Ref: tlmgr *--all*122607
-Ref: tlmgr *--clean*[=_N_]122859
-Ref: tlmgr *--dry-run*123186
-Node: tlmgr candidates _pkg_123316
-Node: tlmgr check [_option_...] [depends|executes|files|runfiles|texmfdbs|all]123671
-Ref: tlmgr *depends*124185
-Ref: tlmgr *executes*124527
-Ref: tlmgr *files*124642
-Ref: tlmgr *runfiles*124778
-Ref: tlmgr *texmfdbs*124915
-Ref: tlmgr - all items in TEXMFDBS have the !! prefix.125145
-Ref: tlmgr - all items in TEXMFBDS have an ls-R file (if they exist at all).125221
-Ref: tlmgr - all items in TEXMF with !! are listed in TEXMFDBS.125286
-Ref: tlmgr - all items in TEXMF with an ls-R file are listed in TEXMFDBS.125361
-Ref: tlmgr *--use-svn*125386
-Node: tlmgr conf125527
-Ref: tlmgr *conf [texmf|tlmgr|updmap [--conffile _file_] [--delete] [_key_ [_value_]]]*125815
-Ref: tlmgr *conf auxtrees [--conffile _file_] [show|add|remove] [_value_]*125879
-Node: tlmgr dump-tlpdb [_option_...] [--json]128294
-Ref: tlmgr *--local*128727
-Ref: tlmgr *--remote*128766
-Ref: tlmgr *--json*128804
-Node: tlmgr generate129375
-Ref: tlmgr *generate [_option_...] language*129571
-Ref: tlmgr *generate [_option_...] language.dat*129610
-Ref: tlmgr *generate [_option_...] language.def*129649
-Ref: tlmgr *generate [_option_...] language.dat.lua*129692
-Ref: tlmgr *--dest* _output_file_132019
-Ref: tlmgr *--localcfg* _local_conf_file_132595
-Ref: tlmgr *--rebuild-sys*132718
-Node: tlmgr gui133533
-Node: tlmgr info133711
-Ref: tlmgr *info [_option_...] _pkg_...*133873
-Ref: tlmgr *info [_option_...] collections*133907
-Ref: tlmgr *info [_option_...] schemes*133937
-Ref: tlmgr *--list*135467
-Ref: tlmgr *--only-installed*135781
-Ref: tlmgr *--only-remote*135969
-Ref: tlmgr *--data item1,item2,...*136273
-Ref: tlmgr *--json* 1137636
-Node: tlmgr init-usertree138019
-Node: tlmgr install [_option_...] _pkg_...138400
-Ref: tlmgr *--dry-run* 1138908
-Ref: tlmgr *--file*139025
-Ref: tlmgr *--force*139247
-Ref: tlmgr *--no-depends*139467
-Ref: tlmgr *--no-depends-at-all*139626
-Ref: tlmgr *--reinstall*140026
-Ref: tlmgr *--with-doc*140404
-Ref: tlmgr *--with-src*140417
-Node: tlmgr key141145
-Ref: tlmgr *key list*141303
-Ref: tlmgr *key add _file_*141321
-Ref: tlmgr *key remove _keyid_*141343
-Node: tlmgr list141937
-Node: tlmgr option142099
-Ref: tlmgr *option [--json] [show]*142254
-Ref: tlmgr *option [--json] showall|help*142285
-Ref: tlmgr *option _key_ [_value_]*142311
-Node: tlmgr paper146890
-Ref: tlmgr *paper [a4|letter]*147039
-Ref: tlmgr *<[xdvi|pdftex|dvips|dvipdfmx|context|psutils] paper [_papersize_|--list]*>147115
-Ref: tlmgr *paper --json*147130
-Node: tlmgr path148345
-Ref: tlmgr *path [--w32mode=user|admin] add*148506
-Ref: tlmgr *path [--w32mode=user|admin] remove*148543
-Node: tlmgr pinning150028
-Ref: tlmgr pinning show150269
-Ref: tlmgr pinning add _repo_ _pkgglob_...150342
-Ref: tlmgr pinning remove _repo_ _pkgglob_...150461
-Ref: tlmgr pinning remove _repo_ --all150614
-Node: tlmgr platform150668
-Ref: tlmgr *platform list|add|remove _platform_...*150854
-Ref: tlmgr *platform set _platform_*150881
-Ref: tlmgr *platform set auto*150902
-Ref: tlmgr *--dry-run* 2151779
-Node: tlmgr postaction151898
-Ref: tlmgr *postaction [_option_...] install [shortcut|fileassoc|script] [_pkg_...]*152128
-Ref: tlmgr *postaction [_option_...] remove [shortcut|fileassoc|script] [_pkg_...]*152202
-Ref: tlmgr *--w32mode=[user|admin]*152517
-Ref: tlmgr *--fileassocmode=[1|2]*152933
-Ref: tlmgr *--all* 1153218
-Node: tlmgr print-platform153273
-Node: tlmgr print-platform-info153604
-Node: tlmgr remove [_option_...] _pkg_...153904
-Ref: tlmgr *--all* 2154388
-Ref: tlmgr *--backup*154498
-Ref: tlmgr *--backupdir* _directory_ 1154524
-Ref: tlmgr *--no-depends* 1154929
-Ref: tlmgr *--no-depends-at-all* 1154991
-Ref: tlmgr *--force* 1155094
-Ref: tlmgr *--dry-run* 3155567
-Node: tlmgr repository155903
-Ref: tlmgr *repository list*156091
-Ref: tlmgr *repository list _path|url|tag_*156125
-Ref: tlmgr *repository add _path_ [_tag_]*156158
-Ref: tlmgr *repository remove _path|tag_*156190
-Ref: tlmgr *repository set _path_[#_tag_] [_path_[#_tag_] ...]*156244
-Ref: tlmgr *repository status*156265
-Ref: tlmgr The tag (which can be the same as the url);157492
-Node: tlmgr restore157970
-Ref: tlmgr *restore [_option_...] _pkg_ [_rev_]*158149
-Ref: tlmgr *restore [_option_...] --all*158179
-Ref: tlmgr *--all* 3158879
-Ref: tlmgr *--backupdir* _directory_ 2159093
-Ref: tlmgr *--dry-run* 4159274
-Ref: tlmgr *--force* 2159406
-Ref: tlmgr *--json* 2159452
-Node: tlmgr search159779
-Ref: tlmgr *search [_option_...] _what_*159943
-Ref: tlmgr *search [_option_...] --file _what_*159980
-Ref: tlmgr *search [_option_...] --all _what_*160016
-Ref: tlmgr *--file* 1160236
-Ref: tlmgr *--all* 4160298
-Ref: tlmgr *--global*160387
-Ref: tlmgr *--word*160514
-Node: tlmgr shell160829
-Ref: tlmgr protocol161564
-Ref: tlmgr help 1161628
-Ref: tlmgr version 1161681
-Ref: tlmgr quit, end, bye, byebye, EOF161749
-Ref: tlmgr restart161770
-Ref: tlmgr load [local|remote]161893
-Ref: tlmgr save161963
-Ref: tlmgr get [_var_] =item set [_var_ [_val_]]162086
-Node: tlmgr show162687
-Node: tlmgr uninstall162854
-Node: tlmgr update [_option_...] [_pkg_...]163084
-Ref: tlmgr *--all* 5163455
-Ref: tlmgr *--self*165634
-Ref: tlmgr *--dry-run* 5166398
-Ref: tlmgr *--list* [_pkg_]166575
-Ref: tlmgr *--exclude* _pkg_167264
-Ref: tlmgr *--no-auto-remove* [_pkg_...]168064
-Ref: tlmgr *--no-auto-install* [_pkg_...]168548
-Ref: tlmgr *--reinstall-forcibly-removed*169310
-Ref: tlmgr *--backup* 1169845
-Ref: tlmgr *--backupdir* _directory_ 3169871
-Ref: tlmgr *--no-depends* 2171037
-Ref: tlmgr *--no-depends-at-all* 2171240
-Ref: tlmgr *--force* 3171343
-Node: tlmgr CONFIGURATION FILE FOR TLMGR172334
-Ref: tlmgr auto-remove, value 0 or 1 (default 1), same as command-line option.173341
-Ref: tlmgr gui-expertmode, value 0 or 1 (default 1). This switches between the full GUI and a simplified GUI with only the most common settings.173478
-Ref: tlmgr gui-lang _llcode_, with a language code value as with the command-line option.173560
-Ref: tlmgr no-checksums, value 0 or 1 (default 0, see below).173614
-Ref: tlmgr persistent-downloads, value 0 or 1 (default 1), same as command-line option.173694
-Ref: tlmgr require-verification, value 0 or 1 (default 0), same as command-line option.173774
-Ref: tlmgr tkfontscale, value any float. Controls the scaling of fonts in the Tk based frontends.173864
-Ref: tlmgr update-exclude, value: comma-separated list of packages (no space allowed). Same as the command line option --exclude for the action update.174012
-Ref: tlmgr verify-downloads, value 0 or 1 (default 1), same as command-line option.174088
-Ref: tlmgr allowed-actions _action1_ [,_action_,...] The value is a comma-separated list of tlmgr actions which are allowed to be executed when tlmgr is invoked in system mode (that is, without --usermode).174357
-Node: tlmgr CRYPTOGRAPHIC VERIFICATION175445
-Node: tlmgr Configuration of GnuPG invocation177618
-Node: tlmgr USER MODE178256
-Node: tlmgr User mode install181102
-Node: tlmgr User mode backup, restore, remove, update182246
-Node: tlmgr User mode generate, option, paper182688
-Node: tlmgr MULTIPLE REPOSITORIES183064
-Node: tlmgr Pinning184793
-Node: tlmgr GUI FOR TLMGR186716
-Node: tlmgr Main display188365
-Node: tlmgr Display configuration area188617
-Ref: tlmgr Status188978
-Ref: tlmgr Category189142
-Ref: tlmgr Match189328
-Ref: tlmgr Selection189509
-Ref: tlmgr Display configuration buttons189713
-Node: tlmgr Package list area189896
-Ref: tlmgr a checkbox190480
-Ref: tlmgr package name190616
-Ref: tlmgr local revision (and version)190715
-Ref: tlmgr remote revision (and version)191090
-Ref: tlmgr short description191387
-Node: tlmgr Main display action buttons191432
-Ref: tlmgr Update all installed191698
-Ref: tlmgr Update192070
-Ref: tlmgr Install192120
-Ref: tlmgr Remove192306
-Ref: tlmgr Backup192484
-Node: tlmgr Menu bar192641
-Ref: tlmgr tlmgr menu192864
-Ref: tlmgr Options menu193172
-Ref: tlmgr Actions menu194255
-Ref: tlmgr Help menu194683
-Node: tlmgr GUI options194817
-Ref: tlmgr -background _color_195063
-Ref: tlmgr -font " _fontname_ _fontsize_ "195128
-Ref: tlmgr -foreground _color_195286
-Ref: tlmgr -geometry _geomspec_195338
-Ref: tlmgr -xrm _xresource_195530
-Node: tlmgr MACHINE-READABLE OUTPUT195799
-Node: tlmgr Machine-readable update and install output196609
-Ref: tlmgr location-url _location_197885
-Ref: tlmgr total-bytes _count_198101
-Ref: tlmgr _pkgname_198511
-Ref: tlmgr _status_198721
-Ref: tlmgr d198799
-Ref: tlmgr f198859
-Ref: tlmgr u199038
-Ref: tlmgr r199084
-Ref: tlmgr a199207
-Ref: tlmgr i199385
-Ref: tlmgr I199504
-Ref: tlmgr _localrev_199606
-Ref: tlmgr _serverrev_199713
-Ref: tlmgr _size_199825
-Ref: tlmgr _runtime_199994
-Ref: tlmgr _esttot_200064
-Node: tlmgr Machine-readable option output200097
-Node: tlmgr ENVIRONMENT VARIABLES200609
-Ref: tlmgr TEXLIVE_COMPRESSOR201120
-Ref: tlmgr TEXLIVE_DOWNLOADER201968
-Ref: tlmgr TL_DOWNLOAD_PROGRAM201991
-Ref: tlmgr TL_DOWNLOAD_ARGS202011
-Ref: tlmgr TEXLIVE_PREFER_OWN203037
-Node: tlmgr AUTHORS AND COPYRIGHT203861
-Node: Index204259
+Node: Adding a new program module52435
+Node: Adding a new engine57198
+Node: Adding a new generic library module58567
+Node: Adding a new TeX-specific library module60788
+Node: Configure options61486
+Node: Global configure options62867
+Node: --disable-native-texlive-build63409
+Node: --prefix --bindir ...64371
+Node: --disable-largefile64911
+Node: --disable-missing65453
+Node: --enable-compiler-warnings=LEVEL65854
+Node: --enable-cxx-runtime-hack66510
+Node: --enable-maintainer-mode66930
+Node: --enable-multiplatform67459
+Node: --enable-shared68032
+Node: --enable-silent-rules68403
+Node: --without-ln-s68855
+Node: --without-x69202
+Node: Program-specific configure options69390
+Node: --enable-PROG --disable-PROG70033
+Node: --disable-all-pkgs70306
+Node: Configure options for texk/web2c71089
+Node: Configure options for texk/bibtex-x73627
+Node: Configure options for texk/dvipdfm-x74170
+Node: Configure options for texk/dvisvgm74946
+Node: Configure options for texk/texlive75827
+Node: Configure options for texk/xdvik76248
+Node: Configure options for utils/xindy76869
+Node: Library-specific configure options77759
+Node: Configure options for kpathsea78720
+Node: Variables for configure79424
+Node: Coding conventions80850
+Node: Declarations and definitions81565
+Node: Const83739
+Node: Continuous integration85603
+Node: Transfer from Subversion to Github86267
+Node: Automatic update of the Git mirror88429
+Node: CI testing on Travis-CI89011
+Node: Releases on Github89720
+Node: install-tl90160
+Node: install-tl NAME90529
+Node: install-tl SYNOPSIS90687
+Node: install-tl DESCRIPTION90945
+Node: install-tl REFERENCES92005
+Node: install-tl OPTIONS92529
+Ref: install-tl *-gui* [[=]_module_]92870
+Ref: install-tl text93078
+Ref: install-tl tcl (or "perltk" or "wizard" or "expert" or nothing)93263
+Ref: install-tl *-no-gui*93895
+Ref: install-tl *-lang* _llcode_93985
+Ref: install-tl *-repository* _url|path_94609
+Ref: install-tl *-select-repository*95498
+Ref: install-tl *-all-options*95916
+Ref: install-tl *-custom-bin* _path_96171
+Ref: install-tl *-debug-translation*97005
+Ref: install-tl *-force-platform* _platform_97281
+Ref: install-tl *-help*, *--help*, *-?*97525
+Ref: install-tl *-in-place*97938
+Ref: install-tl *-init-from-profile* _profile_file_98483
+Ref: install-tl *-logfile* _file_98752
+Ref: install-tl *-no-cls*99103
+Ref: install-tl *-no-persistent-downloads*99251
+Ref: install-tl *-persistent-downloads*99276
+Ref: install-tl *-no-verify-downloads*99894
+Ref: install-tl *-non-admin*100257
+Ref: install-tl *-portable*100350
+Ref: install-tl *-print-platform*100489
+Ref: install-tl *-profile* _profile_file_100687
+Ref: install-tl *-q*100908
+Ref: install-tl *-scheme* _scheme_100970
+Ref: install-tl *-v*101444
+Ref: install-tl *-version*, *--version*101599
+Node: install-tl PROFILES101733
+Ref: install-tl instopt_adjustpath (default 0 on Unix, 1 on Windows)104599
+Ref: install-tl instopt_adjustrepo (default 1)104675
+Ref: install-tl instopt_letter (default 0)104812
+Ref: install-tl instopt_portable (default 0)104903
+Ref: install-tl instopt_write18_restricted (default 1)104999
+Node: install-tl ENVIRONMENT VARIABLES106338
+Ref: install-tl TEXLIVE_DOWNLOADER106716
+Ref: install-tl TL_DOWNLOAD_PROGRAM106739
+Ref: install-tl TL_DOWNLOAD_ARGS106759
+Ref: install-tl TEXLIVE_INSTALL_ENV_NOCHECK106963
+Ref: install-tl TEXLIVE_INSTALL_NO_CONTEXT_CACHE107165
+Ref: install-tl TEXLIVE_INSTALL_NO_RESUME107274
+Ref: install-tl TEXLIVE_INSTALL_NO_WELCOME107426
+Ref: install-tl TEXLIVE_INSTALL_PAPER107547
+Ref: install-tl TEXLIVE_INSTALL_PREFIX107693
+Ref: install-tl TEXLIVE_INSTALL_TEXDIR107719
+Ref: install-tl TEXLIVE_INSTALL_TEXMFCONFIG107750
+Ref: install-tl TEXLIVE_INSTALL_TEXMFVAR107778
+Ref: install-tl TEXLIVE_INSTALL_TEXMFHOME107807
+Ref: install-tl TEXLIVE_INSTALL_TEXMFLOCAL107837
+Ref: install-tl TEXLIVE_INSTALL_TEXMFSYSCONFIG107871
+Ref: install-tl TEXLIVE_INSTALL_TEXMFSYSVAR107902
+Ref: install-tl NOPERLDOC108273
+Node: install-tl AUTHORS AND COPYRIGHT108337
+Node: tlmgr108750
+Node: tlmgr NAME109235
+Node: tlmgr SYNOPSIS109367
+Node: tlmgr DESCRIPTION109557
+Node: tlmgr EXAMPLES110656
+Ref: tlmgr tlmgr option repository ctan110907
+Ref: tlmgr tlmgr option repository https://mirror.ctan.org/systems/texlive/tlnet110980
+Ref: tlmgr tlmgr update --list111585
+Ref: tlmgr tlmgr update --all111678
+Ref: tlmgr tlmgr info _what_111835
+Node: tlmgr OPTIONS112097
+Ref: tlmgr *--repository* _url|path_112617
+Ref: tlmgr /some/local/dir113803
+Ref: tlmgr file:/some/local/dir113832
+Ref: tlmgr ctan113905
+Ref: tlmgr https://mirror.ctan.org/systems/texlive/tlnet113959
+Ref: tlmgr http://server/path/to/tlnet114300
+Ref: tlmgr https://server/path/to/tlnet114681
+Ref: tlmgr ftp://server/path/to/tlnet115149
+Ref: tlmgr user@machine:/path/to/tlnet115281
+Ref: tlmgr scp://user@machine/path/to/tlnet115322
+Ref: tlmgr ssh://user@machine/path/to/tlnet115363
+Ref: tlmgr *--gui* [_action_]115756
+Ref: tlmgr *--gui-lang* _llcode_116569
+Ref: tlmgr *--command-logfile* _file_117310
+Ref: tlmgr *--debug-translation*117576
+Ref: tlmgr *--machine-readable*117779
+Ref: tlmgr *--no-execute-actions*118047
+Ref: tlmgr *--package-logfile* _file_118240
+Ref: tlmgr *--pause*118494
+Ref: tlmgr *--persistent-downloads*118649
+Ref: tlmgr *--no-persistent-downloads*118677
+Ref: tlmgr *--pin-file*119171
+Ref: tlmgr *--usermode*119389
+Ref: tlmgr *--usertree* _dir_119509
+Ref: tlmgr *--verify-repo=[none|main|all]*119635
+Node: tlmgr ACTIONS120534
+Node: tlmgr help121395
+Node: tlmgr version121872
+Node: tlmgr backup122135
+Ref: tlmgr *backup [_option_...] --all*122306
+Ref: tlmgr *backup [_option_...] _pkg_...*122339
+Ref: tlmgr *--backupdir* _directory_123405
+Ref: tlmgr *--all*123622
+Ref: tlmgr *--clean*[=_N_]123874
+Ref: tlmgr *--dry-run*124201
+Node: tlmgr candidates _pkg_124331
+Node: tlmgr check [_option_...] [depends|executes|files|runfiles|texmfdbs|all]124686
+Ref: tlmgr *depends*125200
+Ref: tlmgr *executes*125542
+Ref: tlmgr *files*125657
+Ref: tlmgr *runfiles*125793
+Ref: tlmgr *texmfdbs*125930
+Ref: tlmgr - all items in TEXMFDBS have the !! prefix.126160
+Ref: tlmgr - all items in TEXMFBDS have an ls-R file (if they exist at all).126236
+Ref: tlmgr - all items in TEXMF with !! are listed in TEXMFDBS.126301
+Ref: tlmgr - all items in TEXMF with an ls-R file are listed in TEXMFDBS.126376
+Ref: tlmgr *--use-svn*126401
+Node: tlmgr conf126542
+Ref: tlmgr *conf [texmf|tlmgr|updmap [--conffile _file_] [--delete] [_key_ [_value_]]]*126830
+Ref: tlmgr *conf auxtrees [--conffile _file_] [show|add|remove] [_value_]*126894
+Node: tlmgr dump-tlpdb [_option_...] [--json]129309
+Ref: tlmgr *--local*129742
+Ref: tlmgr *--remote*129781
+Ref: tlmgr *--json*129819
+Node: tlmgr generate130390
+Ref: tlmgr *generate [_option_...] language*130586
+Ref: tlmgr *generate [_option_...] language.dat*130625
+Ref: tlmgr *generate [_option_...] language.def*130664
+Ref: tlmgr *generate [_option_...] language.dat.lua*130707
+Ref: tlmgr *--dest* _output_file_133034
+Ref: tlmgr *--localcfg* _local_conf_file_133610
+Ref: tlmgr *--rebuild-sys*133733
+Node: tlmgr gui134548
+Node: tlmgr info134726
+Ref: tlmgr *info [_option_...] _pkg_...*134888
+Ref: tlmgr *info [_option_...] collections*134922
+Ref: tlmgr *info [_option_...] schemes*134952
+Ref: tlmgr *--list*136482
+Ref: tlmgr *--only-installed*136796
+Ref: tlmgr *--only-remote*136984
+Ref: tlmgr *--data item1,item2,...*137288
+Ref: tlmgr *--json* 1138651
+Node: tlmgr init-usertree139034
+Node: tlmgr install [_option_...] _pkg_...139415
+Ref: tlmgr *--dry-run* 1139923
+Ref: tlmgr *--file*140040
+Ref: tlmgr *--force*140262
+Ref: tlmgr *--no-depends*140482
+Ref: tlmgr *--no-depends-at-all*140641
+Ref: tlmgr *--reinstall*141041
+Ref: tlmgr *--with-doc*141419
+Ref: tlmgr *--with-src*141432
+Node: tlmgr key142160
+Ref: tlmgr *key list*142318
+Ref: tlmgr *key add _file_*142336
+Ref: tlmgr *key remove _keyid_*142358
+Node: tlmgr list142952
+Node: tlmgr option143114
+Ref: tlmgr *option [--json] [show]*143269
+Ref: tlmgr *option [--json] showall|help*143300
+Ref: tlmgr *option _key_ [_value_]*143326
+Node: tlmgr paper147905
+Ref: tlmgr *paper [a4|letter]*148054
+Ref: tlmgr *<[xdvi|pdftex|dvips|dvipdfmx|context|psutils] paper [_papersize_|--list]*>148130
+Ref: tlmgr *paper --json*148145
+Node: tlmgr path149360
+Ref: tlmgr *path [--w32mode=user|admin] add*149521
+Ref: tlmgr *path [--w32mode=user|admin] remove*149558
+Node: tlmgr pinning151043
+Ref: tlmgr pinning show151284
+Ref: tlmgr pinning add _repo_ _pkgglob_...151357
+Ref: tlmgr pinning remove _repo_ _pkgglob_...151476
+Ref: tlmgr pinning remove _repo_ --all151629
+Node: tlmgr platform151683
+Ref: tlmgr *platform list|add|remove _platform_...*151869
+Ref: tlmgr *platform set _platform_*151896
+Ref: tlmgr *platform set auto*151917
+Ref: tlmgr *--dry-run* 2152794
+Node: tlmgr postaction152913
+Ref: tlmgr *postaction [_option_...] install [shortcut|fileassoc|script] [_pkg_...]*153143
+Ref: tlmgr *postaction [_option_...] remove [shortcut|fileassoc|script] [_pkg_...]*153217
+Ref: tlmgr *--w32mode=[user|admin]*153532
+Ref: tlmgr *--fileassocmode=[1|2]*153948
+Ref: tlmgr *--all* 1154233
+Node: tlmgr print-platform154288
+Node: tlmgr print-platform-info154619
+Node: tlmgr remove [_option_...] _pkg_...154919
+Ref: tlmgr *--all* 2155403
+Ref: tlmgr *--backup*155513
+Ref: tlmgr *--backupdir* _directory_ 1155539
+Ref: tlmgr *--no-depends* 1155944
+Ref: tlmgr *--no-depends-at-all* 1156006
+Ref: tlmgr *--force* 1156109
+Ref: tlmgr *--dry-run* 3156582
+Node: tlmgr repository156918
+Ref: tlmgr *repository list*157106
+Ref: tlmgr *repository list _path|url|tag_*157140
+Ref: tlmgr *repository add _path_ [_tag_]*157173
+Ref: tlmgr *repository remove _path|tag_*157205
+Ref: tlmgr *repository set _path_[#_tag_] [_path_[#_tag_] ...]*157259
+Ref: tlmgr *repository status*157280
+Ref: tlmgr The tag (which can be the same as the url);158507
+Node: tlmgr restore158985
+Ref: tlmgr *restore [_option_...] _pkg_ [_rev_]*159164
+Ref: tlmgr *restore [_option_...] --all*159194
+Ref: tlmgr *--all* 3159894
+Ref: tlmgr *--backupdir* _directory_ 2160108
+Ref: tlmgr *--dry-run* 4160289
+Ref: tlmgr *--force* 2160421
+Ref: tlmgr *--json* 2160467
+Node: tlmgr search160794
+Ref: tlmgr *search [_option_...] _what_*160958
+Ref: tlmgr *search [_option_...] --file _what_*160995
+Ref: tlmgr *search [_option_...] --all _what_*161031
+Ref: tlmgr *--file* 1161251
+Ref: tlmgr *--all* 4161313
+Ref: tlmgr *--global*161402
+Ref: tlmgr *--word*161529
+Node: tlmgr shell161844
+Ref: tlmgr protocol162579
+Ref: tlmgr help 1162643
+Ref: tlmgr version 1162696
+Ref: tlmgr quit, end, bye, byebye, EOF162764
+Ref: tlmgr restart162785
+Ref: tlmgr load [local|remote]162908
+Ref: tlmgr save162978
+Ref: tlmgr get [_var_] =item set [_var_ [_val_]]163101
+Node: tlmgr show163702
+Node: tlmgr uninstall163869
+Node: tlmgr update [_option_...] [_pkg_...]164099
+Ref: tlmgr *--all* 5164470
+Ref: tlmgr *--self*166649
+Ref: tlmgr *--dry-run* 5167413
+Ref: tlmgr *--list* [_pkg_]167590
+Ref: tlmgr *--exclude* _pkg_168279
+Ref: tlmgr *--no-auto-remove* [_pkg_...]169079
+Ref: tlmgr *--no-auto-install* [_pkg_...]169563
+Ref: tlmgr *--reinstall-forcibly-removed*170325
+Ref: tlmgr *--backup* 1170860
+Ref: tlmgr *--backupdir* _directory_ 3170886
+Ref: tlmgr *--no-depends* 2172052
+Ref: tlmgr *--no-depends-at-all* 2172255
+Ref: tlmgr *--force* 3172358
+Node: tlmgr CONFIGURATION FILE FOR TLMGR173349
+Ref: tlmgr auto-remove, value 0 or 1 (default 1), same as command-line option.174356
+Ref: tlmgr gui-expertmode, value 0 or 1 (default 1). This switches between the full GUI and a simplified GUI with only the most common settings.174493
+Ref: tlmgr gui-lang _llcode_, with a language code value as with the command-line option.174575
+Ref: tlmgr no-checksums, value 0 or 1 (default 0, see below).174629
+Ref: tlmgr persistent-downloads, value 0 or 1 (default 1), same as command-line option.174709
+Ref: tlmgr require-verification, value 0 or 1 (default 0), same as command-line option.174789
+Ref: tlmgr tkfontscale, value any float. Controls the scaling of fonts in the Tk based frontends.174879
+Ref: tlmgr update-exclude, value: comma-separated list of packages (no space allowed). Same as the command line option --exclude for the action update.175027
+Ref: tlmgr verify-downloads, value 0 or 1 (default 1), same as command-line option.175103
+Ref: tlmgr allowed-actions _action1_ [,_action_,...] The value is a comma-separated list of tlmgr actions which are allowed to be executed when tlmgr is invoked in system mode (that is, without --usermode).175372
+Node: tlmgr CRYPTOGRAPHIC VERIFICATION176460
+Node: tlmgr Configuration of GnuPG invocation178633
+Node: tlmgr USER MODE179271
+Node: tlmgr User mode install182117
+Node: tlmgr User mode backup, restore, remove, update183261
+Node: tlmgr User mode generate, option, paper183703
+Node: tlmgr MULTIPLE REPOSITORIES184079
+Node: tlmgr Pinning185808
+Node: tlmgr GUI FOR TLMGR187731
+Node: tlmgr Main display189380
+Node: tlmgr Display configuration area189632
+Ref: tlmgr Status189993
+Ref: tlmgr Category190157
+Ref: tlmgr Match190343
+Ref: tlmgr Selection190524
+Ref: tlmgr Display configuration buttons190728
+Node: tlmgr Package list area190911
+Ref: tlmgr a checkbox191495
+Ref: tlmgr package name191631
+Ref: tlmgr local revision (and version)191730
+Ref: tlmgr remote revision (and version)192105
+Ref: tlmgr short description192402
+Node: tlmgr Main display action buttons192447
+Ref: tlmgr Update all installed192713
+Ref: tlmgr Update193085
+Ref: tlmgr Install193135
+Ref: tlmgr Remove193321
+Ref: tlmgr Backup193499
+Node: tlmgr Menu bar193656
+Ref: tlmgr tlmgr menu193879
+Ref: tlmgr Options menu194187
+Ref: tlmgr Actions menu195270
+Ref: tlmgr Help menu195698
+Node: tlmgr GUI options195832
+Ref: tlmgr -background _color_196078
+Ref: tlmgr -font " _fontname_ _fontsize_ "196143
+Ref: tlmgr -foreground _color_196301
+Ref: tlmgr -geometry _geomspec_196353
+Ref: tlmgr -xrm _xresource_196545
+Node: tlmgr MACHINE-READABLE OUTPUT196814
+Node: tlmgr Machine-readable update and install output197624
+Ref: tlmgr location-url _location_198900
+Ref: tlmgr total-bytes _count_199116
+Ref: tlmgr _pkgname_199526
+Ref: tlmgr _status_199736
+Ref: tlmgr d199814
+Ref: tlmgr f199874
+Ref: tlmgr u200053
+Ref: tlmgr r200099
+Ref: tlmgr a200222
+Ref: tlmgr i200400
+Ref: tlmgr I200519
+Ref: tlmgr _localrev_200621
+Ref: tlmgr _serverrev_200728
+Ref: tlmgr _size_200840
+Ref: tlmgr _runtime_201009
+Ref: tlmgr _esttot_201079
+Node: tlmgr Machine-readable option output201112
+Node: tlmgr ENVIRONMENT VARIABLES201624
+Ref: tlmgr TEXLIVE_COMPRESSOR202135
+Ref: tlmgr TEXLIVE_DOWNLOADER202983
+Ref: tlmgr TL_DOWNLOAD_PROGRAM203006
+Ref: tlmgr TL_DOWNLOAD_ARGS203026
+Ref: tlmgr TEXLIVE_PREFER_OWN204052
+Node: tlmgr AUTHORS AND COPYRIGHT204876
+Node: Index205274

End Tag Table
diff --git a/Build/source/doc/tlbuild.texi b/Build/source/doc/tlbuild.texi
index b4a790dc120..6084aa2af5c 100644
--- a/Build/source/doc/tlbuild.texi
+++ b/Build/source/doc/tlbuild.texi
@@ -1619,6 +1619,7 @@ Please maintain @file{foo/ChangeLog} for all TL changes.
@menu
* Adding a new program module::
+* Adding a new engine::
* Adding a new generic library module::
* Adding a new @TeX{}-specific library module::
@end menu
@@ -1751,14 +1752,37 @@ builds from scratch.
After final success, don't forget to commit. (Or email the TL
maintainers with the patch.)
+@node Adding a new engine
+@subsection Adding a new engine
+
+@cindex adding a new engine
@cindex engine, adding new
-Caveat: adding a new @TeX{} engine is not completely different, but
-it's not all that similar, either. In that case, the work is done
-inside a new subdirectory of @file{texk/web2c/}. Many things are
-common to all the engines, other things need to be copied and possibly
-modified for each one, yet others are unique to each. No general
-recipe is possible.
+Adding a new @TeX{} engine is not completely different from adding a
+program, but it's not all that similar, either. In this case, the main
+work is done by creating a new subdirectory of @file{texk/web2c/} for
+the engine. The subdirectory is conventionally named ending in
+@file{dir}, like @file{pdftexdir} and @file{xetexdir}.
+
+The source files for the new engine should be put in this
+@file{@var{newengine}dir} subdirectory. Also, a file
+@file{@var{newengine}dir/am/@var{newengine}.am} (e.g.,
+@file{pdftexdir/am/pdftex.am} is needed with the Makefile fragment
+needed to build it.
+
+The overall @file{web2c/Makefile.am} needs to have an @file{include}
+statement added to insert that @file{@var{newengine}.am} file.
+
+In @file{web2c/ac/web2c.ac}, a line needs to be added in the
+definition of the @code{kpse_tex_progs} variable to include it in the
+build. That line specifies whether the new engine is built by default,
+and the additional libraries requires.
+
+For examples of building engines in CWEB, you can check the existing
+@file{hitexdir} and @file{mplibdir} directories; these are somewhat
+simpler than Lua@TeX{}. Of course, every engine will have its own
+unique features and requirements, so existing examples will only take
+you so far.
@node Adding a new generic library module
@subsection Adding a new generic library module
diff --git a/Build/source/libs/configure b/Build/source/libs/configure
index 8a813f41adc..e3c001c5550 100755
--- a/Build/source/libs/configure
+++ b/Build/source/libs/configure
@@ -765,6 +765,7 @@ enable_uptex_synctex
enable_euptex
enable_euptex_synctex
enable_aleph
+enable_hitex
enable_pdftex
enable_pdftex_synctex
enable_luatex
@@ -1560,6 +1561,7 @@ Optional Features:
--disable-euptex do not compile and install e-upTeX
--disable-euptex-synctex build e-upTeX without SyncTeX support
--disable-aleph do not compile and install Aleph
+ --enable-hitex compile and install HiTeX
--disable-pdftex do not compile and install pdfTeX
--disable-pdftex-synctex build pdfTeX without SyncTeX support
--disable-luatex do not compile and install LuaTeX
@@ -4560,6 +4562,21 @@ case $enable_aleph in #(
*) :
enable_aleph=yes ;;
esac
+# Check whether --enable-hitex was given.
+if test ${enable_hitex+y}
+then :
+ enableval=$enable_hitex;
+fi
+case $enable_hitex in #(
+ yes | no) :
+ ;; #(
+ *) :
+ enable_hitex=no ;;
+esac
+
+test "x$enable_web2c:$enable_hitex" = xyes:yes && {
+ need_zlib=yes
+}
# Check whether --enable-pdftex was given.
if test ${enable_pdftex+y}
then :
diff --git a/Build/source/texk/configure b/Build/source/texk/configure
index d0a121e16f6..81475012866 100755
--- a/Build/source/texk/configure
+++ b/Build/source/texk/configure
@@ -765,6 +765,7 @@ enable_uptex_synctex
enable_euptex
enable_euptex_synctex
enable_aleph
+enable_hitex
enable_pdftex
enable_pdftex_synctex
enable_luatex
@@ -1560,6 +1561,7 @@ Optional Features:
--disable-euptex do not compile and install e-upTeX
--disable-euptex-synctex build e-upTeX without SyncTeX support
--disable-aleph do not compile and install Aleph
+ --enable-hitex compile and install HiTeX
--disable-pdftex do not compile and install pdfTeX
--disable-pdftex-synctex build pdfTeX without SyncTeX support
--disable-luatex do not compile and install LuaTeX
@@ -4560,6 +4562,21 @@ case $enable_aleph in #(
*) :
enable_aleph=yes ;;
esac
+# Check whether --enable-hitex was given.
+if test ${enable_hitex+y}
+then :
+ enableval=$enable_hitex;
+fi
+case $enable_hitex in #(
+ yes | no) :
+ ;; #(
+ *) :
+ enable_hitex=no ;;
+esac
+
+test "x$enable_web2c:$enable_hitex" = xyes:yes && {
+ need_zlib=yes
+}
# Check whether --enable-pdftex was given.
if test ${enable_pdftex+y}
then :
diff --git a/Build/source/texk/web2c/ChangeLog b/Build/source/texk/web2c/ChangeLog
index 480f59239f6..ef1f540253c 100644
--- a/Build/source/texk/web2c/ChangeLog
+++ b/Build/source/texk/web2c/ChangeLog
@@ -1,3 +1,9 @@
+2021-09-21 Karl Berry <karl@freefriends.org>
+
+ * ac/web2c.ac (kpse_tex_progs): include hitex, disabled by default.
+ * Makefile.am: include hitexdir/am/hitex.am.
+ * hitexdir: new directory.
+
2021-09-16 Andreas Scherer <https://ascherer.github.io>
* Makefile.in,
diff --git a/Build/source/texk/web2c/Makefile.am b/Build/source/texk/web2c/Makefile.am
index 0ad8e4db3ab..1d8f29f4d63 100644
--- a/Build/source/texk/web2c/Makefile.am
+++ b/Build/source/texk/web2c/Makefile.am
@@ -274,6 +274,9 @@ include $(srcdir)/uptexdir/am/uptex.am
## e-upTeX
include $(srcdir)/euptexdir/am/euptex.am
+## hiTeX
+include $(srcdir)/hitexdir/am/hitex.am
+
## pdfTeX
include $(srcdir)/pdftexdir/am/libpdftex.am
include $(srcdir)/pdftexdir/am/pdftex.am
@@ -289,12 +292,9 @@ include $(srcdir)/luatexdir/am/libluatex.am
include $(srcdir)/luatexdir/am/luaffi.am
include $(srcdir)/luatexdir/am/luatex.am
-
## luahbTeX
include $(srcdir)/luatexdir/am/luaharfbuzz.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 eab99a2e180..7273a62c1ed 100644
--- a/Build/source/texk/web2c/Makefile.in
+++ b/Build/source/texk/web2c/Makefile.in
@@ -112,7 +112,8 @@ EXTRA_PROGRAMS = $(am__EXEEXT_1) tex$(EXEEXT) mf$(EXEEXT) \
mfluajit$(EXEEXT) mfluajit-nowin$(EXEEXT) mpost$(EXEEXT) \
pmpost$(EXEEXT) upmpost$(EXEEXT) etex$(EXEEXT) ptex$(EXEEXT) \
$(am__EXEEXT_2) eptex$(EXEEXT) uptex$(EXEEXT) $(am__EXEEXT_3) \
- euptex$(EXEEXT) pdftex$(EXEEXT) ttf2afm$(EXEEXT) \
+ euptex$(EXEEXT) hitex$(EXEEXT) hishrink$(EXEEXT) \
+ histretch$(EXEEXT) pdftex$(EXEEXT) ttf2afm$(EXEEXT) \
pdftosrc$(EXEEXT) luatex$(EXEEXT) luajittex$(EXEEXT) \
luahbtex$(EXEEXT) luajithbtex$(EXEEXT) xetex$(EXEEXT) \
$(am__EXEEXT_4) aleph$(EXEEXT) synctex$(EXEEXT) \
@@ -128,22 +129,23 @@ bin_PROGRAMS = tangle$(EXEEXT) ctangle$(EXEEXT) $(am__EXEEXT_5) \
$(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_31) $(am__EXEEXT_32)
+ $(am__EXEEXT_30) $(am__EXEEXT_31) $(am__EXEEXT_32) \
+ $(am__EXEEXT_33)
noinst_PROGRAMS = tangleboot$(EXEEXT) ctangleboot$(EXEEXT) \
- $(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 \
+ $(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) \
+ $(am__EXEEXT_43)
+TESTS = tangle.test $(am__EXEEXT_46) ctiedir/ctie.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_63) $(am__append_66) \
$(am__append_68) $(am__append_73) $(am__append_76) \
- $(am__append_78) $(am__append_83) $(am__append_104) \
- $(am__append_105) $(am__append_106) $(am__append_107) \
- $(am__append_115) $(am__append_117) $(am__append_119) \
- $(am__append_153) libmd5/md5.test
+ $(am__append_78) $(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_154) libmd5/md5.test
@WEB_TRUE@am__append_1 = $(web_programs)
@WEB_TRUE@am__append_2 = $(web_tests)
@TEX_TRUE@am__append_3 = tex
@@ -228,150 +230,151 @@ TESTS = tangle.test $(am__EXEEXT_45) ctiedir/ctie.test \
@EUPTEX_TRUE@am__append_78 = $(euptex_tests)
@EUPTEX_TRUE@am__append_79 = euptrip.diffs
@EUPTEX_TRUE@am__append_80 = euptrip-clean
-@MINGW32_TRUE@am__append_81 = \
+@HITEX_TRUE@am__append_81 = hitex hishrink histretch
+@MINGW32_TRUE@am__append_82 = \
@MINGW32_TRUE@ pdftexdir/regex/regex.c \
@MINGW32_TRUE@ pdftexdir/regex/regex.h
-@PDFTEX_TRUE@am__append_82 = pdftex ttf2afm pdftosrc
-@PDFTEX_TRUE@am__append_83 = $(pdftex_tests) $(ttf2afm_tests) \
+@PDFTEX_TRUE@am__append_83 = pdftex ttf2afm pdftosrc
+@PDFTEX_TRUE@am__append_84 = $(pdftex_tests) $(ttf2afm_tests) \
@PDFTEX_TRUE@ $(pdftosrc_tests)
-@LUATEX_TRUE@am__append_84 = luatex
-@LUATEX_TRUE@@WIN32_TRUE@am__append_85 = call_luatex
-@LUATEX_TRUE@@WIN32_TRUE@am__append_86 = install-luatex-links
-@LUATEX_TRUE@@WIN32_TRUE@am__append_87 = uninstall-luatex-links
-@LUATEX_TRUE@@WIN32_FALSE@am__append_88 = luatex$(EXEEXT):texlua luatex$(EXEEXT):texluac
-@LUAHBTEX_TRUE@am__append_89 = luahbtex
-@LUAHBTEX_TRUE@@WIN32_TRUE@am__append_90 = call_luahbtex
-@LUAHBTEX_TRUE@@WIN32_TRUE@am__append_91 = install-luahbtex-links
-@LUAHBTEX_TRUE@@WIN32_TRUE@am__append_92 = uninstall-luahbtex-links
+@LUATEX_TRUE@am__append_85 = luatex
+@LUATEX_TRUE@@WIN32_TRUE@am__append_86 = call_luatex
+@LUATEX_TRUE@@WIN32_TRUE@am__append_87 = install-luatex-links
+@LUATEX_TRUE@@WIN32_TRUE@am__append_88 = uninstall-luatex-links
+@LUATEX_TRUE@@WIN32_FALSE@am__append_89 = luatex$(EXEEXT):texlua luatex$(EXEEXT):texluac
+@LUAHBTEX_TRUE@am__append_90 = luahbtex
+@LUAHBTEX_TRUE@@WIN32_TRUE@am__append_91 = call_luahbtex
+@LUAHBTEX_TRUE@@WIN32_TRUE@am__append_92 = install-luahbtex-links
+@LUAHBTEX_TRUE@@WIN32_TRUE@am__append_93 = uninstall-luahbtex-links
# keep texlua[c] as links to luatex unless luatex is not installed.
-@LUAHBTEX_TRUE@@LUATEX_FALSE@@WIN32_FALSE@am__append_93 = luahbtex$(EXEEXT):texlua luahbtex$(EXEEXT):texluac
-@LUAJITTEX_TRUE@am__append_94 = luajittex
-@LUAJITTEX_TRUE@@WIN32_TRUE@am__append_95 = call_luajittex
-@LUAJITTEX_TRUE@@WIN32_TRUE@am__append_96 = install-luajittex-links
-@LUAJITTEX_TRUE@@WIN32_TRUE@am__append_97 = uninstall-luajittex-links
-@LUAJITTEX_TRUE@@WIN32_FALSE@am__append_98 = luajittex$(EXEEXT):texluajit luajittex$(EXEEXT):texluajitc
-@LUAJITHBTEX_TRUE@am__append_99 = luajithbtex
-@LUAJITHBTEX_TRUE@@WIN32_TRUE@am__append_100 = call_luajithbtex
-@LUAJITHBTEX_TRUE@@WIN32_TRUE@am__append_101 = install-luajithbtex-links
-@LUAJITHBTEX_TRUE@@WIN32_TRUE@am__append_102 = uninstall-luajithbtex-links
+@LUAHBTEX_TRUE@@LUATEX_FALSE@@WIN32_FALSE@am__append_94 = luahbtex$(EXEEXT):texlua luahbtex$(EXEEXT):texluac
+@LUAJITTEX_TRUE@am__append_95 = luajittex
+@LUAJITTEX_TRUE@@WIN32_TRUE@am__append_96 = call_luajittex
+@LUAJITTEX_TRUE@@WIN32_TRUE@am__append_97 = install-luajittex-links
+@LUAJITTEX_TRUE@@WIN32_TRUE@am__append_98 = uninstall-luajittex-links
+@LUAJITTEX_TRUE@@WIN32_FALSE@am__append_99 = luajittex$(EXEEXT):texluajit luajittex$(EXEEXT):texluajitc
+@LUAJITHBTEX_TRUE@am__append_100 = luajithbtex
+@LUAJITHBTEX_TRUE@@WIN32_TRUE@am__append_101 = call_luajithbtex
+@LUAJITHBTEX_TRUE@@WIN32_TRUE@am__append_102 = install-luajithbtex-links
+@LUAJITHBTEX_TRUE@@WIN32_TRUE@am__append_103 = uninstall-luajithbtex-links
# keep texluajit[c] as links to luajittex unless luajittex is not installed.
-@LUAJITHBTEX_TRUE@@LUAJITTEX_FALSE@@WIN32_FALSE@am__append_103 = luajithbtex$(EXEEXT):texluajit luajithbtex$(EXEEXT):texluajitc
-@LUATEX_TRUE@am__append_104 = $(luatex_tests)
-@LUAHBTEX_TRUE@am__append_105 = $(luahbtex_tests)
-@LUAJITTEX_TRUE@am__append_106 = $(luajittex_tests)
-@LUAJITHBTEX_TRUE@am__append_107 = $(luajithbtex_tests)
-@XETEX_TRUE@am__append_108 = xetex
-@XETEX_MACOSX_TRUE@am__append_109 = -DXETEX_MAC
-@XETEX_MACOSX_TRUE@am__append_110 = -std=c++11
-@XETEX_MACOSX_FALSE@am__append_111 = $(FONTCONFIG_INCLUDES)
-@XETEX_MACOSX_FALSE@am__append_112 = $(FONTCONFIG_LIBS)
-@XETEX_MACOSX_TRUE@am__append_113 = \
+@LUAJITHBTEX_TRUE@@LUAJITTEX_FALSE@@WIN32_FALSE@am__append_104 = luajithbtex$(EXEEXT):texluajit luajithbtex$(EXEEXT):texluajitc
+@LUATEX_TRUE@am__append_105 = $(luatex_tests)
+@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 = \
@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_114 = \
+@XETEX_MACOSX_FALSE@am__append_115 = \
@XETEX_MACOSX_FALSE@ xetexdir/XeTeXFontMgr_FC.cpp \
@XETEX_MACOSX_FALSE@ xetexdir/XeTeXFontMgr_FC.h
-@XETEX_TRUE@am__append_115 = $(xetex_tests)
-@OTANGLE_TRUE@am__append_116 = $(omegaware_programs)
-@OTANGLE_TRUE@am__append_117 = $(OTANGLE_tests) $(OMFONTS_tests)
-@ALEPH_TRUE@am__append_118 = aleph
-@ALEPH_TRUE@am__append_119 = $(aleph_tests)
-@SYNCTEX_TRUE@am__append_120 = synctex
-@SYNCTEX_TRUE@am__append_121 = $(LTLIBSYNCTEX)
-@SYNCTEX_TRUE@am__append_122 = $(LIBSYNCTEX)
-@MINGW32_TRUE@am__append_123 = -lshlwapi
+@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 = -lshlwapi
-@TEX_SYNCTEX_TRUE@am__append_125 = -I$(srcdir)/synctexdir \
+@MINGW32_TRUE@am__append_125 = -lshlwapi
+@TEX_SYNCTEX_TRUE@am__append_126 = -I$(srcdir)/synctexdir \
@TEX_SYNCTEX_TRUE@ $(ZLIB_INCLUDES) -D__SyncTeX__ \
@TEX_SYNCTEX_TRUE@ -DSYNCTEX_ENGINE_H=\"synctex-tex.h\"
-@TEX_SYNCTEX_TRUE@am__append_126 = $(ZLIB_LIBS)
-@TEX_SYNCTEX_TRUE@am__append_127 = $(ZLIB_DEPEND)
-@TEX_SYNCTEX_TRUE@am__append_128 = \
+@TEX_SYNCTEX_TRUE@am__append_127 = $(ZLIB_LIBS)
+@TEX_SYNCTEX_TRUE@am__append_128 = $(ZLIB_DEPEND)
+@TEX_SYNCTEX_TRUE@am__append_129 = \
@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_129 = -I$(srcdir)/synctexdir \
+@ETEX_SYNCTEX_TRUE@am__append_130 = -I$(srcdir)/synctexdir \
@ETEX_SYNCTEX_TRUE@ $(ZLIB_INCLUDES) -D__SyncTeX__ \
@ETEX_SYNCTEX_TRUE@ -DSYNCTEX_ENGINE_H=\"synctex-etex.h\"
-@ETEX_SYNCTEX_TRUE@am__append_130 = $(ZLIB_LIBS)
-@ETEX_SYNCTEX_TRUE@am__append_131 = $(ZLIB_DEPEND)
-@ETEX_SYNCTEX_TRUE@am__append_132 = \
+@ETEX_SYNCTEX_TRUE@am__append_131 = $(ZLIB_LIBS)
+@ETEX_SYNCTEX_TRUE@am__append_132 = $(ZLIB_DEPEND)
+@ETEX_SYNCTEX_TRUE@am__append_133 = \
@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
-@PTEX_SYNCTEX_TRUE@am__append_133 = -I$(srcdir)/synctexdir \
+@PTEX_SYNCTEX_TRUE@am__append_134 = -I$(srcdir)/synctexdir \
@PTEX_SYNCTEX_TRUE@ $(ZLIB_INCLUDES) -D__SyncTeX__ \
@PTEX_SYNCTEX_TRUE@ -DSYNCTEX_ENGINE_H=\"synctex-ptex.h\"
-@PTEX_SYNCTEX_TRUE@am__append_134 = $(ZLIB_LIBS)
-@PTEX_SYNCTEX_TRUE@am__append_135 = $(ZLIB_DEPEND)
-@PTEX_SYNCTEX_TRUE@am__append_136 = \
+@PTEX_SYNCTEX_TRUE@am__append_135 = $(ZLIB_LIBS)
+@PTEX_SYNCTEX_TRUE@am__append_136 = $(ZLIB_DEPEND)
+@PTEX_SYNCTEX_TRUE@am__append_137 = \
@PTEX_SYNCTEX_TRUE@ synctexdir/synctex.c \
@PTEX_SYNCTEX_TRUE@ synctexdir/synctex.h \
@PTEX_SYNCTEX_TRUE@ synctexdir/synctex-common.h \
@PTEX_SYNCTEX_TRUE@ synctexdir/synctex-ptex.h
-@UPTEX_SYNCTEX_TRUE@am__append_137 = -I$(srcdir)/synctexdir \
+@UPTEX_SYNCTEX_TRUE@am__append_138 = -I$(srcdir)/synctexdir \
@UPTEX_SYNCTEX_TRUE@ $(ZLIB_INCLUDES) -D__SyncTeX__ \
@UPTEX_SYNCTEX_TRUE@ -DSYNCTEX_ENGINE_H=\"synctex-uptex.h\"
-@UPTEX_SYNCTEX_TRUE@am__append_138 = $(ZLIB_LIBS)
-@UPTEX_SYNCTEX_TRUE@am__append_139 = $(ZLIB_DEPEND)
-@UPTEX_SYNCTEX_TRUE@am__append_140 = \
+@UPTEX_SYNCTEX_TRUE@am__append_139 = $(ZLIB_LIBS)
+@UPTEX_SYNCTEX_TRUE@am__append_140 = $(ZLIB_DEPEND)
+@UPTEX_SYNCTEX_TRUE@am__append_141 = \
@UPTEX_SYNCTEX_TRUE@ synctexdir/synctex.c \
@UPTEX_SYNCTEX_TRUE@ synctexdir/synctex.h \
@UPTEX_SYNCTEX_TRUE@ synctexdir/synctex-common.h \
@UPTEX_SYNCTEX_TRUE@ synctexdir/synctex-uptex.h
-@EPTEX_SYNCTEX_TRUE@am__append_141 = -I$(srcdir)/synctexdir \
+@EPTEX_SYNCTEX_TRUE@am__append_142 = -I$(srcdir)/synctexdir \
@EPTEX_SYNCTEX_TRUE@ $(ZLIB_INCLUDES) -D__SyncTeX__ \
@EPTEX_SYNCTEX_TRUE@ -DSYNCTEX_ENGINE_H=\"synctex-eptex.h\"
-@EPTEX_SYNCTEX_TRUE@am__append_142 = $(ZLIB_LIBS)
-@EPTEX_SYNCTEX_TRUE@am__append_143 = $(ZLIB_DEPEND)
-@EPTEX_SYNCTEX_TRUE@am__append_144 = \
+@EPTEX_SYNCTEX_TRUE@am__append_143 = $(ZLIB_LIBS)
+@EPTEX_SYNCTEX_TRUE@am__append_144 = $(ZLIB_DEPEND)
+@EPTEX_SYNCTEX_TRUE@am__append_145 = \
@EPTEX_SYNCTEX_TRUE@ synctexdir/synctex.c \
@EPTEX_SYNCTEX_TRUE@ synctexdir/synctex.h \
@EPTEX_SYNCTEX_TRUE@ synctexdir/synctex-common.h \
@EPTEX_SYNCTEX_TRUE@ synctexdir/synctex-eptex.h
-@EUPTEX_SYNCTEX_TRUE@am__append_145 = -I$(srcdir)/synctexdir \
+@EUPTEX_SYNCTEX_TRUE@am__append_146 = -I$(srcdir)/synctexdir \
@EUPTEX_SYNCTEX_TRUE@ $(ZLIB_INCLUDES) -D__SyncTeX__ \
@EUPTEX_SYNCTEX_TRUE@ -DSYNCTEX_ENGINE_H=\"synctex-euptex.h\"
-@EUPTEX_SYNCTEX_TRUE@am__append_146 = $(ZLIB_LIBS)
-@EUPTEX_SYNCTEX_TRUE@am__append_147 = $(ZLIB_DEPEND)
-@EUPTEX_SYNCTEX_TRUE@am__append_148 = \
+@EUPTEX_SYNCTEX_TRUE@am__append_147 = $(ZLIB_LIBS)
+@EUPTEX_SYNCTEX_TRUE@am__append_148 = $(ZLIB_DEPEND)
+@EUPTEX_SYNCTEX_TRUE@am__append_149 = \
@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_149 = -I$(srcdir)/synctexdir \
+@PDFTEX_SYNCTEX_TRUE@am__append_150 = -I$(srcdir)/synctexdir \
@PDFTEX_SYNCTEX_TRUE@ -D__SyncTeX__ \
@PDFTEX_SYNCTEX_TRUE@ -DSYNCTEX_ENGINE_H=\"synctex-pdftex.h\"
-@PDFTEX_SYNCTEX_TRUE@am__append_150 = \
+@PDFTEX_SYNCTEX_TRUE@am__append_151 = \
@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_151 = -I$(srcdir)/synctexdir \
+@XETEX_SYNCTEX_TRUE@am__append_152 = -I$(srcdir)/synctexdir \
@XETEX_SYNCTEX_TRUE@ -D__SyncTeX__ \
@XETEX_SYNCTEX_TRUE@ -DSYNCTEX_ENGINE_H=\"synctex-xetex.h\"
-@XETEX_SYNCTEX_TRUE@am__append_152 = \
+@XETEX_SYNCTEX_TRUE@am__append_153 = \
@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_153 = $(synctex_tests)
+@SYNCTEX_TRUE@am__append_154 = $(synctex_tests)
subdir = .
ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
am__aclocal_m4_deps = $(top_srcdir)/m4/web2c-disable.m4 \
@@ -461,30 +464,32 @@ am__EXEEXT_4 = odvicopy$(EXEEXT) odvitype$(EXEEXT) otangle$(EXEEXT) \
@UPTEX_TRUE@am__EXEEXT_21 = uptex$(EXEEXT)
@UPWEB_TRUE@am__EXEEXT_22 = $(am__EXEEXT_3)
@EUPTEX_TRUE@am__EXEEXT_23 = euptex$(EXEEXT)
-@PDFTEX_TRUE@am__EXEEXT_24 = pdftex$(EXEEXT) ttf2afm$(EXEEXT) \
+@HITEX_TRUE@am__EXEEXT_24 = hitex$(EXEEXT) hishrink$(EXEEXT) \
+@HITEX_TRUE@ histretch$(EXEEXT)
+@PDFTEX_TRUE@am__EXEEXT_25 = pdftex$(EXEEXT) ttf2afm$(EXEEXT) \
@PDFTEX_TRUE@ pdftosrc$(EXEEXT)
-@LUATEX_TRUE@am__EXEEXT_25 = luatex$(EXEEXT)
-@LUAHBTEX_TRUE@am__EXEEXT_26 = luahbtex$(EXEEXT)
-@LUAJITTEX_TRUE@am__EXEEXT_27 = luajittex$(EXEEXT)
-@LUAJITHBTEX_TRUE@am__EXEEXT_28 = luajithbtex$(EXEEXT)
-@XETEX_TRUE@am__EXEEXT_29 = xetex$(EXEEXT)
-@OTANGLE_TRUE@am__EXEEXT_30 = $(am__EXEEXT_4)
-@ALEPH_TRUE@am__EXEEXT_31 = aleph$(EXEEXT)
-@SYNCTEX_TRUE@am__EXEEXT_32 = synctex$(EXEEXT)
+@LUATEX_TRUE@am__EXEEXT_26 = luatex$(EXEEXT)
+@LUAHBTEX_TRUE@am__EXEEXT_27 = luahbtex$(EXEEXT)
+@LUAJITTEX_TRUE@am__EXEEXT_28 = luajittex$(EXEEXT)
+@LUAJITHBTEX_TRUE@am__EXEEXT_29 = luajithbtex$(EXEEXT)
+@XETEX_TRUE@am__EXEEXT_30 = xetex$(EXEEXT)
+@OTANGLE_TRUE@am__EXEEXT_31 = $(am__EXEEXT_4)
+@ALEPH_TRUE@am__EXEEXT_32 = aleph$(EXEEXT)
+@SYNCTEX_TRUE@am__EXEEXT_33 = synctex$(EXEEXT)
am__installdirs = "$(DESTDIR)$(bindir)" "$(DESTDIR)$(libdir)" \
"$(DESTDIR)$(libdir)" "$(DESTDIR)$(man1dir)" \
"$(DESTDIR)$(man5dir)" "$(DESTDIR)$(pkgconfigdir)" \
"$(DESTDIR)$(syncincludedir)"
-@MF_TRUE@@WIN32_TRUE@am__EXEEXT_33 = call_mf$(EXEEXT)
-@MFLUA_TRUE@@WIN32_TRUE@am__EXEEXT_34 = call_mflua$(EXEEXT)
-@MFLUAJIT_TRUE@@WIN32_TRUE@am__EXEEXT_35 = call_mfluajit$(EXEEXT)
-@MP_TRUE@@WIN32_TRUE@am__EXEEXT_36 = call_mpost$(EXEEXT)
-@PMP_TRUE@@WIN32_TRUE@am__EXEEXT_37 = call_pmpost$(EXEEXT)
-@UPMP_TRUE@@WIN32_TRUE@am__EXEEXT_38 = call_upmpost$(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 = \
+@MF_TRUE@@WIN32_TRUE@am__EXEEXT_34 = call_mf$(EXEEXT)
+@MFLUA_TRUE@@WIN32_TRUE@am__EXEEXT_35 = call_mflua$(EXEEXT)
+@MFLUAJIT_TRUE@@WIN32_TRUE@am__EXEEXT_36 = call_mfluajit$(EXEEXT)
+@MP_TRUE@@WIN32_TRUE@am__EXEEXT_37 = call_mpost$(EXEEXT)
+@PMP_TRUE@@WIN32_TRUE@am__EXEEXT_38 = call_pmpost$(EXEEXT)
+@UPMP_TRUE@@WIN32_TRUE@am__EXEEXT_39 = call_upmpost$(EXEEXT)
+@LUATEX_TRUE@@WIN32_TRUE@am__EXEEXT_40 = call_luatex$(EXEEXT)
+@LUAHBTEX_TRUE@@WIN32_TRUE@am__EXEEXT_41 = call_luahbtex$(EXEEXT)
+@LUAJITTEX_TRUE@@WIN32_TRUE@am__EXEEXT_42 = call_luajittex$(EXEEXT)
+@LUAJITHBTEX_TRUE@@WIN32_TRUE@am__EXEEXT_43 = \
@LUAJITHBTEX_TRUE@@WIN32_TRUE@ call_luajithbtex$(EXEEXT)
PROGRAMS = $(bin_PROGRAMS) $(noinst_PROGRAMS)
am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`;
@@ -1382,12 +1387,27 @@ nodist_gftype_OBJECTS = gftype.$(OBJEXT)
gftype_OBJECTS = $(nodist_gftype_OBJECTS)
gftype_LDADD = $(LDADD)
gftype_DEPENDENCIES = $(proglib) $(am__DEPENDENCIES_1)
+nodist_hishrink_OBJECTS = hishrink-hformat.$(OBJEXT) \
+ hishrink-hishrink.$(OBJEXT) hishrink-shrink-lexer.$(OBJEXT) \
+ hishrink-shrink-parser.$(OBJEXT)
+hishrink_OBJECTS = $(nodist_hishrink_OBJECTS)
+hishrink_DEPENDENCIES = $(am__DEPENDENCIES_1)
+nodist_histretch_OBJECTS = histretch-hformat.$(OBJEXT) \
+ histretch-histretch.$(OBJEXT)
+histretch_OBJECTS = $(nodist_histretch_OBJECTS)
+histretch_DEPENDENCIES = $(am__DEPENDENCIES_1)
+am__objects_37 = hitex-htex.$(OBJEXT)
+am__objects_38 = hitex-hitex.$(OBJEXT)
+nodist_hitex_OBJECTS = hitex-hformat.$(OBJEXT) hitex-hput.$(OBJEXT) \
+ $(am__objects_37) $(am__objects_38)
+hitex_OBJECTS = $(nodist_hitex_OBJECTS)
+hitex_DEPENDENCIES = $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1)
nodist_initex_OBJECTS = initex-callexe.$(OBJEXT)
initex_OBJECTS = $(nodist_initex_OBJECTS)
initex_DEPENDENCIES =
-am__objects_37 = luatexdir/luahbtex-luatex.$(OBJEXT) \
+am__objects_39 = luatexdir/luahbtex-luatex.$(OBJEXT) \
mplibdir/luahbtex-lmplib.$(OBJEXT)
-nodist_luahbtex_OBJECTS = $(am__objects_37)
+nodist_luahbtex_OBJECTS = $(am__objects_39)
luahbtex_OBJECTS = $(nodist_luahbtex_OBJECTS)
am__DEPENDENCIES_6 = $(am__DEPENDENCIES_1)
am__DEPENDENCIES_7 = libmplibcore.a $(am__DEPENDENCIES_1) \
@@ -1398,16 +1418,16 @@ am__DEPENDENCIES_7 = libmplibcore.a $(am__DEPENDENCIES_1) \
luahbtex_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) \
$(LIBTOOLFLAGS) --mode=link $(CXXLD) $(luahbtex_CXXFLAGS) \
$(CXXFLAGS) $(luahbtex_LDFLAGS) $(LDFLAGS) -o $@
-am__objects_38 = luatexdir/luajithbtex-luatex.$(OBJEXT) \
+am__objects_40 = luatexdir/luajithbtex-luatex.$(OBJEXT) \
mplibdir/luajithbtex-lmplib.$(OBJEXT)
-nodist_luajithbtex_OBJECTS = $(am__objects_38)
+nodist_luajithbtex_OBJECTS = $(am__objects_40)
luajithbtex_OBJECTS = $(nodist_luajithbtex_OBJECTS)
luajithbtex_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) \
$(LIBTOOLFLAGS) --mode=link $(CXXLD) $(luajithbtex_CXXFLAGS) \
$(CXXFLAGS) $(luajithbtex_LDFLAGS) $(LDFLAGS) -o $@
-am__objects_39 = luatexdir/luajittex-luatex.$(OBJEXT) \
+am__objects_41 = luatexdir/luajittex-luatex.$(OBJEXT) \
mplibdir/luajittex-lmplib.$(OBJEXT)
-nodist_luajittex_OBJECTS = $(am__objects_39)
+nodist_luajittex_OBJECTS = $(am__objects_41)
luajittex_OBJECTS = $(nodist_luajittex_OBJECTS)
am__DEPENDENCIES_8 = libmplibcore.a $(am__DEPENDENCIES_1) \
$(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
@@ -1416,9 +1436,9 @@ am__DEPENDENCIES_8 = libmplibcore.a $(am__DEPENDENCIES_1) \
luajittex_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
$(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
$(luajittex_LDFLAGS) $(LDFLAGS) -o $@
-am__objects_40 = luatexdir/luatex-luatex.$(OBJEXT) \
+am__objects_42 = luatexdir/luatex-luatex.$(OBJEXT) \
mplibdir/luatex-lmplib.$(OBJEXT)
-nodist_luatex_OBJECTS = $(am__objects_40)
+nodist_luatex_OBJECTS = $(am__objects_42)
luatex_OBJECTS = $(nodist_luatex_OBJECTS)
luatex_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
$(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
@@ -1444,8 +1464,8 @@ mflua_DEPENDENCIES = libmflua.a libmfluaotfcc.a $(am__DEPENDENCIES_2) \
mflua_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
$(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
$(mflua_LDFLAGS) $(LDFLAGS) -o $@
-am__objects_41 = mfluadir/mflua_nowin-mfluaextra.$(OBJEXT)
-nodist_mflua_nowin_OBJECTS = $(am__objects_41)
+am__objects_43 = mfluadir/mflua_nowin-mfluaextra.$(OBJEXT)
+nodist_mflua_nowin_OBJECTS = $(am__objects_43)
mflua_nowin_OBJECTS = $(nodist_mflua_nowin_OBJECTS)
mflua_nowin_DEPENDENCIES = libmflua.a libmfluaotfcc.a \
$(am__DEPENDENCIES_2) $(windowlib) $(am__DEPENDENCIES_6)
@@ -1460,8 +1480,8 @@ mfluajit_DEPENDENCIES = libmfluajit.a libmfluaotfcc.a \
mfluajit_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
$(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
$(mfluajit_LDFLAGS) $(LDFLAGS) -o $@
-am__objects_42 = mfluajitdir/mfluajit_nowin-mfluajitextra.$(OBJEXT)
-nodist_mfluajit_nowin_OBJECTS = $(am__objects_42)
+am__objects_44 = mfluajitdir/mfluajit_nowin-mfluajitextra.$(OBJEXT)
+nodist_mfluajit_nowin_OBJECTS = $(am__objects_44)
mfluajit_nowin_OBJECTS = $(nodist_mfluajit_nowin_OBJECTS)
mfluajit_nowin_DEPENDENCIES = libmfluajit.a libmfluaotfcc.a \
$(am__DEPENDENCIES_2) $(windowlib) $(am__DEPENDENCIES_1)
@@ -1473,8 +1493,8 @@ nodist_mft_OBJECTS = mft.$(OBJEXT)
mft_OBJECTS = $(nodist_mft_OBJECTS)
mft_LDADD = $(LDADD)
mft_DEPENDENCIES = $(proglib) $(am__DEPENDENCIES_1)
-am__objects_43 = mpost-mpxout.$(OBJEXT)
-nodist_mpost_OBJECTS = mpost-mpost.$(OBJEXT) $(am__objects_43)
+am__objects_45 = mpost-mpxout.$(OBJEXT)
+nodist_mpost_OBJECTS = mpost-mpost.$(OBJEXT) $(am__objects_45)
mpost_OBJECTS = $(nodist_mpost_OBJECTS)
mpost_DEPENDENCIES = libmplibcore.a libmplibextramath.a \
libmplibbackends.a $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
@@ -1507,12 +1527,12 @@ am__dist_pdftex_SOURCES_DIST = pdftexdir/pdftexextra.c \
pdftexdir/etex_version.h synctexdir/synctex.c \
synctexdir/synctex.h synctexdir/synctex-common.h \
synctexdir/synctex-pdftex.h
-@PDFTEX_SYNCTEX_TRUE@am__objects_44 = \
+@PDFTEX_SYNCTEX_TRUE@am__objects_46 = \
@PDFTEX_SYNCTEX_TRUE@ synctexdir/pdftex-synctex.$(OBJEXT)
dist_pdftex_OBJECTS = pdftexdir/pdftex-pdftexextra.$(OBJEXT) \
- $(am__objects_44)
-am__objects_45 = pdftex-pdftexini.$(OBJEXT) pdftex-pdftex0.$(OBJEXT)
-nodist_pdftex_OBJECTS = $(am__objects_45) pdftex-pdftex-pool.$(OBJEXT)
+ $(am__objects_46)
+am__objects_47 = pdftex-pdftexini.$(OBJEXT) pdftex-pdftex0.$(OBJEXT)
+nodist_pdftex_OBJECTS = $(am__objects_47) pdftex-pdftex-pool.$(OBJEXT)
pdftex_OBJECTS = $(dist_pdftex_OBJECTS) $(nodist_pdftex_OBJECTS)
am__DEPENDENCIES_11 = $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
$(am__DEPENDENCIES_1) libmd5.a
@@ -1539,20 +1559,20 @@ nodist_pltotf_OBJECTS = pltotf.$(OBJEXT)
pltotf_OBJECTS = $(nodist_pltotf_OBJECTS)
pltotf_LDADD = $(LDADD)
pltotf_DEPENDENCIES = $(proglib) $(am__DEPENDENCIES_1)
-am__objects_46 = pmpost-pmp.$(OBJEXT)
-am__objects_47 = pmpost-pmpmath.$(OBJEXT)
-am__objects_48 = pmpost-pmpmathbinary.$(OBJEXT)
-am__objects_49 = pmpost-pmpmathdecimal.$(OBJEXT)
-am__objects_50 = pmpost-pmpmathdouble.$(OBJEXT)
-am__objects_51 = pmpost-pmpstrings.$(OBJEXT)
-am__objects_52 = pmpost-pmpxout.$(OBJEXT)
-am__objects_53 = pmpost-ppngout.$(OBJEXT)
-am__objects_54 = pmpost-ppsout.$(OBJEXT)
-am__objects_55 = pmpost-psvgout.$(OBJEXT)
-nodist_pmpost_OBJECTS = $(am__objects_46) $(am__objects_47) \
- $(am__objects_48) $(am__objects_49) $(am__objects_50) \
- pmpost-pmpost.$(OBJEXT) $(am__objects_51) $(am__objects_52) \
- $(am__objects_53) $(am__objects_54) $(am__objects_55) \
+am__objects_48 = pmpost-pmp.$(OBJEXT)
+am__objects_49 = pmpost-pmpmath.$(OBJEXT)
+am__objects_50 = pmpost-pmpmathbinary.$(OBJEXT)
+am__objects_51 = pmpost-pmpmathdecimal.$(OBJEXT)
+am__objects_52 = pmpost-pmpmathdouble.$(OBJEXT)
+am__objects_53 = pmpost-pmpstrings.$(OBJEXT)
+am__objects_54 = pmpost-pmpxout.$(OBJEXT)
+am__objects_55 = pmpost-ppngout.$(OBJEXT)
+am__objects_56 = pmpost-ppsout.$(OBJEXT)
+am__objects_57 = pmpost-psvgout.$(OBJEXT)
+nodist_pmpost_OBJECTS = $(am__objects_48) $(am__objects_49) \
+ $(am__objects_50) $(am__objects_51) $(am__objects_52) \
+ pmpost-pmpost.$(OBJEXT) $(am__objects_53) $(am__objects_54) \
+ $(am__objects_55) $(am__objects_56) $(am__objects_57) \
pmpost-ptfmin.$(OBJEXT)
pmpost_OBJECTS = $(nodist_pmpost_OBJECTS)
pmpost_DEPENDENCIES = $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
@@ -1570,10 +1590,10 @@ am__dist_ptex_SOURCES_DIST = ptexdir/ptexextra.c ptexdir/ptexextra.h \
ptexdir/ptex_version.h synctexdir/synctex.c \
synctexdir/synctex.h synctexdir/synctex-common.h \
synctexdir/synctex-ptex.h
-@PTEX_SYNCTEX_TRUE@am__objects_56 = synctexdir/ptex-synctex.$(OBJEXT)
-dist_ptex_OBJECTS = ptexdir/ptex-ptexextra.$(OBJEXT) $(am__objects_56)
-am__objects_57 = ptex-ptexini.$(OBJEXT) ptex-ptex0.$(OBJEXT)
-nodist_ptex_OBJECTS = $(am__objects_57) ptex-ptex-pool.$(OBJEXT)
+@PTEX_SYNCTEX_TRUE@am__objects_58 = synctexdir/ptex-synctex.$(OBJEXT)
+dist_ptex_OBJECTS = ptexdir/ptex-ptexextra.$(OBJEXT) $(am__objects_58)
+am__objects_59 = ptex-ptexini.$(OBJEXT) ptex-ptex0.$(OBJEXT)
+nodist_ptex_OBJECTS = $(am__objects_59) ptex-ptex-pool.$(OBJEXT)
ptex_OBJECTS = $(dist_ptex_OBJECTS) $(nodist_ptex_OBJECTS)
@PTEX_SYNCTEX_TRUE@am__DEPENDENCIES_12 = $(am__DEPENDENCIES_1)
am_ptftopl_OBJECTS =
@@ -1594,10 +1614,10 @@ tangleboot_LDADD = $(LDADD)
am__dist_tex_SOURCES_DIST = texextra.c synctexdir/synctex.c \
synctexdir/synctex.h synctexdir/synctex-common.h \
synctexdir/synctex-tex.h
-@TEX_SYNCTEX_TRUE@am__objects_58 = synctexdir/tex-synctex.$(OBJEXT)
-dist_tex_OBJECTS = tex-texextra.$(OBJEXT) $(am__objects_58)
-am__objects_59 = tex-texini.$(OBJEXT) tex-tex0.$(OBJEXT)
-nodist_tex_OBJECTS = $(am__objects_59) tex-tex-pool.$(OBJEXT)
+@TEX_SYNCTEX_TRUE@am__objects_60 = synctexdir/tex-synctex.$(OBJEXT)
+dist_tex_OBJECTS = tex-texextra.$(OBJEXT) $(am__objects_60)
+am__objects_61 = tex-texini.$(OBJEXT) tex-tex0.$(OBJEXT)
+nodist_tex_OBJECTS = $(am__objects_61) tex-tex-pool.$(OBJEXT)
tex_OBJECTS = $(dist_tex_OBJECTS) $(nodist_tex_OBJECTS)
@TEX_SYNCTEX_TRUE@am__DEPENDENCIES_14 = $(am__DEPENDENCIES_1)
tex_DEPENDENCIES = $(am__DEPENDENCIES_2) $(am__DEPENDENCIES_1) \
@@ -1622,21 +1642,21 @@ am_updvitype_OBJECTS =
nodist_updvitype_OBJECTS = updvitype-updvitype.$(OBJEXT)
updvitype_OBJECTS = $(am_updvitype_OBJECTS) \
$(nodist_updvitype_OBJECTS)
-am__objects_60 = upmpost-pmp.$(OBJEXT)
-am__objects_61 = upmpost-pmpmath.$(OBJEXT)
-am__objects_62 = upmpost-pmpmathbinary.$(OBJEXT)
-am__objects_63 = upmpost-pmpmathdecimal.$(OBJEXT)
-am__objects_64 = upmpost-pmpmathdouble.$(OBJEXT)
-am__objects_65 = upmpost-pmpstrings.$(OBJEXT)
-am__objects_66 = upmpost-pmpxout.$(OBJEXT)
-am__objects_67 = upmpost-ppngout.$(OBJEXT)
-am__objects_68 = upmpost-ppsout.$(OBJEXT)
-am__objects_69 = upmpost-psvgout.$(OBJEXT)
-am__objects_70 = $(am__objects_60) $(am__objects_61) $(am__objects_62) \
- $(am__objects_63) $(am__objects_64) upmpost-pmpost.$(OBJEXT) \
- $(am__objects_65) $(am__objects_66) $(am__objects_67) \
- $(am__objects_68) $(am__objects_69) upmpost-ptfmin.$(OBJEXT)
-nodist_upmpost_OBJECTS = $(am__objects_70)
+am__objects_62 = upmpost-pmp.$(OBJEXT)
+am__objects_63 = upmpost-pmpmath.$(OBJEXT)
+am__objects_64 = upmpost-pmpmathbinary.$(OBJEXT)
+am__objects_65 = upmpost-pmpmathdecimal.$(OBJEXT)
+am__objects_66 = upmpost-pmpmathdouble.$(OBJEXT)
+am__objects_67 = upmpost-pmpstrings.$(OBJEXT)
+am__objects_68 = upmpost-pmpxout.$(OBJEXT)
+am__objects_69 = upmpost-ppngout.$(OBJEXT)
+am__objects_70 = upmpost-ppsout.$(OBJEXT)
+am__objects_71 = upmpost-psvgout.$(OBJEXT)
+am__objects_72 = $(am__objects_62) $(am__objects_63) $(am__objects_64) \
+ $(am__objects_65) $(am__objects_66) upmpost-pmpost.$(OBJEXT) \
+ $(am__objects_67) $(am__objects_68) $(am__objects_69) \
+ $(am__objects_70) $(am__objects_71) upmpost-ptfmin.$(OBJEXT)
+nodist_upmpost_OBJECTS = $(am__objects_72)
upmpost_OBJECTS = $(nodist_upmpost_OBJECTS)
am__DEPENDENCIES_16 = $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
$(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
@@ -1650,12 +1670,12 @@ am__dist_uptex_SOURCES_DIST = uptexdir/uptexextra.c \
uptexdir/uptexextra.h uptexdir/uptex_version.h \
synctexdir/synctex.c synctexdir/synctex.h \
synctexdir/synctex-common.h synctexdir/synctex-uptex.h
-@UPTEX_SYNCTEX_TRUE@am__objects_71 = \
+@UPTEX_SYNCTEX_TRUE@am__objects_73 = \
@UPTEX_SYNCTEX_TRUE@ synctexdir/uptex-synctex.$(OBJEXT)
dist_uptex_OBJECTS = uptexdir/uptex-uptexextra.$(OBJEXT) \
- $(am__objects_71)
-am__objects_72 = uptex-uptexini.$(OBJEXT) uptex-uptex0.$(OBJEXT)
-nodist_uptex_OBJECTS = $(am__objects_72) uptex-uptex-pool.$(OBJEXT)
+ $(am__objects_73)
+am__objects_74 = uptex-uptexini.$(OBJEXT) uptex-uptex0.$(OBJEXT)
+nodist_uptex_OBJECTS = $(am__objects_74) uptex-uptex-pool.$(OBJEXT)
uptex_OBJECTS = $(dist_uptex_OBJECTS) $(nodist_uptex_OBJECTS)
@UPTEX_SYNCTEX_TRUE@am__DEPENDENCIES_17 = $(am__DEPENDENCIES_1)
am_uptftopl_OBJECTS =
@@ -1694,12 +1714,12 @@ am__dist_xetex_SOURCES_DIST = xetexdir/xetexextra.c \
xetexdir/xetex_version.h synctexdir/synctex.c \
synctexdir/synctex.h synctexdir/synctex-common.h \
synctexdir/synctex-xetex.h
-@XETEX_SYNCTEX_TRUE@am__objects_73 = \
+@XETEX_SYNCTEX_TRUE@am__objects_75 = \
@XETEX_SYNCTEX_TRUE@ synctexdir/xetex-synctex.$(OBJEXT)
dist_xetex_OBJECTS = xetexdir/xetex-xetexextra.$(OBJEXT) \
- $(am__objects_73)
-am__objects_74 = xetex-xetexini.$(OBJEXT) xetex-xetex0.$(OBJEXT)
-nodist_xetex_OBJECTS = $(am__objects_74) xetex-xetex-pool.$(OBJEXT)
+ $(am__objects_75)
+am__objects_76 = xetex-xetexini.$(OBJEXT) xetex-xetex0.$(OBJEXT)
+nodist_xetex_OBJECTS = $(am__objects_76) xetex-xetex-pool.$(OBJEXT)
xetex_OBJECTS = $(dist_xetex_OBJECTS) $(nodist_xetex_OBJECTS)
@XETEX_MACOSX_FALSE@am__DEPENDENCIES_18 = $(am__DEPENDENCIES_1)
am__DEPENDENCIES_19 = $(libxetex) $(am__DEPENDENCIES_1) \
@@ -1750,7 +1770,15 @@ am__depfiles_remade = ./$(DEPDIR)/aleph-aleph-pool.Po \
./$(DEPDIR)/euptex-euptex-pool.Po \
./$(DEPDIR)/euptex-euptex0.Po ./$(DEPDIR)/euptex-euptexini.Po \
./$(DEPDIR)/gftodvi.Po ./$(DEPDIR)/gftopk.Po \
- ./$(DEPDIR)/gftype.Po ./$(DEPDIR)/initex-callexe.Po \
+ ./$(DEPDIR)/gftype.Po ./$(DEPDIR)/hishrink-hformat.Po \
+ ./$(DEPDIR)/hishrink-hishrink.Po \
+ ./$(DEPDIR)/hishrink-shrink-lexer.Po \
+ ./$(DEPDIR)/hishrink-shrink-parser.Po \
+ ./$(DEPDIR)/histretch-hformat.Po \
+ ./$(DEPDIR)/histretch-histretch.Po \
+ ./$(DEPDIR)/hitex-hformat.Po ./$(DEPDIR)/hitex-hitex.Po \
+ ./$(DEPDIR)/hitex-hput.Po ./$(DEPDIR)/hitex-htex.Po \
+ ./$(DEPDIR)/initex-callexe.Po \
./$(DEPDIR)/libluahbtexspecific_a-luainit-hb.Po \
./$(DEPDIR)/libluahbtexspecific_a-luastuff-hb.Po \
./$(DEPDIR)/libluahbtexspecific_a-printing-hb.Po \
@@ -2433,6 +2461,15 @@ AM_V_CXXLD = $(am__v_CXXLD_@AM_V@)
am__v_CXXLD_ = $(am__v_CXXLD_@AM_DEFAULT_V@)
am__v_CXXLD_0 = @echo " CXXLD " $@;
am__v_CXXLD_1 =
+@MAINTAINER_MODE_FALSE@am__skiplex = test -f $@ ||
+LEXCOMPILE = $(LEX) $(AM_LFLAGS) $(LFLAGS)
+LTLEXCOMPILE = $(LIBTOOL) $(AM_V_lt) $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=compile $(LEX) $(AM_LFLAGS) $(LFLAGS)
+AM_V_LEX = $(am__v_LEX_@AM_V@)
+am__v_LEX_ = $(am__v_LEX_@AM_DEFAULT_V@)
+am__v_LEX_0 = @echo " LEX " $@;
+am__v_LEX_1 =
+YLWRAP = $(top_srcdir)/../../build-aux/ylwrap
OBJCXXCOMPILE = $(OBJCXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \
$(AM_CPPFLAGS) $(CPPFLAGS) $(AM_OBJCXXFLAGS) $(OBJCXXFLAGS)
LTOBJCXXCOMPILE = $(LIBTOOL) $(AM_V_lt) $(AM_LIBTOOLFLAGS) \
@@ -2451,6 +2488,16 @@ AM_V_OBJCXXLD = $(am__v_OBJCXXLD_@AM_V@)
am__v_OBJCXXLD_ = $(am__v_OBJCXXLD_@AM_DEFAULT_V@)
am__v_OBJCXXLD_0 = @echo " OBJCXXLD" $@;
am__v_OBJCXXLD_1 =
+@MAINTAINER_MODE_FALSE@am__skipyacc = test -f $@ ||
+am__yacc_c2h = sed -e s/cc$$/hh/ -e s/cpp$$/hpp/ -e s/cxx$$/hxx/ \
+ -e s/c++$$/h++/ -e s/c$$/h/
+YACCCOMPILE = $(YACC) $(AM_YFLAGS) $(YFLAGS)
+LTYACCCOMPILE = $(LIBTOOL) $(AM_V_lt) $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=compile $(YACC) $(AM_YFLAGS) $(YFLAGS)
+AM_V_YACC = $(am__v_YACC_@AM_V@)
+am__v_YACC_ = $(am__v_YACC_@AM_DEFAULT_V@)
+am__v_YACC_0 = @echo " YACC " $@;
+am__v_YACC_1 =
SOURCES = $(libff_a_SOURCES) $(libkanji_a_SOURCES) \
$(nodist_libluaffi_a_SOURCES) $(libluaharfbuzz_a_SOURCES) \
$(nodist_libluahbtexspecific_a_SOURCES) \
@@ -2493,8 +2540,10 @@ SOURCES = $(libff_a_SOURCES) $(libkanji_a_SOURCES) \
$(dist_euptex_SOURCES) $(nodist_euptex_SOURCES) \
$(gftodvi_SOURCES) $(nodist_gftodvi_SOURCES) \
$(nodist_gftopk_SOURCES) $(nodist_gftype_SOURCES) \
- $(nodist_initex_SOURCES) $(nodist_luahbtex_SOURCES) \
- $(nodist_EXTRA_luahbtex_SOURCES) $(nodist_luajithbtex_SOURCES) \
+ $(nodist_hishrink_SOURCES) $(nodist_histretch_SOURCES) \
+ $(nodist_hitex_SOURCES) $(nodist_initex_SOURCES) \
+ $(nodist_luahbtex_SOURCES) $(nodist_EXTRA_luahbtex_SOURCES) \
+ $(nodist_luajithbtex_SOURCES) \
$(nodist_EXTRA_luajithbtex_SOURCES) \
$(nodist_luajittex_SOURCES) $(nodist_luatex_SOURCES) \
$(md5main_SOURCES) $(dist_mf_SOURCES) $(dist_mf_nowin_SOURCES) \
@@ -2755,14 +2804,14 @@ am__set_TESTS_bases = \
bases=`echo $$bases`
AM_TESTSUITE_SUMMARY_HEADER = ' for $(PACKAGE_STRING)'
RECHECK_LOGS = $(TEST_LOGS)
-am__EXEEXT_43 = bibtex.test dvicopy.test dvitype.test gftodvi.test \
+am__EXEEXT_44 = 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
-am__EXEEXT_44 = $(am__EXEEXT_43) tests/bibtex-openout-test.pl \
+am__EXEEXT_45 = $(am__EXEEXT_44) 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_45 = $(am__EXEEXT_44)
+@WEB_TRUE@am__EXEEXT_46 = $(am__EXEEXT_45)
TEST_SUITE_LOG = test-suite.log
am__test_logs1 = $(TESTS:=.log)
am__test_logs2 = $(am__test_logs1:@EXEEXT@.log=.log)
@@ -2790,7 +2839,8 @@ am__DIST_COMMON = $(dist_man_MANS) $(srcdir)/../../am/bin_links.am \
$(srcdir)/am/cweb.am $(srcdir)/am/texmf.am $(srcdir)/am/web.am \
$(srcdir)/c-auto.in $(srcdir)/ctangleboot-sh.in \
$(srcdir)/eptexdir/am/eptex.am $(srcdir)/etexdir/am/etex.am \
- $(srcdir)/euptexdir/am/euptex.am $(srcdir)/libmd5/am/md5.am \
+ $(srcdir)/euptexdir/am/euptex.am \
+ $(srcdir)/hitexdir/am/hitex.am $(srcdir)/libmd5/am/md5.am \
$(srcdir)/luatexdir/am/libluatex.am \
$(srcdir)/luatexdir/am/libunilib.am \
$(srcdir)/luatexdir/am/luaffi.am \
@@ -2822,6 +2872,7 @@ am__DIST_COMMON = $(dist_man_MANS) $(srcdir)/../../am/bin_links.am \
$(top_srcdir)/../../build-aux/ltmain.sh \
$(top_srcdir)/../../build-aux/missing \
$(top_srcdir)/../../build-aux/test-driver \
+ $(top_srcdir)/../../build-aux/ylwrap \
$(top_srcdir)/../texlive/w32_wrapper/callexe.c \
$(top_srcdir)/luatexdir/luafontloader/ff-config.in \
$(top_srcdir)/synctexdir/synctex.pc.in ../../build-aux/ar-lib \
@@ -2985,7 +3036,7 @@ NM = @NM@
NMEDIT = @NMEDIT@
OBJCXX = @OBJCXX@
OBJCXXDEPMODE = @OBJCXXDEPMODE@
-OBJCXXFLAGS = @OBJCXXFLAGS@ $(am__append_110)
+OBJCXXFLAGS = @OBJCXXFLAGS@ $(am__append_111)
OBJDUMP = @OBJDUMP@
OBJEXT = @OBJEXT@
OTANGLE = @OTANGLE@
@@ -3127,8 +3178,8 @@ EXTRA_LIBRARIES = libmf.a libmflua.a libmfluaotfcc.a libmfluaotfcc.a \
libluajithbtexspecific.a libluaffi.a libluaharfbuzz.a \
libluajitharfbuzz.a libxetex.a libsynctex.a libmd5.a
EXTRA_LTLIBRARIES = libsynctex.la
-lib_LIBRARIES = $(am__append_122)
-lib_LTLIBRARIES = $(am__append_121)
+lib_LIBRARIES = $(am__append_123)
+lib_LTLIBRARIES = $(am__append_122)
dist_man_MANS = synctexdir/man1/synctex.1 synctexdir/man5/synctex.5
nodist_man_MANS =
TEST_EXTENSIONS = .pl .test
@@ -3245,21 +3296,21 @@ EXTRA_DIST = PROJECTS cftests cpascal.h help.h w2c/config.h \
euptexdir/euptrip/euptrip.diffs euptexdir/euptrip/texmf.cnf \
euptexdir/pdfprimitive.test \
eptexdir/tests/pdfprimitive-test.tex \
- euptexdir/tests/pdfprimitive-euptex.log \
- 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/pdfprimitive-euptex.log hitexdir/ChangeLog \
+ $(hitex_web) lex.sed yacc.sed 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 \
@@ -3568,7 +3619,10 @@ DISTCLEANFILES = CXXLD.sh tangle.c tangle.h tangle.p tangle-web2c \
uptests/yuparse.* uptests/ygkhuge*.* uptrip.diffs \
$(nodist_euptex_SOURCES) euptex.web euptex.ch euptex-web2c \
euptex.p euptex.pool euptex-tangle euptrip.diffs \
- pdfprimitive-euptex.* $(nodist_pdftex_SOURCES) pdftex-final.ch \
+ pdfprimitive-euptex.* $(nodist_hitex_SOURCES) \
+ $(nodist_hishrink_SOURCES) $(nodist_histretch_SOURCES) \
+ format-tangle htex-tangle hitex-tangle shrink-parser.* \
+ shrink-lexer.* $(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 postV3.afm \
@@ -3607,16 +3661,16 @@ TRIPTRAP = $(am__append_7) $(am__append_17) $(am__append_26) \
$(am__append_79)
bin_links = $(am__append_5) $(am__append_14) $(am__append_15) \
$(am__append_24) $(am__append_33) $(am__append_41) \
- $(am__append_49) $(am__append_54) $(am__append_88) \
- $(am__append_93) $(am__append_98) $(am__append_103)
+ $(am__append_49) $(am__append_54) $(am__append_89) \
+ $(am__append_94) $(am__append_99) $(am__append_104)
install_exe_links = $(am__append_12) $(am__append_22) $(am__append_31) \
$(am__append_39) $(am__append_47) $(am__append_52) \
- $(am__append_86) $(am__append_91) $(am__append_96) \
- $(am__append_101)
+ $(am__append_87) $(am__append_92) $(am__append_97) \
+ $(am__append_102)
uninstall_exe_links = $(am__append_13) $(am__append_23) \
$(am__append_32) $(am__append_40) $(am__append_48) \
- $(am__append_53) $(am__append_87) $(am__append_92) \
- $(am__append_97) $(am__append_102)
+ $(am__append_53) $(am__append_88) $(am__append_93) \
+ $(am__append_98) $(am__append_103)
NEVER_DIST = `find . $(NEVER_NAMES)` cwebdir/cweave.log \
cwebdir/cweave.trs cwebdir/ctwill.log cwebdir/ctwill.trs \
cwebdir/refsort.log cwebdir/refsort.trs cwebdir/twinx.log \
@@ -3787,18 +3841,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_125)
+tex_CPPFLAGS = $(AM_CPPFLAGS) $(am__append_126)
# With --enable-ipc, TeX may need to link with -lsocket.
-tex_LDADD = $(LDADD) $(ipc_socketlibs) $(am__append_126)
+tex_LDADD = $(LDADD) $(ipc_socketlibs) $(am__append_127)
# 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_128)
+dist_tex_SOURCES = texextra.c $(am__append_129)
# We must create texd.h before building the tex_OBJECTS.
-tex_prereq = texd.h $(am__append_127)
+tex_prereq = texd.h $(am__append_128)
tex_ch_srcs = \
tex.web \
tex.ch \
@@ -4274,21 +4328,21 @@ libmplib_web = mplibdir/mp.w mplibdir/psout.w mplibdir/svgout.w \
mplibdir/pngout.w mplibdir/mpmath.w mplibdir/mpmathbinary.w \
mplibdir/mpmathdecimal.w mplibdir/mpmathdouble.w \
mplibdir/mpstrings.w mplibdir/tfmin.w
-etex_CPPFLAGS = $(AM_CPPFLAGS) $(ZLIB_INCLUDES) $(am__append_129)
+etex_CPPFLAGS = $(AM_CPPFLAGS) $(ZLIB_INCLUDES) $(am__append_130)
# With --enable-ipc, e-TeX may need to link with -lsocket.
-etex_LDADD = $(LDADD) $(ZLIB_LIBS) $(ipc_socketlibs) $(am__append_130)
+etex_LDADD = $(LDADD) $(ZLIB_LIBS) $(ipc_socketlibs) $(am__append_131)
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_132)
+ etexdir/etex_version.h $(am__append_133)
# 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_131)
+etex_prereq = etexd.h etexdir/etex_version.h $(am__append_132)
etex_web_srcs = \
tex.web \
etexdir/etex.ch
@@ -4317,20 +4371,20 @@ pweb_programs = pbibtex pdvitype ppltotf ptftopl
pproglib = lib/libp.a
libkanji_a_SOURCES = ptexdir/kanji.c ptexdir/kanji.h ptexdir/kanji_dump.c
libkanji_a_CPPFLAGS = $(ptex_cppflags)
-ptex_CPPFLAGS = $(ptex_cppflags) $(am__append_133)
+ptex_CPPFLAGS = $(ptex_cppflags) $(am__append_134)
# With --enable-ipc, pTeX may need to link with -lsocket.
-ptex_LDADD = $(ptex_ldadd) $(ipc_socketlibs) $(am__append_134)
+ptex_LDADD = $(ptex_ldadd) $(ipc_socketlibs) $(am__append_135)
ptex_DEPENDENCIES = $(ptex_dependencies)
# pTeX C sources
ptex_c_h = ptexini.c ptex0.c ptexcoerce.h ptexd.h
nodist_ptex_SOURCES = $(ptex_c_h) ptex-pool.c
dist_ptex_SOURCES = ptexdir/ptexextra.c ptexdir/ptexextra.h \
- ptexdir/ptex_version.h $(am__append_136)
+ ptexdir/ptex_version.h $(am__append_137)
# We must create ptexd.h and ptexdir/ptex_version.h before building the ptex_OBJECTS.
-ptex_prereq = ptexd.h ptexdir/ptex_version.h $(am__append_135)
+ptex_prereq = ptexd.h ptexdir/ptex_version.h $(am__append_136)
ptex_web_srcs = \
tex.web \
tex.ch \
@@ -4374,22 +4428,22 @@ pweb_tests = ptexdir/pbibtex.test ptexdir/pdvitype.test \
ptexdir/nissya.test ptexdir/sample.test ptexdir/yokotate.test \
ptexdir/skipjfmp.test
eptex_CPPFLAGS = $(PTEXENC_INCLUDES) $(AM_CPPFLAGS) -I$(srcdir)/libmd5 \
- $(ZLIB_INCLUDES) $(am__append_141)
+ $(ZLIB_INCLUDES) $(am__append_142)
# With --enable-ipc, e-pTeX may need to link with -lsocket.
eptex_LDADD = libkanji.a $(pproglib) $(PTEXENC_LIBS) $(LDADD) \
- $(ipc_socketlibs) libmd5.a $(ZLIB_LIBS) $(am__append_142)
+ $(ipc_socketlibs) libmd5.a $(ZLIB_LIBS) $(am__append_143)
eptex_DEPENDENCIES = libkanji.a $(pproglib) $(PTEXENC_DEPEND) $(default_dependencies) libmd5.a $(ZLIB_DEPEND)
# e-pTeX C sources
eptex_c_h = eptexini.c eptex0.c eptexcoerce.h eptexd.h
nodist_eptex_SOURCES = $(eptex_c_h) eptex-pool.c
dist_eptex_SOURCES = eptexdir/eptexextra.c eptexdir/eptexextra.h \
- eptexdir/eptex_version.h $(am__append_144)
+ eptexdir/eptex_version.h $(am__append_145)
# We must create eptexd.h and eptexdir/eptex_version.h before building the eptex_OBJECTS.
eptex_prereq = eptexd.h etexdir/etex_version.h ptexdir/ptex_version.h \
- eptexdir/eptex_version.h $(am__append_143)
+ eptexdir/eptex_version.h $(am__append_144)
eptex_web_srcs = \
tex.web \
etexdir/etex.ch \
@@ -4423,21 +4477,21 @@ up_tangle = $(tangle_silent)WEBINPUTS=.:$(srcdir)/uptexdir:$(srcdir) $(buildenv)
upweb_programs = upbibtex updvitype uppltotf uptftopl
libukanji_a_SOURCES = uptexdir/kanji.c uptexdir/kanji.h uptexdir/kanji_dump.c
libukanji_a_CPPFLAGS = $(uptex_cppflags)
-uptex_CPPFLAGS = $(uptex_cppflags) $(am__append_137)
+uptex_CPPFLAGS = $(uptex_cppflags) $(am__append_138)
# With --enable-ipc, upTeX may need to link with -lsocket.
-uptex_LDADD = $(uptex_ldadd) $(ipc_socketlibs) $(am__append_138)
+uptex_LDADD = $(uptex_ldadd) $(ipc_socketlibs) $(am__append_139)
uptex_DEPENDENCIES = $(uptex_dependencies)
# upTeX C sources
uptex_c_h = uptexini.c uptex0.c uptexcoerce.h uptexd.h
nodist_uptex_SOURCES = $(uptex_c_h) uptex-pool.c
dist_uptex_SOURCES = uptexdir/uptexextra.c uptexdir/uptexextra.h \
- uptexdir/uptex_version.h $(am__append_140)
+ uptexdir/uptex_version.h $(am__append_141)
# We must create uptexd.h and uptexdir/uptex_version.h before building the uptex_OBJECTS.
uptex_prereq = uptexd.h ptexdir/ptex_version.h \
- uptexdir/uptex_version.h $(am__append_139)
+ uptexdir/uptex_version.h $(am__append_140)
uptex_web_srcs = \
tex.web \
tex.ch \
@@ -4485,23 +4539,23 @@ upweb_tests = \
uptexdir/gkhuge.test
euptex_CPPFLAGS = $(PTEXENC_INCLUDES) $(AM_CPPFLAGS) \
- -I$(srcdir)/libmd5 $(ZLIB_INCLUDES) $(am__append_145)
+ -I$(srcdir)/libmd5 $(ZLIB_INCLUDES) $(am__append_146)
# 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_146)
+ $(ipc_socketlibs) libmd5.a $(ZLIB_LIBS) $(am__append_147)
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_148)
+ $(am__append_149)
# 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 \
ptexdir/ptex_version.h eptexdir/eptex_version.h \
- uptexdir/uptex_version.h $(am__append_147)
+ uptexdir/uptex_version.h $(am__append_148)
euptex_web_srcs = \
tex.web \
etexdir/etex.ch \
@@ -4532,6 +4586,47 @@ euptex_ch_srcs = \
# e-upTeX Tests
#
euptex_tests = euptexdir/euptriptest.test euptexdir/pdfprimitive.test euptexdir/eupver.test
+
+#kb hitex_CFLAGS = $(AM_CFLAGS) \
+#kb -Wno-parentheses -Wno-maybe-uninitialized -Wno-unused-function -Wno-unused-variable
+hitex_CPPFLAGS = $(AM_CPPFLAGS) $(ZLIB_INCLUDES)
+hitex_LDADD = $(KPATHSEA_LIBS) $(ZLIB_LIBS)
+#
+histretch_CPPFLAGS = $(AM_CPPFLAGS) $(ZLIB_INCLUDES)
+histretch_LDADD = $(ZLIB_LIBS)
+#
+hishrink_CPPFLAGS = $(AM_CPPFLAGS) $(ZLIB_INCLUDES)
+hishrink_LDADD = $(ZLIB_LIBS)
+
+# We want debugging for the parser, and we use features not in POSIX yacc.
+# I guess the result is that we assume bison? Not sure if that's ok.
+AM_YFLAGS = -d -v -Wno-yacc
+
+# HiTeX CWEB sources
+hitex_web = hitexdir/format.w hitexdir/htex.w hitexdir/hitex.w
+
+# Creating several files: need stamp file and two rules with identical recipes
+hi_ctangle_sh = CWEBINPUTS=$(srcdir)/hitexdir AM_V_P=$(AM_V_P) $(SHELL) ./tangle-sh $@ $(CTANGLE)
+
+# HiTeX C/yacc/lex sources generated using ctangle.
+# For each cweb (.w) source file, list the files generated.
+format_c_h_l_y = basetypes.h error.h hformat.h \
+ mkhformat.c \
+ hput.c hput.h \
+ hget.c hget.h \
+ shrink.lex-in shrink.yacc-in \
+ hishrink.c histretch.c
+
+htex_c_h = htex.c htex.h
+hitex_c_h = hitex.c hitex.h
+
+# Other web2c/*dir/am/*.am engine Makefile fragments define a bunch of
+# _OBJECTS variables. This does not seem right to me. Instead, we
+# correctly define the _SOURCES.
+#
+nodist_hitex_SOURCES = hformat.c hput.c $(htex_c_h) $(hitex_c_h)
+nodist_histretch_SOURCES = hformat.c histretch.c
+nodist_hishrink_SOURCES = hformat.c hishrink.c shrink-lexer.l shrink-parser.y
libpdftex_a_CPPFLAGS = $(REGEX_INCLUDES) $(pdftex_cppflags)
libpdftex_a_CXXFLAGS = $(WARNING_CXXFLAGS)
libpdftex_a_SOURCES = pdftexdir/avl.c pdftexdir/avl.h \
@@ -4545,7 +4640,7 @@ libpdftex_a_SOURCES = pdftexdir/avl.c pdftexdir/avl.h \
pdftexdir/writejbig2.c pdftexdir/writejpg.c \
pdftexdir/writepng.c pdftexdir/writet1.c pdftexdir/writet3.c \
pdftexdir/writettf.c pdftexdir/writettf.h pdftexdir/writezip.c \
- $(am__append_81)
+ $(am__append_82)
@MINGW32_TRUE@REGEX_INCLUDES = -I$(srcdir)/pdftexdir/regex
EXTRA_libpdftex_a_SOURCES = pdftexdir/macnames.c \
pdftexdir/regex/regcomp.c pdftexdir/regex/regex_internal.c \
@@ -4561,7 +4656,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_149)
+pdftex_CPPFLAGS = $(pdftex_cppflags) $(am__append_150)
pdftex_CXXFLAGS = $(WARNING_CXXFLAGS)
# With --enable-ipc, pdfTeX may need to link with -lsocket.
@@ -4574,7 +4669,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_150)
+ $(am__append_151)
pdftex_ch_srcs = \
pdftexdir/pdftex.web \
pdftexdir/tex.ch0 \
@@ -5248,18 +5343,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_109) \
- $(am__append_111)
+ $(PPLIB_INCLUDES) -I$(srcdir)/libmd5 $(am__append_110) \
+ $(am__append_112)
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_112)
+ $(am__append_113)
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_151)
+xetex_CPPFLAGS = $(xetex_cppflags) $(am__append_152)
xetex_CFLAGS = $(WARNING_CFLAGS)
xetex_CXXFLAGS = # $(WARNING_CXXFLAGS)
xetex_LDADD = $(xetex_ldadd) $(LDADD) $(ipc_socketlibs)
@@ -5268,7 +5363,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_152)
+ $(am__append_153)
xetex_ch_srcs = \
xetexdir/xetex.web \
xetexdir/tex.ch0 \
@@ -5299,7 +5394,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_113) $(am__append_114)
+ xetexdir/image/pngimage.h $(am__append_114) $(am__append_115)
# We must create xetexd.h etc. before building the libxetex_a_OBJECTS.
libxetex_prereq = xetexd.h $(xetex_dependencies)
@@ -5404,12 +5499,12 @@ synctex_SOURCES = \
synctexdir/synctex_main.c
synctex_CPPFLAGS = -I$(srcdir)/synctexdir
-synctex_LDADD = $(libsynctex) $(ZLIB_LIBS) $(am__append_123)
+synctex_LDADD = $(libsynctex) $(ZLIB_LIBS) $(am__append_124)
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_124)
+libsynctex_la_LIBADD = $(ZLIB_LIBS) $(am__append_125)
libsynctex_la_SOURCES = \
synctexdir/synctex_parser.c \
synctexdir/synctex_parser_local.h \
@@ -5523,10 +5618,10 @@ all: ff-config.h
$(MAKE) $(AM_MAKEFLAGS) all-recursive
.SUFFIXES:
-.SUFFIXES: .c .cc .cin .cpp .cxx .h .hin .lo .log .mm .o .obj .p .pin .pl .pl$(EXEEXT) .test .test$(EXEEXT) .trs
+.SUFFIXES: .c .cc .cin .cpp .cxx .h .hin .l .lo .log .mm .o .obj .p .pin .pl .pl$(EXEEXT) .test .test$(EXEEXT) .trs .y
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)/mfluajitdir/am/mfluajit.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)/eptexdir/am/eptex.am $(srcdir)/uptexdir/am/uptex.am $(srcdir)/euptexdir/am/euptex.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)/mfluajitdir/am/mfluajit.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)/eptexdir/am/eptex.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)
@for dep in $?; do \
case '$(am__configure_deps)' in \
*$$dep*) \
@@ -5548,7 +5643,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)/mfluajitdir/am/mfluajit.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)/eptexdir/am/eptex.am $(srcdir)/uptexdir/am/uptex.am $(srcdir)/euptexdir/am/euptex.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)/mfluajitdir/am/mfluajit.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)/eptexdir/am/eptex.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):
$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
$(SHELL) ./config.status --recheck
@@ -7950,6 +8045,21 @@ gftopk$(EXEEXT): $(gftopk_OBJECTS) $(gftopk_DEPENDENCIES) $(EXTRA_gftopk_DEPENDE
gftype$(EXEEXT): $(gftype_OBJECTS) $(gftype_DEPENDENCIES) $(EXTRA_gftype_DEPENDENCIES)
@rm -f gftype$(EXEEXT)
$(AM_V_CCLD)$(LINK) $(gftype_OBJECTS) $(gftype_LDADD) $(LIBS)
+shrink-parser.h: shrink-parser.c
+ @if test ! -f $@; then rm -f shrink-parser.c; else :; fi
+ @if test ! -f $@; then $(MAKE) $(AM_MAKEFLAGS) shrink-parser.c; else :; fi
+
+hishrink$(EXEEXT): $(hishrink_OBJECTS) $(hishrink_DEPENDENCIES) $(EXTRA_hishrink_DEPENDENCIES)
+ @rm -f hishrink$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(hishrink_OBJECTS) $(hishrink_LDADD) $(LIBS)
+
+histretch$(EXEEXT): $(histretch_OBJECTS) $(histretch_DEPENDENCIES) $(EXTRA_histretch_DEPENDENCIES)
+ @rm -f histretch$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(histretch_OBJECTS) $(histretch_LDADD) $(LIBS)
+
+hitex$(EXEEXT): $(hitex_OBJECTS) $(hitex_DEPENDENCIES) $(EXTRA_hitex_DEPENDENCIES)
+ @rm -f hitex$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(hitex_OBJECTS) $(hitex_LDADD) $(LIBS)
initex$(EXEEXT): $(initex_OBJECTS) $(initex_DEPENDENCIES) $(EXTRA_initex_DEPENDENCIES)
@rm -f initex$(EXEEXT)
@@ -8331,6 +8441,16 @@ distclean-compile:
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gftodvi.Po@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gftopk.Po@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gftype.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hishrink-hformat.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hishrink-hishrink.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hishrink-shrink-lexer.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hishrink-shrink-parser.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/histretch-hformat.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/histretch-histretch.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hitex-hformat.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hitex-hitex.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hitex-hput.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hitex-htex.Po@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/initex-callexe.Po@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libluahbtexspecific_a-luainit-hb.Po@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libluahbtexspecific_a-luastuff-hb.Po@am__quote@ # am--include-marker
@@ -17248,6 +17368,146 @@ euptex-euptex-pool.obj: euptex-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) $(euptex_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o euptex-euptex-pool.obj `if test -f 'euptex-pool.c'; then $(CYGPATH_W) 'euptex-pool.c'; else $(CYGPATH_W) '$(srcdir)/euptex-pool.c'; fi`
+hishrink-hformat.o: hformat.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(hishrink_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT hishrink-hformat.o -MD -MP -MF $(DEPDIR)/hishrink-hformat.Tpo -c -o hishrink-hformat.o `test -f 'hformat.c' || echo '$(srcdir)/'`hformat.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/hishrink-hformat.Tpo $(DEPDIR)/hishrink-hformat.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='hformat.c' object='hishrink-hformat.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) $(hishrink_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o hishrink-hformat.o `test -f 'hformat.c' || echo '$(srcdir)/'`hformat.c
+
+hishrink-hformat.obj: hformat.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(hishrink_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT hishrink-hformat.obj -MD -MP -MF $(DEPDIR)/hishrink-hformat.Tpo -c -o hishrink-hformat.obj `if test -f 'hformat.c'; then $(CYGPATH_W) 'hformat.c'; else $(CYGPATH_W) '$(srcdir)/hformat.c'; fi`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/hishrink-hformat.Tpo $(DEPDIR)/hishrink-hformat.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='hformat.c' object='hishrink-hformat.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) $(hishrink_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o hishrink-hformat.obj `if test -f 'hformat.c'; then $(CYGPATH_W) 'hformat.c'; else $(CYGPATH_W) '$(srcdir)/hformat.c'; fi`
+
+hishrink-hishrink.o: hishrink.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(hishrink_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT hishrink-hishrink.o -MD -MP -MF $(DEPDIR)/hishrink-hishrink.Tpo -c -o hishrink-hishrink.o `test -f 'hishrink.c' || echo '$(srcdir)/'`hishrink.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/hishrink-hishrink.Tpo $(DEPDIR)/hishrink-hishrink.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='hishrink.c' object='hishrink-hishrink.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) $(hishrink_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o hishrink-hishrink.o `test -f 'hishrink.c' || echo '$(srcdir)/'`hishrink.c
+
+hishrink-hishrink.obj: hishrink.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(hishrink_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT hishrink-hishrink.obj -MD -MP -MF $(DEPDIR)/hishrink-hishrink.Tpo -c -o hishrink-hishrink.obj `if test -f 'hishrink.c'; then $(CYGPATH_W) 'hishrink.c'; else $(CYGPATH_W) '$(srcdir)/hishrink.c'; fi`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/hishrink-hishrink.Tpo $(DEPDIR)/hishrink-hishrink.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='hishrink.c' object='hishrink-hishrink.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) $(hishrink_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o hishrink-hishrink.obj `if test -f 'hishrink.c'; then $(CYGPATH_W) 'hishrink.c'; else $(CYGPATH_W) '$(srcdir)/hishrink.c'; fi`
+
+hishrink-shrink-lexer.o: shrink-lexer.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(hishrink_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT hishrink-shrink-lexer.o -MD -MP -MF $(DEPDIR)/hishrink-shrink-lexer.Tpo -c -o hishrink-shrink-lexer.o `test -f 'shrink-lexer.c' || echo '$(srcdir)/'`shrink-lexer.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/hishrink-shrink-lexer.Tpo $(DEPDIR)/hishrink-shrink-lexer.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='shrink-lexer.c' object='hishrink-shrink-lexer.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) $(hishrink_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o hishrink-shrink-lexer.o `test -f 'shrink-lexer.c' || echo '$(srcdir)/'`shrink-lexer.c
+
+hishrink-shrink-lexer.obj: shrink-lexer.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(hishrink_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT hishrink-shrink-lexer.obj -MD -MP -MF $(DEPDIR)/hishrink-shrink-lexer.Tpo -c -o hishrink-shrink-lexer.obj `if test -f 'shrink-lexer.c'; then $(CYGPATH_W) 'shrink-lexer.c'; else $(CYGPATH_W) '$(srcdir)/shrink-lexer.c'; fi`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/hishrink-shrink-lexer.Tpo $(DEPDIR)/hishrink-shrink-lexer.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='shrink-lexer.c' object='hishrink-shrink-lexer.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) $(hishrink_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o hishrink-shrink-lexer.obj `if test -f 'shrink-lexer.c'; then $(CYGPATH_W) 'shrink-lexer.c'; else $(CYGPATH_W) '$(srcdir)/shrink-lexer.c'; fi`
+
+hishrink-shrink-parser.o: shrink-parser.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(hishrink_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT hishrink-shrink-parser.o -MD -MP -MF $(DEPDIR)/hishrink-shrink-parser.Tpo -c -o hishrink-shrink-parser.o `test -f 'shrink-parser.c' || echo '$(srcdir)/'`shrink-parser.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/hishrink-shrink-parser.Tpo $(DEPDIR)/hishrink-shrink-parser.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='shrink-parser.c' object='hishrink-shrink-parser.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) $(hishrink_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o hishrink-shrink-parser.o `test -f 'shrink-parser.c' || echo '$(srcdir)/'`shrink-parser.c
+
+hishrink-shrink-parser.obj: shrink-parser.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(hishrink_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT hishrink-shrink-parser.obj -MD -MP -MF $(DEPDIR)/hishrink-shrink-parser.Tpo -c -o hishrink-shrink-parser.obj `if test -f 'shrink-parser.c'; then $(CYGPATH_W) 'shrink-parser.c'; else $(CYGPATH_W) '$(srcdir)/shrink-parser.c'; fi`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/hishrink-shrink-parser.Tpo $(DEPDIR)/hishrink-shrink-parser.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='shrink-parser.c' object='hishrink-shrink-parser.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) $(hishrink_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o hishrink-shrink-parser.obj `if test -f 'shrink-parser.c'; then $(CYGPATH_W) 'shrink-parser.c'; else $(CYGPATH_W) '$(srcdir)/shrink-parser.c'; fi`
+
+histretch-hformat.o: hformat.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(histretch_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT histretch-hformat.o -MD -MP -MF $(DEPDIR)/histretch-hformat.Tpo -c -o histretch-hformat.o `test -f 'hformat.c' || echo '$(srcdir)/'`hformat.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/histretch-hformat.Tpo $(DEPDIR)/histretch-hformat.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='hformat.c' object='histretch-hformat.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) $(histretch_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o histretch-hformat.o `test -f 'hformat.c' || echo '$(srcdir)/'`hformat.c
+
+histretch-hformat.obj: hformat.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(histretch_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT histretch-hformat.obj -MD -MP -MF $(DEPDIR)/histretch-hformat.Tpo -c -o histretch-hformat.obj `if test -f 'hformat.c'; then $(CYGPATH_W) 'hformat.c'; else $(CYGPATH_W) '$(srcdir)/hformat.c'; fi`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/histretch-hformat.Tpo $(DEPDIR)/histretch-hformat.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='hformat.c' object='histretch-hformat.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) $(histretch_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o histretch-hformat.obj `if test -f 'hformat.c'; then $(CYGPATH_W) 'hformat.c'; else $(CYGPATH_W) '$(srcdir)/hformat.c'; fi`
+
+histretch-histretch.o: histretch.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(histretch_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT histretch-histretch.o -MD -MP -MF $(DEPDIR)/histretch-histretch.Tpo -c -o histretch-histretch.o `test -f 'histretch.c' || echo '$(srcdir)/'`histretch.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/histretch-histretch.Tpo $(DEPDIR)/histretch-histretch.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='histretch.c' object='histretch-histretch.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) $(histretch_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o histretch-histretch.o `test -f 'histretch.c' || echo '$(srcdir)/'`histretch.c
+
+histretch-histretch.obj: histretch.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(histretch_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT histretch-histretch.obj -MD -MP -MF $(DEPDIR)/histretch-histretch.Tpo -c -o histretch-histretch.obj `if test -f 'histretch.c'; then $(CYGPATH_W) 'histretch.c'; else $(CYGPATH_W) '$(srcdir)/histretch.c'; fi`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/histretch-histretch.Tpo $(DEPDIR)/histretch-histretch.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='histretch.c' object='histretch-histretch.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) $(histretch_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o histretch-histretch.obj `if test -f 'histretch.c'; then $(CYGPATH_W) 'histretch.c'; else $(CYGPATH_W) '$(srcdir)/histretch.c'; fi`
+
+hitex-hformat.o: hformat.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(hitex_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT hitex-hformat.o -MD -MP -MF $(DEPDIR)/hitex-hformat.Tpo -c -o hitex-hformat.o `test -f 'hformat.c' || echo '$(srcdir)/'`hformat.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/hitex-hformat.Tpo $(DEPDIR)/hitex-hformat.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='hformat.c' object='hitex-hformat.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) $(hitex_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o hitex-hformat.o `test -f 'hformat.c' || echo '$(srcdir)/'`hformat.c
+
+hitex-hformat.obj: hformat.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(hitex_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT hitex-hformat.obj -MD -MP -MF $(DEPDIR)/hitex-hformat.Tpo -c -o hitex-hformat.obj `if test -f 'hformat.c'; then $(CYGPATH_W) 'hformat.c'; else $(CYGPATH_W) '$(srcdir)/hformat.c'; fi`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/hitex-hformat.Tpo $(DEPDIR)/hitex-hformat.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='hformat.c' object='hitex-hformat.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) $(hitex_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o hitex-hformat.obj `if test -f 'hformat.c'; then $(CYGPATH_W) 'hformat.c'; else $(CYGPATH_W) '$(srcdir)/hformat.c'; fi`
+
+hitex-hput.o: hput.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(hitex_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT hitex-hput.o -MD -MP -MF $(DEPDIR)/hitex-hput.Tpo -c -o hitex-hput.o `test -f 'hput.c' || echo '$(srcdir)/'`hput.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/hitex-hput.Tpo $(DEPDIR)/hitex-hput.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='hput.c' object='hitex-hput.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) $(hitex_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o hitex-hput.o `test -f 'hput.c' || echo '$(srcdir)/'`hput.c
+
+hitex-hput.obj: hput.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(hitex_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT hitex-hput.obj -MD -MP -MF $(DEPDIR)/hitex-hput.Tpo -c -o hitex-hput.obj `if test -f 'hput.c'; then $(CYGPATH_W) 'hput.c'; else $(CYGPATH_W) '$(srcdir)/hput.c'; fi`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/hitex-hput.Tpo $(DEPDIR)/hitex-hput.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='hput.c' object='hitex-hput.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) $(hitex_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o hitex-hput.obj `if test -f 'hput.c'; then $(CYGPATH_W) 'hput.c'; else $(CYGPATH_W) '$(srcdir)/hput.c'; fi`
+
+hitex-htex.o: htex.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(hitex_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT hitex-htex.o -MD -MP -MF $(DEPDIR)/hitex-htex.Tpo -c -o hitex-htex.o `test -f 'htex.c' || echo '$(srcdir)/'`htex.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/hitex-htex.Tpo $(DEPDIR)/hitex-htex.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='htex.c' object='hitex-htex.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) $(hitex_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o hitex-htex.o `test -f 'htex.c' || echo '$(srcdir)/'`htex.c
+
+hitex-htex.obj: htex.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(hitex_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT hitex-htex.obj -MD -MP -MF $(DEPDIR)/hitex-htex.Tpo -c -o hitex-htex.obj `if test -f 'htex.c'; then $(CYGPATH_W) 'htex.c'; else $(CYGPATH_W) '$(srcdir)/htex.c'; fi`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/hitex-htex.Tpo $(DEPDIR)/hitex-htex.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='htex.c' object='hitex-htex.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) $(hitex_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o hitex-htex.obj `if test -f 'htex.c'; then $(CYGPATH_W) 'htex.c'; else $(CYGPATH_W) '$(srcdir)/htex.c'; fi`
+
+hitex-hitex.o: hitex.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(hitex_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT hitex-hitex.o -MD -MP -MF $(DEPDIR)/hitex-hitex.Tpo -c -o hitex-hitex.o `test -f 'hitex.c' || echo '$(srcdir)/'`hitex.c
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/hitex-hitex.Tpo $(DEPDIR)/hitex-hitex.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='hitex.c' object='hitex-hitex.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) $(hitex_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o hitex-hitex.o `test -f 'hitex.c' || echo '$(srcdir)/'`hitex.c
+
+hitex-hitex.obj: hitex.c
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(hitex_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT hitex-hitex.obj -MD -MP -MF $(DEPDIR)/hitex-hitex.Tpo -c -o hitex-hitex.obj `if test -f 'hitex.c'; then $(CYGPATH_W) 'hitex.c'; else $(CYGPATH_W) '$(srcdir)/hitex.c'; fi`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/hitex-hitex.Tpo $(DEPDIR)/hitex-hitex.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='hitex.c' object='hitex-hitex.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) $(hitex_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o hitex-hitex.obj `if test -f 'hitex.c'; then $(CYGPATH_W) 'hitex.c'; else $(CYGPATH_W) '$(srcdir)/hitex.c'; fi`
+
initex-callexe.o: callexe.c
@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(initex_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT initex-callexe.o -MD -MP -MF $(DEPDIR)/initex-callexe.Tpo -c -o initex-callexe.o `test -f 'callexe.c' || echo '$(srcdir)/'`callexe.c
@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/initex-callexe.Tpo $(DEPDIR)/initex-callexe.Po
@@ -18636,6 +18896,13 @@ xetex-dummy.obj: dummy.cxx
@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(LTCXXCOMPILE) -c -o $@ $<
+.l.c:
+ $(AM_V_LEX)$(am__skiplex) $(SHELL) $(YLWRAP) $< $(LEX_OUTPUT_ROOT).c $@ -- $(LEXCOMPILE)
+
+shrink-lexer.c: shrink-lexer.l
+ $(AM_V_LEX) \
+ $(SHELL) $(YLWRAP) `test -f 'shrink-lexer.l' || echo '$(srcdir)/'`shrink-lexer.l $(LEX_OUTPUT_ROOT).c shrink-lexer.c -- $(LEX) $(AM_LFLAGS) $(LFLAGS)
+
.mm.o:
@am__fastdepOBJCXX_TRUE@ $(AM_V_OBJCXX)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.o$$||'`;\
@am__fastdepOBJCXX_TRUE@ $(OBJCXXCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\
@@ -18674,6 +18941,13 @@ xetexdir/libxetex_a-XeTeXFontMgr_Mac.obj: xetexdir/XeTeXFontMgr_Mac.mm
@AMDEP_TRUE@@am__fastdepOBJCXX_FALSE@ DEPDIR=$(DEPDIR) $(OBJCXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepOBJCXX_FALSE@ $(AM_V_OBJCXX@am__nodep@)$(OBJCXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libxetex_a_CPPFLAGS) $(CPPFLAGS) $(libxetex_a_OBJCXXFLAGS) $(OBJCXXFLAGS) -c -o xetexdir/libxetex_a-XeTeXFontMgr_Mac.obj `if test -f 'xetexdir/XeTeXFontMgr_Mac.mm'; then $(CYGPATH_W) 'xetexdir/XeTeXFontMgr_Mac.mm'; else $(CYGPATH_W) '$(srcdir)/xetexdir/XeTeXFontMgr_Mac.mm'; fi`
+.y.c:
+ $(AM_V_YACC)$(am__skipyacc) $(SHELL) $(YLWRAP) $< y.tab.c $@ y.tab.h `echo $@ | $(am__yacc_c2h)` y.output $*.output -- $(YACCCOMPILE)
+
+shrink-parser.c: shrink-parser.y
+ $(AM_V_YACC) \
+ $(SHELL) $(YLWRAP) `test -f 'shrink-parser.y' || echo '$(srcdir)/'`shrink-parser.y y.tab.c shrink-parser.c y.tab.h `echo shrink-parser.c | $(am__yacc_c2h)` y.output shrink-parser.output -- $(YACC) $(AM_YFLAGS) $(YFLAGS)
+
mostlyclean-libtool:
-rm -f *.lo
@@ -19324,6 +19598,9 @@ mostlyclean-generic:
-test -z "$(TEST_SUITE_LOG)" || rm -f $(TEST_SUITE_LOG)
clean-generic:
+ -rm -f shrink-lexer.c
+ -rm -f shrink-parser.c
+ -rm -f shrink-parser.h
-test -z "$(CLEANFILES)" || rm -f $(CLEANFILES)
distclean-generic:
@@ -19511,6 +19788,16 @@ distclean: distclean-recursive
-rm -f ./$(DEPDIR)/gftodvi.Po
-rm -f ./$(DEPDIR)/gftopk.Po
-rm -f ./$(DEPDIR)/gftype.Po
+ -rm -f ./$(DEPDIR)/hishrink-hformat.Po
+ -rm -f ./$(DEPDIR)/hishrink-hishrink.Po
+ -rm -f ./$(DEPDIR)/hishrink-shrink-lexer.Po
+ -rm -f ./$(DEPDIR)/hishrink-shrink-parser.Po
+ -rm -f ./$(DEPDIR)/histretch-hformat.Po
+ -rm -f ./$(DEPDIR)/histretch-histretch.Po
+ -rm -f ./$(DEPDIR)/hitex-hformat.Po
+ -rm -f ./$(DEPDIR)/hitex-hitex.Po
+ -rm -f ./$(DEPDIR)/hitex-hput.Po
+ -rm -f ./$(DEPDIR)/hitex-htex.Po
-rm -f ./$(DEPDIR)/initex-callexe.Po
-rm -f ./$(DEPDIR)/libluahbtexspecific_a-luainit-hb.Po
-rm -f ./$(DEPDIR)/libluahbtexspecific_a-luastuff-hb.Po
@@ -20280,6 +20567,16 @@ maintainer-clean: maintainer-clean-recursive
-rm -f ./$(DEPDIR)/gftodvi.Po
-rm -f ./$(DEPDIR)/gftopk.Po
-rm -f ./$(DEPDIR)/gftype.Po
+ -rm -f ./$(DEPDIR)/hishrink-hformat.Po
+ -rm -f ./$(DEPDIR)/hishrink-hishrink.Po
+ -rm -f ./$(DEPDIR)/hishrink-shrink-lexer.Po
+ -rm -f ./$(DEPDIR)/hishrink-shrink-parser.Po
+ -rm -f ./$(DEPDIR)/histretch-hformat.Po
+ -rm -f ./$(DEPDIR)/histretch-histretch.Po
+ -rm -f ./$(DEPDIR)/hitex-hformat.Po
+ -rm -f ./$(DEPDIR)/hitex-hitex.Po
+ -rm -f ./$(DEPDIR)/hitex-hput.Po
+ -rm -f ./$(DEPDIR)/hitex-htex.Po
-rm -f ./$(DEPDIR)/initex-callexe.Po
-rm -f ./$(DEPDIR)/libluahbtexspecific_a-luainit-hb.Po
-rm -f ./$(DEPDIR)/libluahbtexspecific_a-luastuff-hb.Po
@@ -21825,6 +22122,48 @@ euptrip.diffs: euptex$(EXEEXT) dvitype$(EXEEXT) pltotf$(EXEEXT) tftopl$(EXEEXT)
euptrip-clean:
rm -rf euptripdir
+$(format_c_h_l_y): format-tangle
+ $(hi_ctangle_sh) format
+#
+format-tangle: ctangle$(EXEEXT) hitexdir/format.w tangle-sh
+ $(hi_ctangle_sh) format
+
+$(hitex_c_h): hitex-tangle
+ $(hi_ctangle_sh) hitex
+#
+hitex-tangle: ctangle$(EXEEXT) hitexdir/hitex.w tangle-sh
+ $(hi_ctangle_sh) hitex
+
+# htex.c needs hitex.h, so depend on the hitex tangle.
+$(htex_c_h): htex-tangle hitex-tangle
+ $(hi_ctangle_sh) htex
+#
+htex-tangle: ctangle$(EXEEXT) hitexdir/htex.w tangle-sh hitex-tangle
+ $(hi_ctangle_sh) htex
+
+# Generating hformat.c using mkhformat.
+hformat.c: mkhformat
+ ./mkhformat >hformat.c || { rm -f hformat.c; exit 1; }
+
+# Postprocessing lex and yacc files generated by ctangle.
+# (SED is defined with AC_PROG_SED)
+shrink-parser.y: $(srcdir)/hitexdir/yacc.sed shrink.yacc-in
+ $(SED) -f $(srcdir)/hitexdir/yacc.sed shrink.yacc-in >shrink-parser.y \
+ || { rm -f shrink-parser.y; exit 1; }
+#
+shrink-lexer.l: $(srcdir)/hitexdir/lex.sed shrink.lex-in
+ $(SED) -f $(srcdir)/hitexdir/lex.sed shrink.lex-in >shrink-lexer.l \
+ || { rm -f shrink-lexer.l; exit 1; }
+
+# shrink needs the yacc header. It's actually the .o that should be the
+# target here, seems to me, but hishrink.o is not used for the object
+# name, it's hishrink-hishrink.o (because the program is also named
+# hishrink), and adding that here ends up trying to link with
+# hishrink-hishrink.o (I don't know), which doesn't exist. Sigh.
+hishrink.c: shrink-parser.h shrink-parser.c
+
+# still missing
+
# We must create pdftexd.h (and xpdf...) before building the libpdftex_a_OBJECTS.
$(libpdftex_a_OBJECTS): pdftexd.h $(XPDF_DEPEND) $(ZLIB_DEPEND) $(LIBPNG_DEPEND)
$(pdftex_OBJECTS): $(pdftex_prereq)
diff --git a/Build/source/texk/web2c/ac/web2c.ac b/Build/source/texk/web2c/ac/web2c.ac
index 40afc44f3d6..aec20a703c5 100644
--- a/Build/source/texk/web2c/ac/web2c.ac
+++ b/Build/source/texk/web2c/ac/web2c.ac
@@ -1,7 +1,7 @@
dnl $Id$
# texk/web2c/ac/web2c.ac: configure.ac fragment for TL subdir
dnl
-dnl Copyright 2015-2020 Karl Berry <tex-live@tug.org>
+dnl Copyright 2015-2021 Karl Berry <tex-live@tug.org>
dnl Copyright 2009-2015 Peter Breitenlohner <tex-live@tug.org>
dnl You may freely use, modify and/or distribute this file.
dnl
@@ -36,6 +36,7 @@ m4_define([kpse_tex_progs], [dnl
[[uptex], [yes], [yes], [upTeX], [ptexenc zlib]],
[[euptex], [yes], [yes], [e-upTeX], [ptexenc zlib]],
[[aleph], [yes], [], [Aleph], []],
+[[hitex], [no], [], [HiTeX], [zlib]],
[[pdftex], [yes], [yes], [pdfTeX], [xpdf libpng zlib]],
[[luatex], [yes], [], [LuaTeX], [pplib libpng zziplib lua53]],
[[luajittex], [yes], [], [LuaJITTeX], [pplib libpng zziplib luajit]],
diff --git a/Build/source/texk/web2c/c-auto.in b/Build/source/texk/web2c/c-auto.in
index 9b356d00a32..7ffd2216b29 100644
--- a/Build/source/texk/web2c/c-auto.in
+++ b/Build/source/texk/web2c/c-auto.in
@@ -2,7 +2,7 @@
/* w2c/c-auto.h: defines for web2c, as determined by configure.
- Copyright 1994-97, 2008-2012 Karl Berry.
+ Copyright 1994-97, 2008-2021 Karl Berry.
Copyright 1997-99, 2002, 2005 Olaf Weber.
This program is free software: you can redistribute it and/or modify
diff --git a/Build/source/texk/web2c/configure b/Build/source/texk/web2c/configure
index 974706d41e0..185c8da41a4 100755
--- a/Build/source/texk/web2c/configure
+++ b/Build/source/texk/web2c/configure
@@ -822,6 +822,8 @@ PDFTEX_SYNCTEX_FALSE
PDFTEX_SYNCTEX_TRUE
PDFTEX_FALSE
PDFTEX_TRUE
+HITEX_FALSE
+HITEX_TRUE
ALEPH_FALSE
ALEPH_TRUE
EUPTEX_SYNCTEX_FALSE
@@ -1045,6 +1047,7 @@ enable_uptex_synctex
enable_euptex
enable_euptex_synctex
enable_aleph
+enable_hitex
enable_pdftex
enable_pdftex_synctex
enable_luatex
@@ -1784,6 +1787,7 @@ Optional Features:
--disable-euptex do not compile and install e-upTeX
--disable-euptex-synctex build e-upTeX without SyncTeX support
--disable-aleph do not compile and install Aleph
+ --enable-hitex compile and install HiTeX
--disable-pdftex do not compile and install pdfTeX
--disable-pdftex-synctex build pdfTeX without SyncTeX support
--disable-luatex do not compile and install LuaTeX
@@ -19811,6 +19815,21 @@ case $enable_aleph in #(
*) :
enable_aleph=yes ;;
esac
+# Check whether --enable-hitex was given.
+if test ${enable_hitex+y}
+then :
+ enableval=$enable_hitex;
+fi
+case $enable_hitex in #(
+ yes | no) :
+ ;; #(
+ *) :
+ enable_hitex=no ;;
+esac
+
+test "x$enable_web2c:$enable_hitex" = xyes:yes && {
+ need_zlib=yes
+}
# Check whether --enable-pdftex was given.
if test ${enable_pdftex+y}
then :
@@ -22526,6 +22545,13 @@ else
ALEPH_TRUE='#'
ALEPH_FALSE=
fi
+ if test "x$enable_hitex" = xyes; then
+ HITEX_TRUE=
+ HITEX_FALSE='#'
+else
+ HITEX_TRUE='#'
+ HITEX_FALSE=
+fi
if test "x$enable_pdftex" = xyes; then
PDFTEX_TRUE=
PDFTEX_FALSE='#'
@@ -28375,6 +28401,10 @@ if test -z "${ALEPH_TRUE}" && test -z "${ALEPH_FALSE}"; then
as_fn_error $? "conditional \"ALEPH\" was never defined.
Usually this means the macro was only invoked conditionally." "$LINENO" 5
fi
+if test -z "${HITEX_TRUE}" && test -z "${HITEX_FALSE}"; then
+ as_fn_error $? "conditional \"HITEX\" was never defined.
+Usually this means the macro was only invoked conditionally." "$LINENO" 5
+fi
if test -z "${PDFTEX_TRUE}" && test -z "${PDFTEX_FALSE}"; then
as_fn_error $? "conditional \"PDFTEX\" was never defined.
Usually this means the macro was only invoked conditionally." "$LINENO" 5
diff --git a/Build/source/texk/web2c/configure.ac b/Build/source/texk/web2c/configure.ac
index f2ecd7f26ca..7b00fd4fadb 100644
--- a/Build/source/texk/web2c/configure.ac
+++ b/Build/source/texk/web2c/configure.ac
@@ -326,7 +326,7 @@ AC_CONFIG_HEADERS([w2c/c-auto.h:c-auto.in],
AH_TOP([/* w2c/c-auto.h: defines for web2c, as determined by configure.
- Copyright 1994-97, 2008-2012 Karl Berry.
+ Copyright 1994-97, 2008-2021 Karl Berry.
Copyright 1997-99, 2002, 2005 Olaf Weber.
This program is free software: you can redistribute it and/or modify
diff --git a/Build/source/texk/web2c/hitexdir/am/hitex.am b/Build/source/texk/web2c/hitexdir/am/hitex.am
new file mode 100644
index 00000000000..31cf845c487
--- /dev/null
+++ b/Build/source/texk/web2c/hitexdir/am/hitex.am
@@ -0,0 +1,100 @@
+## $Id$
+## texk/web2c/hitexdir/am/hitex.am: Makefile fragment for HiTeX
+##
+## Copyright 2021 Martin Ruckert <ruckert@cs.hm.edu>
+## You may freely use, modify and/or distribute this file.
+
+if HITEX
+bin_PROGRAMS += hitex hishrink histretch
+endif HITEX
+EXTRA_PROGRAMS += hitex hishrink histretch
+
+#kb hitex_CFLAGS = $(AM_CFLAGS) \
+#kb -Wno-parentheses -Wno-maybe-uninitialized -Wno-unused-function -Wno-unused-variable
+hitex_CPPFLAGS = $(AM_CPPFLAGS) $(ZLIB_INCLUDES)
+hitex_LDADD = $(KPATHSEA_LIBS) $(ZLIB_LIBS)
+#
+histretch_CPPFLAGS = $(AM_CPPFLAGS) $(ZLIB_INCLUDES)
+histretch_LDADD = $(ZLIB_LIBS)
+#
+hishrink_CPPFLAGS = $(AM_CPPFLAGS) $(ZLIB_INCLUDES)
+hishrink_LDADD = $(ZLIB_LIBS)
+
+# We want debugging for the parser, and we use features not in POSIX yacc.
+# I guess the result is that we assume bison? Not sure if that's ok.
+AM_YFLAGS = -d -v -Wno-yacc
+
+# HiTeX CWEB sources
+hitex_web = hitexdir/format.w hitexdir/htex.w hitexdir/hitex.w
+
+# Creating several files: need stamp file and two rules with identical recipes
+hi_ctangle_sh = CWEBINPUTS=$(srcdir)/hitexdir AM_V_P=$(AM_V_P) $(SHELL) ./tangle-sh $@ $(CTANGLE)
+
+# HiTeX C/yacc/lex sources generated using ctangle.
+# For each cweb (.w) source file, list the files generated.
+format_c_h_l_y = basetypes.h error.h hformat.h \
+ mkhformat.c \
+ hput.c hput.h \
+ hget.c hget.h \
+ shrink.lex-in shrink.yacc-in \
+ hishrink.c histretch.c
+htex_c_h = htex.c htex.h
+hitex_c_h = hitex.c hitex.h
+
+$(format_c_h_l_y): format-tangle
+ $(hi_ctangle_sh) format
+#
+format-tangle: ctangle$(EXEEXT) hitexdir/format.w tangle-sh
+ $(hi_ctangle_sh) format
+
+$(hitex_c_h): hitex-tangle
+ $(hi_ctangle_sh) hitex
+#
+hitex-tangle: ctangle$(EXEEXT) hitexdir/hitex.w tangle-sh
+ $(hi_ctangle_sh) hitex
+
+# htex.c needs hitex.h, so depend on the hitex tangle.
+$(htex_c_h): htex-tangle hitex-tangle
+ $(hi_ctangle_sh) htex
+#
+htex-tangle: ctangle$(EXEEXT) hitexdir/htex.w tangle-sh hitex-tangle
+ $(hi_ctangle_sh) htex
+
+# Generating hformat.c using mkhformat.
+hformat.c: mkhformat
+ ./mkhformat >hformat.c || { rm -f hformat.c; exit 1; }
+
+# Postprocessing lex and yacc files generated by ctangle.
+# (SED is defined with AC_PROG_SED)
+shrink-parser.y: $(srcdir)/hitexdir/yacc.sed shrink.yacc-in
+ $(SED) -f $(srcdir)/hitexdir/yacc.sed shrink.yacc-in >shrink-parser.y \
+ || { rm -f shrink-parser.y; exit 1; }
+#
+shrink-lexer.l: $(srcdir)/hitexdir/lex.sed shrink.lex-in
+ $(SED) -f $(srcdir)/hitexdir/lex.sed shrink.lex-in >shrink-lexer.l \
+ || { rm -f shrink-lexer.l; exit 1; }
+
+# Other web2c/*dir/am/*.am engine Makefile fragments define a bunch of
+# _OBJECTS variables. This does not seem right to me. Instead, we
+# correctly define the _SOURCES.
+#
+nodist_hitex_SOURCES = hformat.c hput.c $(htex_c_h) $(hitex_c_h)
+nodist_histretch_SOURCES = hformat.c histretch.c
+nodist_hishrink_SOURCES = hformat.c hishrink.c shrink-lexer.l shrink-parser.y
+
+# shrink needs the yacc header. It's actually the .o that should be the
+# target here, seems to me, but hishrink.o is not used for the object
+# name, it's hishrink-hishrink.o (because the program is also named
+# hishrink), and adding that here ends up trying to link with
+# hishrink-hishrink.o (I don't know), which doesn't exist. Sigh.
+hishrink.c: shrink-parser.h shrink-parser.c
+
+EXTRA_DIST += hitexdir/ChangeLog $(hitex_web) lex.sed yacc.sed
+
+DISTCLEANFILES += $(nodist_hitex_SOURCES)
+DISTCLEANFILES += $(nodist_hishrink_SOURCES) $(nodist_histretch_SOURCES)
+DISTCLEANFILES += format-tangle htex-tangle hitex-tangle
+DISTCLEANFILES += shrink-parser.* shrink-lexer.*
+
+## HiTeX tests
+# still missing
diff --git a/Build/source/texk/web2c/hitexdir/format.w b/Build/source/texk/web2c/hitexdir/format.w
new file mode 100644
index 00000000000..0aa8152ccaa
--- /dev/null
+++ b/Build/source/texk/web2c/hitexdir/format.w
@@ -0,0 +1,10509 @@
+% This file is part of HINT
+% Copyright 2017-2021 Martin Ruckert
+%
+% 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 ../hint.sty
+
+%% 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
+
+
+\def\setrevision$#1: #2 ${\gdef\lastrevision{#2}}
+\setrevision$Revision: 2485 $
+\def\setdate$#1(#2) ${\gdef\lastdate{#2}}
+\setdate$Date: 2021-08-19 19:37:13 +0200 (Thu, 19 Aug 2021) $
+
+\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 HINT:\par
+ \Largetitlefont\raggedleft The File Format\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 HINT: The File Format}\par
+ \leftline{\smalltitlefont Version 1.3}
+ \vskip 10pt plus 0.5fill
+ \leftline{\smalltitlefont Reflowable}
+ \vskip-3pt
+ \leftline{\smalltitlefont Output}
+ \vskip-3pt
+ \leftline{\smalltitlefont for \TeX}
+ \vskip 10pt plus 0.5fill
+ \hskip 0pt plus 2fill{\it F\"ur meine Mutter}\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{Second 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.
+ HINT: The File Format
+ Includes index.
+ ISBN 979-854992684-4
+}
+\bigskip
+
+{\raggedright\advance\rightskip 3.5pc
+\def\:{\discretionary{}{}{}}
+Internet page {\tt http:\://hint.\:userweb.\:mwn.\:de/\:hint/\:format.html}
+may contain current information about this book, downloadable software,
+and news.
+
+\vfill
+Copyright $\copyright$ 2019, 2021 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 ruckert\:@@cs.hm.edu}
+\medskip
+ISBN-13: 979-854992684-4\par
+\medskip
+First printing: August 2019\par
+Second edition: August 2021\par
+\medskip
+Revision: \lastrevision,\quad Date: \lastdate\par
+}
+}
+\endgroup
+
+
+\frontmatter
+
+
+
+\plainsection{Preface}
+Late in summer 2017, with my new \CEE\ based {\tt cweb} implementation
+of \TeX\cite{Knuth:tex} in hand\cite{MR:webtocweb}\cite{MR:tug38}\cite{MR:web2w}, I started to write
+the first prototype of the \HINT\ viewer. I basically made two copies
+of \TeX: In the first copy, I replaced the |build_page| procedure by
+an output routine which used more or less the printing routines
+already available in \TeX. This was the beginning of the
+\HINT\ file format.
+In the second copy, I replaced \TeX's main loop by an input routine
+that would feed the \HINT\ file more or less directly to \TeX's
+|build_page| procedure. And after replacing \TeX's |ship_out|
+procedure by a modified rendering routine of a dvi viewer that I had
+written earlier for my experiments with \TeX's Computer Modern
+fonts\cite{MR:tug37}, I had my first running \HINT\ viewer. My
+sabbatical during the following Fall term gave me time for ``rapid
+prototyping'' various features that I considered necessary for
+reflowable \TeX\ output\cite{MR:tug39}.
+
+The textual output format derived from the original \TeX\ debugging
+routines proved to be insufficient when I implemented a ``page up''
+button because it did not support reading the page content
+``backwards''. As a consequence, I developed a compact binary file
+format that could be parsed easily in both directions. The \HINT\
+short file format war born. I stopped an initial attempt at
+eliminating the old textual format because it was so much nicer when
+debugging. Instead, I converted the long textual format into the short
+binary format as a preliminary step in the viewer. This was not a long
+term solution. When opening a big file, as produced from a 1000
+pages \TeX\ file, the parsing took several seconds before the first
+page would appear on screen. This delay, observed on a fast desktop
+PC, is barley tolerable, and the delay one would expect on a low-cost,
+low-power, mobile device seemed prohibitive. The consequence is
+simple: The viewer will need an input file in the short format; and to
+support debugging (or editing), separate programs are needed to
+translate the short format into the long format and back again. But
+for the moment, I did not bother to implement any of this but
+continued with unrestricted experimentation.
+
+With the beginning of the Spring term 2018, I stopped further
+experiments with the \HINT\ viewer and decided that I had to write
+down a clean design of the \HINT\ file format. Or of both file
+formats? Professors are supposed to do research, and hence I tried an
+experiment: Instead of writing down a traditional language
+specification, I decided to stick with the ``literate programming''
+paradigm\cite{Knuth:lp} and write the present book. It describes and implements
+the \.{stretch} and \.{shrink} programs translating one file format
+into the other. As a side effect, it contains the underlying language
+specification. Whether this experiment is a success as a language
+specification remains to be seen, and you should see for yourself. But
+the only important measure for the value of a scientific experiment is
+how much you can learn form it---and I learned a lot.
+
+The whole project turned out to be much more difficult than I had
+expected. Early on, I decided that I would use a recursive descent
+parser for the short format and an LR($k$) parser for the long
+format. Of course, I would use {\tt lex}/{\tt flex} and {\tt yacc}/{\tt bison}
+to generate the LR($k$) parser, and so I had to extend the {\tt
+cweb} tools\cite{Knuth:cweb} to support the corresponding source files.
+
+About in mid May, after writing down about 100 pages, the first
+problems emerged that could not be resolved with my current
+approach. I had started to describe font definitions containing
+definitions of the interword glue and the default hyphen, and the
+declarative style of my exposition started to conflict with the
+sequential demands of writing an output file. So it was time for a
+first complete redesign. Two more passes over the whole book were
+necessary to find the concepts and the structure that would allow me
+to go forward and complete the book as you see it now.
+
+While rewriting was on its way, many ``nice ideas'' were pruned from
+the book. For example, the initial idea of optimizing the \HINT\ file
+while translating it was first reduced to just gathering statistics
+and then disappeared completely. The added code and complexity was
+just too distracting.
+
+What you see before you is still a snapshot of the \HINT\ file format
+because its development is still under way. We will know what
+features are needed for a reflowable \TeX\ file format only after many
+people have started using the format. To use the format, the end-user
+will need implementations, and the implementer will need a language
+specification. The present book is the first step in an attempt to
+solve this ``chicken or egg'' dilemma.
+
+
+\vskip 1cm
+\noindent {\it M\"unchen\hfil\break
+August 20, 2019 \hfill Martin Ruckert}
+
+
+\tableofcontent
+%\thefigindex
+
+
+\mainmatter
+
+\section{Introduction}\label{intro}
+This book defines a file format for reflowable text.
+Actually it describes two file formats: a long format
+that optimizes readability for human beings, and
+a short format that optimizes readability for machines
+and the use of storage space. Both formats use the concept of nodes and lists of
+nodes to describe the file content. Programs that process these nodes
+will likely want to convert the compressed binary representation of a
+node---the short format---or the lengthy textual representation of a
+node---the long format---into a convenient internal representation.
+So most of what follows is just a description of these nodes: their short format,
+their long format and sometimes their internal representation.
+Where as the description of the long and short external format is part
+of the file specification, the description of the internal representation
+is just informational. Different internal representations can be chosen
+based on the individual needs of the program.
+
+While defining the format, I illustrate the processing of long and short format
+files by implementing two utilities: \.{shrink} and \.{stretch}.
+\.{shrink} converts the long format into the short format and \.{stretch}
+goes the other way.
+
+There is also a prototype viewer for this
+file format and a special version of \TeX\cite{DK:texbook} to produce output
+in this format. Both are not described here; a survey describing
+them can be found in \cite{MR:tug39}.
+
+\subsection{Glyphs}
+Let's start with a simple and very common kind of node: a node describing
+a character.
+Because we describe a format that is used to display text,
+we are not so much interested in the
+character itself but we are interested in the specific glyph\index{glyph}.
+In typography, a glyph is a unique mark to be placed on the page representing
+a character. For example the glyph representing the character `a' can have
+many forms among them `{\it a\/}', `{\bf a}', or `{\tenss a}'.
+Such glyphs come in collections, called fonts, representing every character
+of the alphabet in a consistent way.
+
+The long format of a node describing the glyph `a'
+ might look like this:`` \.{<glyph} \.{97} \.{*1>}''.
+Here ``\.{97}'' is the character code which
+happens to be the ASCII code of the letter `a' and ``{\tt *1}'' is a font reference
+that stands for ``Computer Modern Roman 10pt''.
+Reference numbers, as you can see,
+start with an asterisk reminiscent of references in the \CEE\ programming language.
+The Astrix enables us to distinguish between ordinary numbers like ``\.{1}'' and references like ``{\tt *1}''.
+
+To make this node more readable, we will see in section~\secref{chars} that it is also
+possible to write `` \.{<glyph 'a' (cmr10) *1>}''.
+The latter form uses a comment ``\.{(cmr10)}'', enclosed in parentheses, to
+give an indication of what kind of font happens to be font 1, and it uses ``\.{'a'}'',
+the character enclosed in single quotes to denote the ASCII code of `a'.
+But let's keep things simple for now and stick with the decimal notation of the character code.
+
+The rest is common for all nodes: a keyword, here ``\.{glyph}'', and a pair of pointed brackets ``\.{<}\dots\.{>}''.
+
+Internally, we represent a glyph by the font number
+and the character number or character code.
+To store the internal representation of a glyph node,
+we define an appropriate structure type, named after the node with a trailing {\dots\bf\_t}.
+@<hint types@>=
+typedef struct {@+ uint32_t c;@+ uint8_t f; @+} glyph_t;
+@
+
+Let us now look at the program \.{shrink} and see how it will convert the long format description
+to the internal representation of the glyph and finally to a short format description.
+
+
+\subsection{Scanning the Long Format}
+First, \.{shrink} reads the input file and extracts a sequence of tokens. This is called ``scanning''\index{scanning}.
+We generate the procedure to do the scanning using the program \.{flex}\cite{JL:flexbison}\index{flex+{\tt flex}} which is the
+GNU version of the common UNIX tool \.{lex}\cite{JL:lexyacc}\index{lex+{\tt lex}}.
+
+The input to \.{flex} is a list of pattern/\kern -1pt action rules where the pattern is a regular
+expression and the action is a piece of \CEE\ code.
+Most of the time, the \CEE\ code is very simple: it just returns the right token\index{token} number
+to the parser which we consider shortly.
+
+The code that defines the tokens will be marked with a line ending in ``\redsymbol''.
+This symbol\index{symbol} stands for ``{\it Reading the long format\/}''.
+These code sequences define the syntactical elements of the long format and at the same time
+implement the reading process. All sections where that happens are preceded by a similar heading
+and for reference they are conveniently listed together starting on page~\pageref{codeindex}.
+
+\codesection{\redsymbol}{Reading the Long Format}\redindex{1}{2}{Glyphs}
+@s START symbol
+@s END symbol
+@s GLYPH symbol
+@s UNSIGNED symbol
+@s REFERENCE symbol
+
+@<symbols@>=
+%token START "<"
+%token END ">"
+%token GLYPH "glyph"
+%token <u> UNSIGNED
+%token <u> REFERENCE
+@
+You might notice that a small caps font is used for |START|, |END| or |GLYPH|.
+These are ``terminal symbols'' or ``tokens''.
+Next are the scanning rules which define the connection between tokens and their
+textual representation.
+
+@<scanning rules@>=
+::@="<"@> :< SCAN_START; return START; >:
+::@=">"@> :< SCAN_END; return END; >:
+::@=glyph@> :< return GLYPH; >:
+::@=0|[1-9][0-9]*@> :< SCAN_UDEC(yytext); return UNSIGNED; >:
+::@=\*(0|[1-9][0-9]*)@> :< SCAN_UDEC(yytext+1); return REFERENCE; >:
+::@=[[:space:]]@> :< ; >:
+::@=\([^()\n]*[)\n]@> :< ; >:
+@
+
+As we will see later, the macros starting with |SCAN_|\dots\ are scanning macros.
+Here |SCAN_UDEC| is a macro that converts the decimal representation
+that did match the given pattern to an unsigned integer value; it is explained in
+section~\secref{integers}.
+The macros |SCAN_START| and |SCAN_END| are explained in section~\secref{text}.
+
+
+The action ``{\tt ;}'' is a ``do nothing'' action; here it causes spaces or comments\index{comment}
+to be ignored. Comments start with an opening parenthesis and are terminated by a
+closing parenthesis or the end of line character.
+The pattern ``\.{[\^()\\n]}'' is a negated
+character class that matches all characters except parentheses and the newline
+character. These are not allowed inside comments. For detailed information about
+the patterns used in a \.{flex} program, see the \.{flex} user manual\cite{JL:flexbison}.
+
+\subsection{Parsing the Long Format}
+\label{parse_glyph}
+Next, the tokens produced by the scanner are assembled into larger entities.
+This is called ``parsing''\index{parsing}.
+We generate the procedure to do the parsing using the program \.{bison}\cite{JL:flexbison}\index{bison+{\tt bison}} which is
+the GNU version of the common UNIX tool \.{yacc}\cite{JL:lexyacc}\index{yacc+{\tt yacc}}.
+
+The input to \.{bison} is a list of parsing rules, called a ``grammar''\index{grammar}.
+The rules describe how to build larger entities from smaller entities.
+For a simple glyph node like `` \.{<glyph 97 *1>}'', we need just these rules:
+\codesection{\redsymbol}{Reading the Long Format}%\redindex{1}{2}{Glyphs}
+@s content_node symbol
+@s node symbol
+@s glyph symbol
+@s glyph_t int
+@s start symbol
+@<symbols@>=
+%type <u> start
+%type <c> glyph
+@
+
+@<parsing rules@>=@/
+glyph: UNSIGNED REFERENCE @/{ $$.c=$1; REF(font_kind,$2); $$.f=$2; };
+content_node: start GLYPH glyph END { hput_tags($1,hput_glyph(&($3))); };
+start: START {HPUTNODE; $$=(uint32_t)(hpos++-hstart);}
+@
+
+You might notice that a slanted font is used for |glyph|, |content_node|, or |start|.
+These are ``nonterminal symbols' and occur on the left hand side of a rule. On the
+right hand side of a rule you find nonterminal symbols, as well as terminal\index{terminal symbol} symbols
+and \CEE\ code enclosed in braces.
+
+Within the \CEE\ code, the expressions |$1| and |$2| refer to the variables on the parse stack
+that are associated with the first and second symbol on the right hand side of the rule.
+In the case of our glyph node, these will be the values 97 and 1, respectively, as produced
+by the macro |SCAN_UDEC|.
+|$$| refers to the variable associated with the left hand side of the rule.
+These variables contain the internal representation of the object in question.
+The type of the variable is specified by a mandatory {\bf token} or optional {\bf type} clause
+when we define the symbol.
+In the above {\bf type} clause for |start| and |glyph| , the identifiers |u| and |c| refer to
+the |union| declaration of the parser (see page~\pageref{union})
+where we find |uint32_t u| and |glyph_t c|. The macro |REF| tests a reference number for
+its valid range.
+
+
+Reading a node is usually split into the following sequence of steps:
+\itemize
+\item Reading the node specification, here a |glyph|
+ consisting of an |UNSIGNED| value and a |REFERENCE| value.
+\item Creating the internal representation in the variable |$$|
+ based on the values of |$1|, |$2|, \dots\ Here the character
+ code field |c| is initialized using the |UNSIGNED| value
+ stored in |$1| and the font field |f| is initialized using
+ |$2| after checking the reference number for the proper range.
+\item A |content_node| rule explaining that |start| is followed by |GLYPH|,
+ the keyword that directs the parser to |glyph|, the
+ node specification, and a final |END|.
+\item Parsing |start|, which is defined as the token |START| will assign
+ to the corresponding variable |p| on the parse stack the current
+ position |hpos| in the output and increments that position
+ to make room for the start byte, which we will discuss shortly.
+\item At the end of the |content_node| rule, the \.{shrink} program calls
+ a {\it hput\_\dots\/} function, here |hput_glyph|, to write the short
+ format of the node as given by its internal representation to the output
+ and return the correct tag value.
+\item Finally the |hput_tags| function will add the tag as a start byte and end byte
+ to the output stream.
+\enditemize
+
+Now let's see how writing the short format works in detail.
+
+
+\subsection{Writing the Short Format}
+A content node in short form begins with a start\index{start byte} byte. It tells us what kind of node it is.
+To describe the content of a short \HINT\ file, 32 different kinds\index{kind} of nodes are defined.
+Hence the kind of a node can be stored in 5 bits and the remaining bits of the start byte
+can be used to contain a 3 bit ``info''\index{info} value.
+
+We define an enumeration type to give symbolic names to the kind-values.
+The exact numerical values are of no specific importance;
+we will see in section~\secref{text}, however, that the assignment chosen below,
+has certain advantages.
+
+Because the usage of kind-values in content nodes is
+slightly different from the usage in definition nodes, we define alternative names for some kind-values.
+To display readable names instead of numerical values when debugging,
+we define two arrays of strings as well. Keeping the definitions consistent
+is achieved by creating all definitions from the same list
+of identifiers using different definitions of the macro |DEF_KIND|.
+
+@<hint basic types@>=
+#define DEF_KIND(C,D,N) @[C##_kind=N@]
+typedef enum {@+@<kinds@>@+,@+ @<alternative kind names@> @+} kind_t;
+#undef DEF_KIND
+@
+
+@<define |content_name| and |definition_name|@>=
+
+#define DEF_KIND(C,D,N) @[#C@]
+const char *content_name[32]=@+{@+@<kinds@>@;@+}@+;
+#undef DEF_KIND@#
+#define DEF_KIND(C,D,N) @[#D@]
+const char *definition_name[0x20]=@+{@+@<kinds@>@;@+}@+;
+#undef DEF_KIND
+@
+
+@<print |content_name| and |definition_name|@>=
+printf("const char *content_name[32]={");
+for (k=0; k<= 31;k++)
+{ printf("\"%s\"",content_name[k]);
+ if (k<31) printf(", ");
+}
+printf("};\n\n");
+printf("const char *definition_name[32]={");
+for (k=0; k<= 31;k++)
+{ printf("\"%s\"",definition_name[k]);
+ if (k<31) printf(", ");
+}
+printf("};\n\n");
+@
+
+
+
+
+
+
+\goodbreak
+\index{glyph kind+\\{glyph\_kind}}
+\index{font kind+\\{font\_kind}}
+\index{penalty kind+\\{penalty\_kind}}
+\index{int kind+\\{int\_kind}}
+\index{kern kind+\\{kern\_kind}}
+\index{xdimen kind+\\{xdimen\_kind}}
+\index{ligature kind+\\{ligature\_kind}}
+\index{disc kind+\\{disc\_kind}}
+\index{glue kind+\\{glue\_kind}}
+\index{language kind+\\{language\_kind}}
+\index{rule kind+\\{rule\_kind}}
+\index{image kind+\\{image\_kind}}
+\index{baseline kind+\\{baseline\_kind}}
+\index{dimen kind+\\{dimen\_kind}}
+\index{hbox kind+\\{hbox\_kind}}
+\index{vbox kind+\\{vbox\_kind}}
+\index{par kind+\\{par\_kind}}
+\index{math kind+\\{math\_kind}}
+\index{table kind+\\{table\_kind}}
+\index{item kind+\\{item\_kind}}
+\index{hset kind+\\{hset\_kind}}
+\index{vset kind+\\{vset\_kind}}
+\index{hpack kind+\\{hpack\_kind}}
+\index{vpack kind+\\{vpack\_kind}}
+\index{stream kind+\\{stream\_kind}}
+\index{page kind+\\{page\_kind}}
+\index{range kind+\\{range\_kind}}
+\index{adjust kind+\\{adjust\_kind}}
+\index{param kind+\\{param\_kind}}
+\index{text kind+\\{text\_kind}}
+\index{list kind+\\{list\_kind}}
+\label{kinddef}
+@<kinds@>=
+DEF_KIND(t@&ext,t@&ext,0),@/
+DEF_KIND(l@&ist,l@&ist,1),@/
+DEF_KIND(p@&aram,p@&aram,2),@/
+DEF_KIND(x@&dimen,x@&dimen,3),@/
+DEF_KIND(a@&djust,a@&djust,4),@/
+DEF_KIND(g@&lyph, f@&ont,5),@/
+DEF_KIND(k@&ern,d@&imen,6),@/
+DEF_KIND(g@&lue,g@&lue,7),@/
+DEF_KIND(l@&igature,l@&igature,8),@/
+DEF_KIND(d@&isc,d@&isc,9),@/
+DEF_KIND(l@&anguage,l@&anguage,10),@/
+DEF_KIND(r@&ule,r@&ule,11),@/
+DEF_KIND(i@&mage,i@&mage,12),@/
+DEF_KIND(l@&eaders,l@&eaders,13),@/
+DEF_KIND(b@&aseline,b@&aseline,14),@/
+DEF_KIND(h@&b@&ox,h@&b@&ox,15),@/
+DEF_KIND(v@&b@&ox,v@&b@&ox,16),@/
+DEF_KIND(p@&ar,p@&ar,17),@/
+DEF_KIND(m@&ath,m@&ath,18),@/
+DEF_KIND(t@&able,t@&able,19),@/
+DEF_KIND(i@&tem,i@&tem,20),@/
+DEF_KIND(h@&set,h@&set,21),@/
+DEF_KIND(v@&set,v@&set,22),@/
+DEF_KIND(h@&pack,h@&pack,23),@/
+DEF_KIND(v@&pack,v@&pack,24),@/
+DEF_KIND(s@&tream,s@&tream,25),@/
+DEF_KIND(p@&age,p@&age,26),@/
+DEF_KIND(r@&ange,r@&ange,27),@/
+DEF_KIND(l@&ink,l@&abel,28),@/
+DEF_KIND(u@&ndefined2,u@&ndefined2,29),@/
+DEF_KIND(u@&ndefined3,u@&ndefined3,30),@/
+DEF_KIND(p@&enalty, i@&nt,31)
+@t@>
+@
+
+For a few kind-values we have
+alternative names; we will use them
+to express different intentions when using them.
+@<alternative kind names@>=
+font_kind=glyph_kind,int_kind=penalty_kind, dimen_kind=kern_kind, label_kind=link_kind, outline_kind=link_kind@/@t{}@>
+@
+
+The info\index{info value} values can be used to represent numbers in the range 0 to 7; for an example
+see the |hput_glyph| function later in this section.
+Mostly, however, the individual bits are used as flags indicating the presence
+or absence of immediate parameter values. If the info bit is set, it
+means the corresponding parameter is present as an immediate value; if it
+is zero, it means that there is no immediate parameter value present, and
+the node specification will reveal what value to use instead.
+In some cases there is a common default value that can be used, in other
+cases a one byte reference number is used to select a predefined value.
+
+To make the binary
+representation of the info bits more readable, we define an
+enumeration type.
+
+\index{b000+\\{b000}}
+\index{b001+\\{b001}}
+\index{b010+\\{b010}}
+\index{b011+\\{b011}}
+\index{b100+\\{b100}}
+\index{b101+\\{b101}}
+\index{b110+\\{b110}}
+\index{b111+\\{b111}}
+@<hint basic types@>=
+typedef enum {@+ b000=0,b001=1,b010=2,b011=3,b100=4,b101=5,b110=6,b111=7@+ } info_t;
+@
+
+
+After the start byte follows the node content and it is the purpose of
+the start byte to reveal the exact syntax and semantics of the node
+content. Because we want to be able to read the short form of a \HINT\
+file in forward direction and in backward direction, the start byte is
+duplicated after the content as an end\index{end byte} byte.
+
+
+We store a kind and an info value in one byte and call this a tag.
+The following macros are used to assemble and disassemble tags:\index{TAG+\.{TAG}}
+@<hint macros@>=
+#define @[KIND(T)@] (((T)>>3)&0x1F)
+#define @[NAME(T)@] @[content_name[KIND(T)]@]
+#define @[INFO(T)@] ((T)&0x7)
+#define @[TAG(K,I)@] (((K)<<3)|(I))
+@
+
+Writing a short format \HINT\ file is implemented by a collection of {\it hput\_\kern 1pt\dots\/} functions;
+they follow most of the time the same schema:
+\itemize
+\item First, we define a variable for |info|.
+\item Then follows the main part of the function body, where we
+decide on the output format, do the actual output and set the |info| value accordingly.
+\item We combine the info value with the kind-value and return the correct tag.
+\item The tag value will be passed to |hput_tags| which generates
+debugging information, if requested, and stores the tag before and after the node content.
+\enditemize
+
+
+After these preparations, we turn our attention again to the |hput_glyph| function.
+The font number in a glyph node is between 0 and 255 and fits nicely in one byte,
+but the character code is more difficult: we want to store the most common character
+codes as a single byte and less frequent codes with two, three, or even four byte.
+Naturally, we use the |info| bits to store the number of bytes needed for the character code.
+
+\codesection{\putsymbol}{Writing the Short Format}\putindex{1}{2}{Glyphs}
+@<put functions@>=
+static uint8_t hput_n(uint32_t n)
+{@+ if (n<=0xFF) @+
+ {@+HPUT8(n);@+ return 1;@+}
+ else if (n<=0xFFFF) @+
+ {@+HPUT16(n);@+ return 2;@+}
+ else if (n<=0xFFFFFF)@+
+ {@+HPUT24(n);@+ return 3;@+}
+ else @+
+ {@+HPUT32(n);@+ return 4;@+}
+}
+
+uint8_t hput_glyph(glyph_t *g)
+{ info_t info;
+ info = hput_n(g->c);
+ HPUT8(g->f);@/
+ return TAG(glyph_kind,info);
+}
+@
+The |hput_tags| function is called after the node content has been written to the
+stream. It gets a the position of the start byte and the tag. With this information
+it writes the start byte at the given position and the end byte at the current stream position.
+@<put functions@>=
+void hput_tags(uint32_t pos, uint8_t tag)
+{ DBGTAG(tag,hstart+pos);DBGTAG(tag,hpos);
+ HPUTX(1); *(hstart+pos)=*(hpos++)=tag; @+
+}
+@
+
+
+
+The variables |hpos| and |hstart|, the macros |HPUT8|, |HPUT16|,
+|HPUT24|, |HPUT32|, and |HPUTX| are all defined in
+section~\secref{HPUT}; they put 8, 16, 24, or 32 bits into the output
+stream and check for sufficient space in the output buffer. The macro
+|DBGTAG| writes debugging output; its definition is found in
+section~\secref{error_section}.
+
+Now that we have seen the general outline of the \.{shrink} program,
+starting with a long format file and ending with a short format file,
+we will look at the program \.{stretch} that reverses this
+transformation.
+
+
+\subsection{Parsing the Short Format}
+The inverse of writing the short format with a {\it hput\_\kern 1pt\dots\/} function
+is reading the short format with a {\it hget\_\kern 1pt\dots\/} function.
+
+The schema of {\it hget\_\kern 1pt\dots\/} functions reverse the schema of {\it hput\_\kern 1pt\dots\/} functions.
+Here is the code for the initial and final part of a get function:
+
+@<read the start byte |a|@>=
+uint8_t a,z; /* the start and the end byte*/
+uint32_t node_pos=hpos-hstart;
+if (hpos>=hend) QUIT("Attempt to read a start byte at the end of the section");
+HGETTAG(a);@/@t{}@>
+@
+
+@<read and check the end byte |z|@>=
+HGETTAG(z);@+
+if (a!=z)
+ QUIT(@["Tag mismatch [%s,%d]!=[%s,%d] at 0x%x to " SIZE_F "\n"@],@|
+ NAME(a),INFO(a),NAME(z),INFO(z),@|node_pos, hpos-hstart-1);
+@
+
+
+The central routine to parse\index{parsing} the content section of a short format
+file is the function |hget_content_node| which calls |hget_content| to
+do most of the processing.
+
+|hget_content_node| will read a content node in short format and write
+it out in long format: It reads the start\index{start byte} byte |a|, writes the |START|
+token using the function |hwrite_start|, and based on |KIND(a)|, it
+writes the node's keyword found in the |content_name| array. Then it
+calls |hget_content| to read the node's content and write it out.
+Finally it reads the end\index{end byte} byte, checks it against the start byte, and
+finishes up the content node by writing the |END| token using the
+|hwrite_end| function. The function returns the tag byte so that
+the calling function might check that the content node meets its requirements.
+
+|hget_content| uses the start byte |a|, passed as a parameter, to
+branch directly to the reading routine for the given combination of
+kind and info value. The reading routine will read the data and store
+its internal representation in a variable. All that the \.{stretch}
+program needs to do with this internal representation is writing it in
+the long format. As we will see, the call to the proper
+{\it hwrite\_\kern 1pt\dots} function is included as final part of the the
+reading routine (avoiding another switch statement).
+
+
+\codesection{\getsymbol}{Reading the Short Format}\getindex{1}{2}{Content Nodes}
+@<get functions@>=
+void hget_content(uint8_t a);
+uint8_t hget_content_node(void)
+{ @<read the start byte |a|@>@;@+ hwrite_start();
+ hwritef("%s",content_name[KIND(a)]);
+ hget_content(a);@/
+ @<read and check the end byte |z|@>@; hwrite_end();
+ return a;
+}
+
+void hget_content(uint8_t a)
+{@+
+ switch (a)@/
+ {@+
+ @<cases to get content@>@;@t\1@>@/
+ default:
+ TAGERR(a);
+ break;@t\2@>@/
+ }
+}
+@
+
+We implement the code to read a glyph node in two stages.
+First we define a general reading macro |HGET_GLYPH(I,G)| that reads a glyph node with info value |I| into
+a |glyph_t| variable |G|; then we insert this macro
+in the above switch statement for all cases where it applies.
+Knowing the function |hput_glyph|, the macro |HGET_GLYPH| should not be a surprise.
+It reverses |hput_glyph|, storing the glyph node in its internal representation.
+After that, the \.{stretch} program calls |hwrite_glyph| to produce the glyph
+node in long format.
+
+\codesection{\getsymbol}{Reading the Short Format}\getindex{1}{2}{Glyphs}
+@<get macros@>=
+#define @[HGET_N(I,X)@] \
+ if ((I)==1) (X)=HGET8;\
+ else if ((I)==2) HGET16(X);\
+ else if ((I)==3) HGET24(X);\
+ else if ((I)==4) HGET32(X);
+
+#define @[HGET_GLYPH(I,G)@] \
+ HGET_N(I,(G).c); (G).f=HGET8; @+REF_RNG(font_kind,(G).f);@/\
+ hwrite_glyph(&(G));\
+@
+
+Note that we allow a glyph to reference a font even before that font is defined.
+This is necessary because fonts usually contain definitions---for example
+the fonts hyphen character---that reference this or other fonts.
+
+
+@<cases to get content@>=
+@t\1\kern1em@>case TAG(glyph_kind,1): @+{@+glyph_t g;@+ HGET_GLYPH(1,g);@+}@+break;
+case TAG(glyph_kind,2): @+{@+glyph_t g;@+ HGET_GLYPH(2,g);@+}@+break;
+case TAG(glyph_kind,3): @+{@+glyph_t g;@+ HGET_GLYPH(3,g);@+}@+break;
+case TAG(glyph_kind,4): @+{@+glyph_t g;@+ HGET_GLYPH(4,g);@+}@+break;
+@
+
+If this two stage method seems strange to you, consider what the \CEE\ compiler will
+do with it. It will expand the |HGET_GLYPH| macro four times inside the switch
+statement. The macro is, however, expanded with a constant |I| value, so the expansion
+of the |if| statement in |HGET_GLYPH(1,g)|, for example,
+will become ``|if (1==1)| \dots\ |else if (1==2)| \dots''
+and the compiler will have no difficulties eliminating the constant tests and
+the dead branches altogether. This is the most effective use of the switch statement:
+a single jump takes you to a specialized code to handle just the given combination
+of kind and info value.
+
+Last not least, we implement the function |hwrite_glyph| to write a
+glyph node in long form---that is: in a form that is as readable as possible.
+
+\subsection{Writing the Long Format}
+
+The |hwrite_glyph| function inverts the scanning and parsing process we have described
+at the very beginning of this chapter.
+To implement the |hwrite_glyph| function, we use the function |hwrite_charcode|
+to write the character code.
+Besides writing the character code as a decimal number, this function can handle also other
+representations of character codes as fully explained in section~\secref{chars}.
+We split off the writing of the opening and the closing pointed bracket, because
+we will need this function very often and because it will keep track of the |nesting|
+of nodes and indent them accordingly. The |hwrite_range| and |hwrite_label| functions
+used in |hwrite_end| are discussed in section~\secref{range} and~\secref{hwritelabel}.
+
+\codesection{\wrtsymbol}{Writing the Long Format}\wrtindex{1}{2}{Glyphs}
+@<write functions@>=
+int nesting=0;
+void hwrite_nesting(void)
+{ int i;
+ hwritec('\n');
+ for (i=0;i<nesting;i++) hwritec(' ');
+}
+
+void hwrite_start(void)
+{ @+hwrite_nesting();@+ hwritec('<');@+ nesting++;
+}
+
+void hwrite_range(void);
+void hwrite_label(void);
+
+void hwrite_end(void)
+{ nesting--; hwritec('>');
+ if (section_no==2)
+ { if (nesting==0) hwrite_range();
+ hwrite_label();
+ }
+}
+
+void hwrite_comment(char *str)
+{ char c;
+ if (str==NULL) return;
+ hwritef(" (");
+ while ((c=*str++)!=0)
+ if (c=='(' || c==')') hwritec('_');
+ else if (c=='\n') hwritef("\n(");
+ else hwritec(c);
+ hwritec(')');
+}
+
+void hwrite_charcode(uint32_t c);
+void hwrite_ref(int n);
+
+void hwrite_glyph(glyph_t *g)
+{ char *n=hfont_name[g->f];
+ hwrite_charcode(g->c);
+ hwrite_ref(g->f);
+ if (n!=NULL) hwrite_comment(n);
+}
+@
+
+The two primitive operations to write the long format file are defined
+as macros:
+
+@<write macros@>=
+#define @[hwritec(c)@] @[putc(c,hout)@]
+#define @[hwritef(...)@] @[fprintf(hout,__VA_ARGS__)@]
+@
+
+
+Now that we have completed the round trip of shrinking and stretching
+glyph nodes, we continue the description of the \HINT\ file formats
+in a more systematic way.
+
+
+\section{Data Types}\hascode
+\subsection{Integers}
+\label{integers}
+We have already seen the pattern/\kern -1pt action rule for unsigned decimal\index{decimal number} numbers. It remains
+to define the macro |SCAN_UDEC| which converts a string containing an unsigned\index{unsigned} decimal
+number into an unsigned integer\index{integer}.
+We use the \CEE\ library function | strtoul|:
+
+\readcode
+@<scanning macros@>=
+#define @[SCAN_UDEC(S)@] @[yylval.u=strtoul(S,NULL,10)@]
+@
+Unsigned integers can be given in hexadecimal\index{hexadecimal} notation as well.
+@<scanning definitions@>=
+::@=HEX@> :< @=[0-9A-F]@> >:
+@
+
+@<scanning rules@>=
+::@=0x{HEX}+@> :< SCAN_HEX(yytext+2); return UNSIGNED; >:
+@
+
+Note that the pattern above allows only upper case letters in the
+hexadecimal notation for integers.
+
+@<scanning macros@>=
+#define @[SCAN_HEX(S)@] @[yylval.u=strtoul(S,NULL,16)@]
+@
+
+Last not least, we add rules for signed\index{signed integer} integers.
+@s SIGNED symbol
+@s number symbol
+@s integer symbol
+
+@<symbols@>=
+%token <i> SIGNED
+%type <i> integer
+@
+
+@<scanning rules@>=
+::@=[+-](0|[1-9][0-9]*)@> :< SCAN_DEC(yytext); return SIGNED; >:
+@
+
+@<scanning macros@>=
+#define @[SCAN_DEC(S)@] @[yylval.i=strtol(S,NULL,10)@]
+@
+
+@<parsing rules@>=
+integer: SIGNED @+| UNSIGNED { RNG("number",$1,0,INT32_MAX);};
+@
+
+To preserve the ``signedness'' of an integer also for positive signed integers
+in the long format, we implement the function |hwrite_signed|.
+
+\writecode
+@<write functions@>=
+void hwrite_signed(int32_t i)
+{ if (i<0) hwritef(" -%d",-i);
+ else hwritef(" +%d",+i);
+}
+@
+
+Reading and writing integers in the short format is done directly with the {\tt HPUT} and {\tt HGET}
+macros.
+
+
+\subsection{Strings}
+\label{strings}
+Strings\index{string} are needed in the definition part of a \HINT\
+file to specify names of objects, and in the long file format, we also use them for file\index{file name} names.
+In the long format, strings are sequences of characters delimited by single quote\index{single quote} characters;
+for example: ``\.{'Hello'}'' or ``\.{'cmr10-600dpi.tfm'}''; in the short format, strings are
+byte sequences terminated by a zero byte.
+Because file names are system dependent, we no not allow arbitrary characters in strings
+but only printable ASCII codes which we can reasonably expect to be available on most operating systems.
+If your file names in a long format \HINT\ file are supposed to be portable,
+you should probably be even more restrictive. For example you should avoid characters like
+``\.{\\}'' or ``\.{/}'' which are used in different ways for directories.
+
+The internal representation of a string is a simple zero terminated \CEE\ string.
+When scanning a string, we copy it to the |str_buffer| keeping track
+of its length in |str_length|. When done,
+we make a copy for permanent storage and return the pointer to the parser.
+To operate on the |str_buffer|, we define a few macros.
+The constant |MAX_STR| determines the maximum size of a string (including the zero byte) to be $2^{10}$ byte.
+This restriction is part of the \HINT\ file format specification.
+
+@<scanning macros@>=
+#define MAX_STR (1<<10) /* $2^{10}$ Byte or 1kByte */
+static char str_buffer[MAX_STR];
+static int str_length;
+#define STR_START @[(str_length=0)@]
+#define @[STR_PUT(C)@] @[(str_buffer[str_length++]=(C))@]
+#define @[STR_ADD(C)@] @[STR_PUT(C);RNG("String length",str_length,0,MAX_STR-1)@]
+#define STR_END @[str_buffer[str_length]=0@]
+#define SCAN_STR @[yylval.s=str_buffer@]
+@
+
+
+To scan a string, we switch the scanner to |STR| mode when we find a quote character,
+then we scan bytes in the range |0x20| to |0x7E|, which is the range of printable ASCII
+characters, until we find the closing single\index{single quote} quote.
+Quote characters inside the string are written as two consecutive single quote characters.
+
+\readcode
+@s STRING symbol
+@s STR symbol
+@s INITIAL symbol
+
+@<scanning definitions@>=
+%x STR
+@
+
+@<symbols@>=
+%token <s> STRING
+@
+
+@<scanning rules@>=
+::@='@> :< STR_START; BEGIN(STR); >:
+<STR>{
+::@='@> :< STR_END; SCAN_STR; BEGIN(INITIAL); return STRING; >:
+::@=''@> :< STR_ADD('\''); >:
+::@=[\x20-\x7E]@> :< STR_ADD(yytext[0]); >:
+::@=.@> :< RNG("String character",yytext[0],0x20,0x7E); >:
+::@=\n@> :< QUIT("Unterminated String in line %d",yylineno); >:
+}
+
+
+@
+The function |hwrite_string| reverses this process; it must take care of the quote symbols.
+\writecode
+@<write functions@>=
+void hwrite_string(char *str)
+{@+hwritec(' ');
+ if (str==NULL) hwritef("''");
+ else@/
+ { hwritec('\'');
+ while (*str!=0)@/
+ { @+if (*str=='\'') hwritec('\'');
+ hwritec(*str++);
+ }
+ hwritec('\'');
+ }
+}
+
+
+@
+In the short format, a string is just a byte sequence terminated by a zero byte.
+This makes the function |hput_string|, to write a string, and the macro |HGET_STRING|,
+to read a string in short format, very simple. Note that after writing an unbounded
+string to the output buffer, the macro |HPUTNODE| will make sure that there is enough
+space left to write the remainder of the node.
+
+\putcode
+@<put functions@>=
+void hput_string(char *str)
+{ char *s=str;
+ if (s!=NULL)
+ { do {
+ HPUTX(1);
+ HPUT8(*s);
+ } while (*s++!=0);
+ HPUTNODE;
+ }
+ else HPUT8(0);
+}
+@
+
+\getcode
+@<shared get macros@>=
+#define @[HGET_STRING(S)@] @[S=(char*)hpos;\
+ while(hpos<hend && *hpos!=0) { RNG("String character",*hpos,0x20,0x7E); hpos++;}\
+ hpos++;
+@
+
+\subsection{Character Codes}
+\label{chars}
+We have already seen in the introduction that character\index{character code} codes can be written as decimal numbers
+and section~\secref{integers} adds the possibility to use hexadecimal numbers as well.
+
+It is, however, in most cases more readable if we represent character codes directly
+using the characters themselves. Writing ``\.{a}'' is just so much better than writing ``\.{97}''.
+To distinguish the character ``\.{9}'' from the number ``\.{9}'', we use the common technique
+of enclosing characters within single\index{single quote} quotes. So ``\.{'9'}'' is the character code and
+``\.{9}'' is the number.
+Therefore we will define |CHARCODE| tokens and complement the parsing rules of section~\secref{parse_glyph}
+with the following rule:
+\readcode
+@<parsing rules@>=
+glyph: CHARCODE REFERENCE @|{ $$.c=$1; REF(font_kind,$2); $$.f=$2; };
+@
+
+
+
+If the character codes are small, we can represent them using
+ASCII character codes. We do not offer a special notation for very small
+character codes that map to the non-printable ASCII control codes; for them, the decimal
+or hexadecimal notation will suffice.
+For larger character codes, we use the multibyte encoding scheme known from UTF8\index{UTF8} as
+follows. Given a character code~|c|:
+
+\itemize
+\item
+Values in the range |0x00| to |0x7f| are encoded as a single byte with a leading bit of 0.
+
+@<scanning definitions@>=
+::@=UTF8_1@> :< @=[\x00-\x7F]@> >:
+@
+@<scanning macros@>=
+#define @[SCAN_UTF8_1(S)@] @[yylval.u=((S)[0]&0x7F)@]
+@
+
+
+\item
+Values in the range |0x80| to |0x7ff| are encoded in two byte with the first byte
+having three high bits |110|, indicating a two byte sequence, and the lower five bits equal
+to the five high bits of |c|. It is followed by a continuation byte having two high bits |10|
+and the lower six bits
+equal to the lower six bits of |c|.
+
+@<scanning definitions@>=
+::@=UTF8_2@> :< @=[\xC0-\xDF][\x80-\xBF]@> >:
+@
+
+@<scanning macros@>=
+#define @[SCAN_UTF8_2(S)@] @[yylval.u=(((S)[0]&0x1F)<<6)+((S)[1]&0x3F)@]
+@
+
+\item
+Values in the range |0x800| to |0xFFFF| are encoded in three byte with the first byte
+having the high bits |1110| indicating a three byte sequence followed by two continuation bytes.
+
+@<scanning definitions@>=
+::@=UTF8_3@> :< @=[\xE0-\xEF][\x80-\xBF][\x80-\xBF]@> >:
+@
+
+@<scanning macros@>=
+#define @[SCAN_UTF8_3(S)@] @[yylval.u=(((S)[0]&0x0F)<<12)+(((S)[1]&0x3F)<<6)+((S)[2]&0x3F)@]
+@
+
+\item
+Values in the range |0x1000| to |0x1FFFFF| are encoded in four byte with the first byte
+having the high bits |11110| indicating a four byte sequence followed by three continuation bytes.
+
+@<scanning definitions@>=
+::@=UTF8_4@> :< @=[\xF0-\xF7][\x80-\xBF][\x80-\xBF][\x80-\xBF]@> >:
+@
+
+@<scanning macros@>=
+#define @[SCAN_UTF8_4(S)@] @[yylval.u=(((S)[0]&0x03)<<18)+(((S)[1]&0x3F)<<12)+@|(((S)[2]&0x3F)<<6)+((S)[3]&0x3F)@]
+@
+
+\enditemize
+
+In the long format file, we enclose a character code in single\index{single quote} quotes, just as we do for strings.
+This is convenient but it has the downside that we must exercise special care when giving the
+scanning rules in order
+not to confuse character codes with strings. Further we must convert character codes back into strings
+in the rare case where the parser expects a string and gets a character code because the string
+was only a single character long.
+
+Let's start with the first problem:
+The scanner might confuse a string\index{string} and a character code if the first or second
+character of the string is a quote character which is written as two consecutive quotes.
+For example \.{'a''b'} is a string with three characters, ``\.{a}'',
+``\.{'}'', and ``\.{b}''. Two character codes would need a space to separate
+them like this: \.{'a' 'b'}.
+
+
+@s CHARCODE symbol
+@<symbols@>=
+%token <u> CHARCODE
+@
+
+@<scanning rules@>=
+::@='''@> :< STR_START; STR_PUT('\''); BEGIN(STR); >:
+::@=''''@> :< SCAN_UTF8_1(yytext+1); return CHARCODE; >:
+::@='[\x20-\x7E]''@> :< STR_START; STR_PUT(yytext[1]); STR_PUT('\''); BEGIN(STR); >:
+::@='''''@> :< STR_START; STR_PUT('\''); STR_PUT('\''); BEGIN(STR); >:
+::@='{UTF8_1}'@> :< SCAN_UTF8_1(yytext+1); return CHARCODE; >:
+::@='{UTF8_2}'@> :< SCAN_UTF8_2(yytext+1); return CHARCODE; >:
+::@='{UTF8_3}'@> :< SCAN_UTF8_3(yytext+1); return CHARCODE; >:
+::@='{UTF8_4}'@> :< SCAN_UTF8_4(yytext+1); return CHARCODE; >:
+@
+
+If needed, the parser can convert character codes back to single character strings.
+
+@s string symbol
+
+@<symbols@>=
+%type <s> string
+@
+
+@<parsing rules@>=
+string: STRING @+ | CHARCODE { static char s[2];
+ RNG("String element",$1,0x20,0x7E);
+ s[0]=$1; s[1]=0; $$=s;};
+@
+
+
+The function |hwrite_charcode| will write a character code. While ASCII codes are handled directly,
+larger character codes are passed to the function |hwrite_utf8|.
+It returns the number of characters written.
+
+\writecode
+@<write functions@>=
+int hwrite_utf8(uint32_t c)
+{@+ if (c<0x80)
+ { hwritec(c); @+return 1;@+ }
+ else if (c<0x800)@/
+ { hwritec(0xC0|(c>>6));@+ hwritec(0x80|(c&0x3F));@+ return 2;@+}
+ else if (c<0x10000)@/
+ { hwritec(0xE0|(c>>12)); hwritec(0x80|((c>>6)&0x3F));@+ hwritec(0x80|(c&0x3F)); return 3; }
+ else if (c<0x200000)@/
+ { hwritec(0xF0|(c>>18));@+ hwritec(0x80|((c>>12)&0x3F));
+ hwritec(0x80|((c>>6)&0x3F));@+ hwritec(0x80|(c&0x3F)); return 4;}
+ else
+ RNG("character code",c,0,0x1FFFFF);
+ return 0;
+}
+
+void hwrite_charcode(uint32_t c)
+{ @+if (c < 0x20)
+ { if (option_hex) hwritef(" 0x%02X",c); /* non printable ASCII */
+ else hwritef(" %u",c);
+ }
+ else if (c=='\'') hwritef(" ''''");
+ else if (c<=0x7E) hwritef(" \'%c\'",c); /* printable ASCII */
+ else if (option_utf8) { hwritef(" \'"); @+ hwrite_utf8(c); @+ hwritec('\'');@+}
+ else if (option_hex) hwritef(" 0x%04X",c);
+ else hwritef(" %u",c);
+}
+@
+
+\getcode
+@<shared get functions@>=
+#define @[HGET_UTF8C(X)@] (X)=HGET8;@+ if ((X&0xC0)!=0x80) \
+ QUIT(@["UTF8 continuation byte expected at " SIZE_F " got 0x%02X\n"@],hpos-hstart-1,X)@;
+
+uint32_t hget_utf8(void)
+{ uint8_t a;
+ a=HGET8;
+ if (a<0x80) return a;
+ else
+ { if ((a&0xE0)==0xC0) @/
+ { uint8_t b; @+ HGET_UTF8C(b);
+ return ((a&~0xE0)<<6)+(b&~0xC0);
+ }
+ else if ((a&0xF0)==0xE0) @/
+ { uint8_t b,c; @+ HGET_UTF8C(b); @+ HGET_UTF8C(c);
+ return ((a&~0xF0)<<12)+((b&~0xC0)<<6)+(c&~0xC0);
+ }
+ else if ((a&0xF8)==0xF0) @/
+ { uint8_t b,c,d; @+ HGET_UTF8C(b); @+ HGET_UTF8C(c); @+ HGET_UTF8C(d);
+ return ((a&~0xF8)<<18)@|+ ((b&~0xC0)<<12)+((c&~0xC0)<<6)+(d&~0xC0);
+ }
+ else QUIT("UTF8 byte sequence expected");
+ }
+}
+@
+\putcode
+@<put functions@>=
+void hput_utf8(uint32_t c)
+{ @+HPUTX(4);
+ if (c<0x80)
+ HPUT8(c);
+ else if (c<0x800)
+ { HPUT8(0xC0|(c>>6));@+ HPUT8(0x80|(c&0x3F));@+ }
+ else if (c<0x10000)@/
+ { HPUT8(0xE0|(c>>12)); HPUT8(0x80|((c>>6)&0x3F));@+ HPUT8(0x80|(c&0x3F)); }
+ else if (c<0x200000)@/
+ { HPUT8(0xF0|(c>>18));@+ HPUT8(0x80|((c>>12)&0x3F));
+ HPUT8(0x80|((c>>6)&0x3F));@+ HPUT8(0x80|(c&0x3F)); }
+ else
+ RNG("character code",c,0,0x1FFFFF);
+}
+@
+
+\subsection{Floating Point Numbers}
+You know a floating point numbers\index{floating point number} when you see it because it features a radix\index{radix point} point.
+The optional exponent\index{exponent} allows you to ``float'' the point.
+
+\readcode
+@s FPNUM symbol
+@s number symbol
+
+@<symbols@>=
+%token <f> FPNUM
+%type <f> number
+@
+@<scanning rules@>=
+::@=[+-]?[0-9]+\.[0-9]+(e[+-]?[0-9])?@> :< SCAN_DECFLOAT; return FPNUM; >:
+@
+
+The layout of floating point variables of type |double|
+or |float| typically follows the IEEE754\index{IEEE754} standard\cite{IEEE754-1985}\cite{IEEE754-2008}.
+We use the following definitions:
+
+\index{float32 t+\&{float32\_t}}
+\index{float64 t+\&{float64\_t}}
+
+@<hint basic types@>=
+
+#define FLT_M_BITS 23
+#define FLT_E_BITS 8
+#define FLT_EXCESS 127
+
+#define DBL_M_BITS 52
+#define DBL_E_BITS 11
+#define DBL_EXCESS 1023
+
+@
+
+@s float32_t int
+@s float64_t int
+
+We expect a variable of type |float64_t| to have a binary representation using 64 bit.
+The most significant bit is the sign bit, then follow $|DBL_E_BITS|=11$ bits for
+the exponent\index{exponent}, and $|DBL_M_BITS|=52$ bits for the mantissa\index{mantissa}.
+The sign\index{sign bit} bit is 1 for a negative number and 0 for a positive number.
+A floating point number is stored in normalized\index{normalization} form which means that
+the mantissa is shifted such that it has exactly 52+1 bit not counting leading zeros.
+The leading bit is then always 1 and there is no need to store it. So 52 bits suffice.
+To store the exponent, the excess $q=1023$ is added and the result is stored as an
+unsigned 11 bit number.
+For example if we regard the exponent bits and the mantissa bits as unsigned binary numbers
+$e$ and $m$ then the absolute value of such a floating point number can be expressed
+as $(1+m*2^{-52})\cdot2^{e-1023}$.
+We make similar assumptions about variables of type |float32_t| using the constants as defined above.
+
+
+
+
+
+To convert the decimal representation of a floating point number to binary values of type |float64_t|,
+we use a \CEE\ library function.
+
+@<scanning macros@>=
+#define SCAN_DECFLOAT @[yylval.f=atof(yytext)@]
+@
+
+When the parser expects a floating point number and gets an integer number,
+it converts it. So whenever in the long format a floating point number is expected,
+an integer number will do as well.
+
+@<parsing rules@>=
+number: UNSIGNED {$$=(float64_t)$1; } | SIGNED {$$=(float64_t)$1; } | FPNUM;
+@
+
+Unfortunately the decimal representation is not optimal for floating point numbers
+since even simple numbers in decimal notation like $0.1$ do not have an exact
+representation as a binary floating point number.
+So if we want a notation that allows an exact representation
+of binary floating point numbers, we must use a hexadecimal\index{hexadecimal} representation.
+Hexadecimal floating point numbers start with an optional sign, then as usual the two characters ``{\tt 0x}'',
+then follows a sequence of hex digits, a radix point, more hex digits, and an optional exponent.
+The optional exponent starts with the character ``{\tt x}'', followed by an optional sign, and some more
+hex digits. The hexadecimal exponent is given as a base 16 number and it is interpreted as an exponent
+with the base 16. As an example an exponent of ``{\tt x10}'', would multiply the mantissa by $16^{16}$.
+In other words it would shift any mantissa 16 hexadecimal digits to the left. Here are the exact rules:
+
+@<scanning rules@>=
+::@=[+-]?0x{HEX}+\.{HEX}+(x[+-]?{HEX}+)?@> :< SCAN_HEXFLOAT; return FPNUM; >:
+@
+
+@<scanning macros@>=
+#define SCAN_HEXFLOAT @[yylval.f=xtof(yytext)@]
+@
+There is no function in the \CEE\ library for hexadecimal floating point notation
+so we have to write our own conversion routine.
+The function |xtof| converts a string matching the above regular expression to
+its binary representation. Its outline is very simple:
+
+@<scanning functions@>=
+
+float64_t xtof(char *x)
+{ int sign, digits, exp;
+ uint64_t mantissa=0;
+ DBG(DBGFLOAT,"converting %s:\n",x);
+ @<read the optional sign@>@;
+ x=x+2; /* skip ``\.{0x}'' */
+ @<read the mantissa@>@;
+ @<normalize the mantissa@>@;
+ @<read the optional exponent@>@;
+ @<return the binary representation@>@;
+}
+@
+
+Now the pieces:
+
+@<read the optional sign@>=
+ if (*x=='-') { sign=-1;@+ x++;@+ }
+ else if (*x=='+') { sign=+1;@+ x++;@+ }
+ else @+sign=+1;
+ DBG(DBGFLOAT,"\tsign=%d\n",sign);
+@
+
+When we read the mantissa, we use the temporary variable |mantissa|, keep track
+of the number of digits, and adjust the exponent while reading the fractional part.
+@<read the mantissa@>=
+ digits=0;
+ while (*x=='0') x++; /*ignore leading zeros*/
+ while (*x!='.')@/
+ { mantissa=mantissa<<4;
+ if (*x<'A') mantissa=mantissa+*x-'0';
+ else mantissa=mantissa+*x-'A'+10;
+ x++;
+ digits++;
+ }
+ x++; /* skip ``\.{.}'' */
+ exp=0;
+ while (*x!=0 && *x!='x')@/
+ { mantissa=mantissa<<4;
+ exp=exp-4;
+ if (*x<'A') mantissa=mantissa+*x-'0';
+ else mantissa=mantissa+*x-'A'+10;
+ x++;
+ digits++;
+ }
+ DBG(DBGFLOAT,"\tdigits=%d mantissa=0x%" PRIx64 ", exp=%d\n",@|digits,mantissa,exp);
+@
+
+To normalize the mantissa, first we shift it to place exactly one nonzero hexadecimal
+digit to the left of the radix point. Then we shift it right bit-wise until there is
+just a single 1 bit to the left of the radix point.
+To compensate for the shifting, we adjust the exponent accordingly.
+Finally we remove the most significant bit because it is
+not stored.
+
+@<normalize the mantissa@>=
+if (mantissa==0) return 0.0;
+{ int s;
+ s = digits-DBL_M_BITS/4;
+ if (s>1)
+ mantissa=mantissa>>(4*(s-1));
+ else if (s<1)
+ mantissa=mantissa<<(4*(1-s));
+ exp=exp+4*(digits-1);
+ DBG(DBGFLOAT,"\tdigits=%d mantissa=0x%" PRIx64 ", exp=%d\n",@|digits,mantissa,exp);
+ while ((mantissa>>DBL_M_BITS)>1)@/ { mantissa=mantissa>>1; @+ exp++;@+ }
+ DBG(DBGFLOAT,"\tdigits=%d mantissa=0x%" PRIx64 ", exp=%d\n",@|digits,mantissa,exp);
+ mantissa=mantissa&~((uint64_t)1<<DBL_M_BITS);
+ DBG(DBGFLOAT,"\tdigits=%d mantissa=0x%" PRIx64 ", exp=%d\n",@|digits,mantissa,exp);
+}
+@
+
+In the printed representation,
+the exponent is an exponent with base 16. For example, an exponent of 2 shifts
+the hexadecimal mantissa two hexadecimal digits to the left, which corresponds to a
+multiplication by ${16}^2$.
+
+@<read the optional exponent@>=
+ if (*x=='x')@/
+ { int s;
+ x++; /* skip the ``\.{x}'' */
+ if (*x=='-') {s=-1;@+x++;@+}
+ else if (*x=='+') {s=+1;@+x++;@+}
+ else s=+1;
+ DBG(DBGFLOAT,"\texpsign=%d\n",s);
+ DBG(DBGFLOAT,"\texp=%d\n",exp);
+ while (*x!=0 )
+ { if (*x<'A') exp=exp+4*s*(*x-'0');
+ else exp=exp+4*s*(*x-'A'+10);
+ x++;
+ DBG(DBGFLOAT,"\texp=%d\n",exp);
+ }
+ }
+ RNG("Floating point exponent",@|exp,-DBL_EXCESS,DBL_EXCESS);
+@
+
+To assemble the binary representation, we use a |union| of a |float64_t| and |uint64_t|.
+
+
+@<return the binary representation@>=
+{ union {@+float64_t d; @+uint64_t bits; @+} u;
+ if (sign<0) sign=1;@+ else@+ sign=0; /* the sign bit */
+ exp=exp+DBL_EXCESS; /* the exponent bits */
+ u.bits=((uint64_t)sign<<63)@/
+ | ((uint64_t)exp<<DBL_M_BITS) | mantissa;
+ DBG(DBGFLOAT," return %f\n",u.d);
+ return u.d;
+}
+@
+
+The inverse function is |hwrite_float64|. It strives to print floating point numbers
+as readable as possible. So numbers without fractional part are written as integers.
+Numbers that can be represented exactly in decimal notation are represented in
+decimal notation. All other values are written as hexadecimal floating point numbers.
+We avoid an exponent if it can be avoided by using up to |MAX_HEX_DIGITS|
+
+\writecode
+@<write functions@>=
+#define MAX_HEX_DIGITS 12
+void hwrite_float64(float64_t d)
+{ uint64_t bits, mantissa;
+ int exp, digits;
+ hwritec(' ');
+ if (floor(d)==d)
+ { hwritef("%d",(int)d);@+ return;@+}
+ if (floor(10000.0*d)==10000.0*d)
+ { hwritef("%g",d); @+return;@+}
+ DBG(DBGFLOAT,"Writing hexadecimal float %f\n",d);
+ if (d<0) { hwritec('-');@+ d=-d;@+}
+ hwritef("0x");
+ @<extract mantissa and exponent@>@;
+ if (exp>MAX_HEX_DIGITS)
+ @<write large numbers@>@;
+ else if (exp>=0) @<write medium numbers@>@;
+ else @<write small numbers@>@;
+}
+@
+
+The extraction just reverses the creation of the binary representation.
+
+@<extract mantissa and exponent@>=
+{ union {@+float64_t d; @+ uint64_t bits; @+} u;
+ u.d=d; @+ bits=u.bits;
+}
+ mantissa= bits&(((uint64_t)1<<DBL_M_BITS)-1);
+ mantissa=mantissa+((uint64_t)1<<DBL_M_BITS);
+ exp= ((bits>>DBL_M_BITS)&((1<<DBL_E_BITS)-1))-DBL_EXCESS;
+ digits=DBL_M_BITS+1;
+ DBG(DBGFLOAT,"\tdigits=%d mantissa=0x%" PRIx64 " binary exp=%d\n",@|digits,mantissa,exp);
+@
+
+After we have obtained the binary exponent,
+we round it down, and convert it to a hexadecimal
+exponent.
+@<extract mantissa and exponent@>=
+ { int r;
+ if (exp>=0)
+ { r= exp%4;
+ if (r>0)
+ { mantissa=mantissa<<r; @+exp=exp-r; @+digits=digits+r; @+}
+ }
+ else
+ { r=(-exp)%4;
+ if (r>0)
+ { mantissa=mantissa>>r; @+exp=exp+r; @+digits=digits-r;@+}
+ }
+ }
+ exp=exp/4;
+ DBG(DBGFLOAT,"\tdigits=%d mantissa=0x%" PRIx64 " hex exp=%d\n",@|digits,mantissa,exp);
+@
+
+In preparation for writing,
+we shift the mantissa to the left so that the leftmost hexadecimal
+digit of it will occupy the 4 leftmost bits of the variable |bits| .
+
+@<extract mantissa and exponent@>=
+ mantissa=mantissa<<(64-DBL_M_BITS-4); /* move leading digit to leftmost nibble */
+@
+
+If the exponent is larger than |MAX_HEX_DIGITS|, we need to
+use an exponent even if the mantissa uses only a few digits.
+When we use an exponent, we always write exactly one digit preceding the radix point.
+
+@<write large numbers@>=
+{ DBG(DBGFLOAT,"writing large number\n");
+ hwritef("%X.",(uint8_t)(mantissa>>60));
+ mantissa=mantissa<<4;
+ do {
+ hwritef("%X",(uint8_t)(mantissa>>DBL_M_BITS)&0xF);
+ mantissa=mantissa<<4;
+ } while (mantissa!=0);
+ hwritef("x+%X",exp);
+}
+@
+If the exponent is small and non negative, we can write the
+number without an exponent by writing the radix point at the
+appropriate place.
+ @<write medium numbers@>=
+ { DBG(DBGFLOAT,"writing medium number\n");
+ do {
+ hwritef("%X",(uint8_t)(mantissa>>60));
+ mantissa=mantissa<<4;
+ if (exp--==0) hwritec('.');
+ } while (mantissa!=0 || exp>=-1);
+ }
+@
+Last non least, we write numbers that would require additional zeros after the
+radix point with an exponent, because it keeps the mantissa shorter.
+@<write small numbers@>=
+ { DBG(DBGFLOAT,"writing small number\n");
+ hwritef("%X.",(uint8_t)(mantissa>>60));
+ mantissa=mantissa<<4;
+ do {
+ hwritef("%X",(uint8_t)(mantissa>>60));
+ mantissa=mantissa<<4;
+ } while (mantissa!=0);
+ hwritef("x-%X",-exp);
+ }
+@
+
+Compared to the complications of long format floating point numbers,
+the short format is very simple because we just use the binary representation.
+Since 32 bit floating point numbers offer sufficient precision we use only
+the |float32_t| type.
+It is however not possible to just write |HPUT32(d)| for a |float32_t| variable |d|
+or |HPUT32((uint32_t)d)| because in the \CEE\ language this would imply
+rounding the floating point number to the nearest integer.
+But we have seen how to convert floating point values to bit pattern before.
+
+@<put functions@>=
+void hput_float32(float32_t d)
+{ union {@+float32_t d; @+ uint32_t bits; @+} u;
+ u.d=d; @+ HPUT32(u.bits);
+}
+@
+
+@<shared get functions@>=
+float32_t hget_float32(void)
+{ union {@+float32_t d; @+ uint32_t bits; @+} u;
+ HGET32(u.bits);
+ return u.d;
+}
+@
+
+\subsection{Fixed Point Numbers}
+\TeX\ internally represents most real numbers as fixed\index{fixed point number} point numbers or ``scaled integers''\index{scaled integer}.
+The type {\bf scaled\_t} is defined as a signed 32 bit integer, but we consider it as a fixed point number
+with the binary radix point just in the middle with sixteen bits before and sixteen bits after it.
+To convert an integer into a scaled number, we multiply it by |ONE|; to convert a floating point number
+into a scaled number, we multiply it by |ONE| and |ROUND| the result to the nearest integer;
+to convert a scaled number to a floating point number we divide it by |(float64_t)ONE|.
+
+\noindent
+@<hint basic types@>=
+typedef int32_t scaled_t;
+#define ONE ((scaled_t)(1<<16))
+@
+
+@<hint macros@>=
+#define ROUND(X) ((int)((X)>=0.0?floor((X)+0.5):ceil((X)-0.5)))
+@
+
+\writecode
+@<write functions@>=
+void hwrite_scaled(scaled_t x)
+{ hwrite_float64(x/(float64_t)ONE);
+}
+@
+
+\subsection{Dimensions}
+In the long format,
+the dimensions\index{dimension} of characters, boxes, and other things can be given
+in three units: \.{pt}, \.{in}, and \.{mm}.
+
+\readcode
+@s PT symbol
+@s MM symbol
+@s INCH symbol
+@s dimension symbol
+@s DIMEN symbol
+@<symbols@>=
+%token DIMEN "dimen"
+%token PT "pt"
+%token MM "mm"
+%token INCH "in"
+%type <d> dimension
+@
+
+@<scanning rules@>=
+::@=dimen@> :< return DIMEN; >:
+::@=pt@> :< return PT; >:
+::@=mm@> :< return MM; >:
+::@=in@> :< return INCH; >:
+@
+
+The unit \.{pt} is a printers point\index{point}\index{pt+{\tt pt}}.
+The unit ``\.{in}'' stands for inches\index{inch}\index{in+{\tt in}} and we have $1\.{in}= 72.27\,\.{pt}$.
+The unit ``\.{mm}'' stands for millimeter\index{millimeter}\index{mm+{\tt mm}} and we have $1\.{in}= 25.4\,\.{mm}$.
+
+The definition of a printers\index{printers point} point given above follows the definition used in
+\TeX\ which is slightly larger than the official definition of a printer's
+point which was defined to equal exactly 0.013837\.{in} by the American Typefounders
+Association in~1886\cite{DK:texbook}.
+
+We follow the tradition of \TeX\ and
+store dimensions as ``scaled points''\index{scaled point} that is a dimension of $d$ points is
+stored as $d\cdot2^{16}$ rounded to the nearest integer.
+The maximum absolute value of a dimension is $(2^{30}-1)$ scaled points.
+
+@<hint basic types@>=
+typedef scaled_t dimen_t;
+#define MAX_DIMEN ((dimen_t)(0x3FFFFFFF))
+@
+
+@<parsing rules@>=
+dimension: number PT @|{$$=ROUND($1*ONE); RNG("Dimension",$$,-MAX_DIMEN,MAX_DIMEN); }
+ | number INCH @|{$$=ROUND($1*ONE*72.27); RNG("Dimension",$$,-MAX_DIMEN,MAX_DIMEN);@+}
+ | number MM @|{$$=ROUND($1*ONE*(72.27/25.4)); RNG("Dimension",$$,-MAX_DIMEN,MAX_DIMEN);@+};
+@
+
+When \.{stretch} is writing dimensions in the long format,
+for simplicity it always uses the unit ``\.{pt}''.
+\writecode
+@<write functions@>=
+void hwrite_dimension(dimen_t x)
+{ hwrite_scaled(x);
+ hwritef("pt");
+}
+@
+
+In the short format, dimensions are stored as 32 bit scaled point values without conversion.
+\getcode
+@<get functions@>=
+void hget_dimen(uint8_t a)
+{ if (INFO(a)==b000)
+ {uint8_t r; r=HGET8; REF(dimen_kind,r); hwrite_ref(r);}
+ else
+ { uint32_t d; HGET32(d); hwrite_dimension(d); }
+}
+@
+
+\putcode
+@<put functions@>=
+
+uint8_t hput_dimen(dimen_t d)
+{ HPUT32(d);
+ return TAG(dimen_kind, b001);
+}
+@
+
+
+
+\subsection{Extended Dimensions}\index{extended dimension}\index{hsize+{\tt hsize}}\index{vsize+{\tt vsize}}
+The dimension that is probably used most frequently in a \TeX\ file is {\tt hsize}:
+the ho\-ri\-zon\-tal size of a line of text. Common are also assignments
+like \.{\\hsize=0.5\\hsize} \.{\\advance\\hsize by -10pt}, for example to
+get two columns with lines almost half as wide as usual, leaving a small gap
+between left and right column. Similar considerations apply to {\tt vsize}.
+
+Because we aim at a reflowable format for \TeX\ output, we have to postpone
+such computations until the values of \.{hsize} and \.{vsize} are known in the viewer.
+Until then, we do symbolic computations on linear functions\index{linear function} of \.{hsize} and \.{vsize}.
+We call such a linear function $w+h\cdot\.{hsize}+v\cdot\.{vsize}$
+an extended dimension and represent it by the three numbers $w$, $h$, and $v$.
+
+@<hint basic types@>=
+typedef struct {@+
+dimen_t w; @+ float32_t h, v; @+
+} xdimen_t;
+@
+Since very often a component of an extended dimension is zero, we
+store in the short format only the nonzero components and use the
+info bits to mark them: |b100| implies $|w|\ne0$,
+|b010| implies $|h|\ne 0$, and |b001| implies $|v|\ne 0$.
+
+\readcode
+@s XDIMEN symbol
+@s xdimen symbol
+@s xdimen_node symbol
+@s H symbol
+@s V symbol
+@<symbols@>=
+%token XDIMEN "xdimen"
+%token H "h"
+%token V "v"
+%type <xd> xdimen
+@
+@<scanning rules@>=
+::@=xdimen@> :< return XDIMEN; >:
+::@=h@> :< return H; >:
+::@=v@> :< return V; >:
+@
+
+
+@<parsing rules@>=
+xdimen: dimension number H number V { $$.w=$1; @+$$.h=$2; @+$$.v=$4; }
+ | dimension number H { $$.w=$1; @+$$.h=$2; @+$$.v=0.0; }
+ | dimension number V { $$.w=$1; @+$$.h=0.0; @+$$.v=$2; }
+ | dimension { $$.w=$1; @+$$.h=0.0; @+$$.v=0.0; };
+
+
+
+xdimen_node: start XDIMEN xdimen END { hput_tags($1,hput_xdimen(&($3))); };
+@
+
+\writecode
+@<write functions@>=
+void hwrite_xdimen(xdimen_t *x)
+{ hwrite_dimension(x->w);
+ if (x->h!=0.0) {hwrite_float64(x->h); @+hwritec('h');@+}
+ if (x->v!=0.0) {hwrite_float64(x->v); @+hwritec('v');@+}
+}
+
+void hwrite_xdimen_node(xdimen_t *x)
+{ hwrite_start(); hwritef("xdimen"); hwrite_xdimen(x); hwrite_end();}
+@
+
+\getcode
+
+@<get macros@>=
+#define @[HGET_XDIMEN(I,X)@] \
+ if((I)&b100) HGET32((X).w);@+ else (X).w=0;\
+ if((I)&b010) (X).h=hget_float32(); @+ else (X).h=0.0;\
+ if((I)&b001) (X).v=hget_float32(); @+else (X).v=0.0;
+@
+
+@<get functions@>=
+void hget_xdimen(uint8_t a, xdimen_t *x)
+{ switch(a)
+ {
+ case TAG(xdimen_kind,b001): HGET_XDIMEN(b001,*x);@+break;
+ case TAG(xdimen_kind,b010): HGET_XDIMEN(b010,*x);@+break;
+ case TAG(xdimen_kind,b011): HGET_XDIMEN(b011,*x);@+break;
+ case TAG(xdimen_kind,b100): HGET_XDIMEN(b100,*x);@+break;
+ case TAG(xdimen_kind,b101): HGET_XDIMEN(b101,*x);@+break;
+ case TAG(xdimen_kind,b110): HGET_XDIMEN(b110,*x);@+break;
+ case TAG(xdimen_kind,b111): HGET_XDIMEN(b111,*x);@+break;
+ default:
+ QUIT("Extent expected got [%s,%d]",NAME(a),INFO(a));
+ }
+ }
+@
+
+Note that the info value |b000|, usually indicating a reference,
+is not supported for extended dimensions.
+Most nodes that need an extended dimension offer the opportunity to give
+a reference directly without the start and end byte. An exception is the glue node,
+but glue nodes that need an extended width are rare.
+
+@<get functions@>=
+void hget_xdimen_node(xdimen_t *x)
+{ @<read the start byte |a|@>@;
+ if (KIND(a)==xdimen_kind)
+ hget_xdimen(a,x);
+ else
+ QUIT("Extent expected at 0x%x got %s",node_pos,NAME(a));
+ @<read and check the end byte |z|@>@;
+}
+@
+
+
+
+\putcode
+@<put functions@>=
+uint8_t hput_xdimen(xdimen_t *x)
+{ info_t info=b000;
+ if (x->w==0 && x->h==0.0 && x->v==0.0){ HPUT32(0); @+info|=b100; @+}
+ else
+ { if (x->w!=0) { HPUT32(x->w); @+info|=b100; @+}
+ if (x->h!=0.0) { hput_float32(x->h); @+info|=b010; @+}
+ if (x->v!=0.0) { hput_float32(x->v); @+info|=b001; @+}
+ }
+ return TAG(xdimen_kind,info);
+}
+void hput_xdimen_node(xdimen_t *x)
+{ uint32_t p=hpos++-hstart;
+ hput_tags(p, hput_xdimen(x));
+}
+
+
+@
+
+
+
+\subsection{Stretch and Shrink}\label{stretch}
+In section~\secref{glue}, we will consider glue\index{glue} which
+is something that can stretch and shrink.
+The stretchability\index{stretchability} and shrinkability\index{shrinkability} of the
+glue can be given in ``\.{pt}'' like a dimension,
+but there are three more units: \.{fil}, \.{fill}, and \.{filll}.
+A glue with a stretchability of $1\,\hbox{\tt fil}$ will stretch infinitely more
+than a glue with a stretchability of $1\,\hbox{\tt pt}$. So if you stretch both glues
+together, the first glue will do all the stretching and the latter will not stretch
+at all. The ``\.{fil}'' glue has simply a higher order of infinity.
+You might guess that ``\.{fill}'' glue and ``\.{filll}'' glue have even higher
+orders of infinite stretchability.
+The order of infinity is 0 for \.{pt}, 1 for \.{fil}, 2 for \.{fill}, and 3 for \.{filll}.
+
+The internal representation of a stretch is a variable of type |stretch_t|.
+It stores the floating point value and the order of infinity separate as a |float64_t| and a |uint8_t|.
+
+
+The short format tries to be space efficient and because it is not necessary to give the
+stretchability with a precision exceeding about six decimal digits,
+we use a single 32 bit floating point value.
+To write a |float32_t| value and an order value as one 32 bit value,
+we round the two lowest bit of the |float32_t| variable to zero
+using ``round to even'' and store the order of infinity in these bits.
+We define a union type \&{stch\_t} to simplify conversion.
+
+@<hint basic types@>=
+typedef enum { @+ normal_o=0, fil_o=1, fill_o=2, filll_o=3@+} order_t;
+typedef struct {@+ float64_t f;@+ order_t o; @+} stretch_t;
+typedef union {@+float32_t f; @+ uint32_t u; @+} stch_t;
+@
+
+\putcode
+@<put functions@>=
+void hput_stretch(stretch_t *s)
+{ uint32_t mantissa, lowbits, sign, exponent;
+ stch_t st;
+ st.f=s->f;
+ DBG(DBGFLOAT,"joining %f->%f(0x%X),%d:",s->f,st.f,st.u,s->o);
+ mantissa = st.u &(((uint32_t)1<<FLT_M_BITS)-1);
+ lowbits = mantissa&0x7; /* lowest 3 bits */
+ exponent=(st.u>>FLT_M_BITS)&(((uint32_t)1<<FLT_E_BITS)-1);
+ sign=st.u & ((uint32_t)1<<(FLT_E_BITS+FLT_M_BITS));
+ DBG(DBGFLOAT,"s=%d e=0x%x m=0x%x",sign, exponent, mantissa);
+ switch (lowbits) /* round to even */
+ { @+case 0: break; /* no change */
+ case 1: mantissa = mantissa -1; @+break;/* round down */
+ case 2: mantissa = mantissa -2; @+break;/* round down to even */
+ case 3: mantissa = mantissa +1; @+break; /* round up */
+ case 4: break; /* no change */
+ case 5: mantissa = mantissa -1; @+break;/* round down */
+ case 6: mantissa = mantissa +1; /* round up to even, fall through */
+ case 7: mantissa = mantissa +1; /* round up to even */
+ if (mantissa >= ((uint32_t)1<<FLT_M_BITS))@/
+ {exponent++; /* adjust exponent */
+ RNG("Float32 exponent",exponent,1,2*FLT_EXCESS);
+ @+mantissa=mantissa>>1;
+ }
+ break;
+ }
+ DBG(DBGFLOAT," round s=%d e=0x%x m=0x%x",sign, exponent, mantissa);
+ st.u=sign| (exponent<<FLT_M_BITS) | mantissa | s->o;
+ DBG(DBGFLOAT,"float %f hex 0x%x\n",st.f, st.u);
+ HPUT32(st.u);
+}
+@
+
+\getcode
+@<get macros@>=
+#define @[HGET_STRETCH(S)@] { stch_t st; @+ HGET32(st.u);@+ S.o=st.u&3; st.u&=~3; S.f=st.f; @+}
+@
+
+\readcode
+@s FIL symbol
+@s FILL symbol
+@s FILLL symbol
+@s order symbol
+
+@<symbols@>=
+%token FIL "fil"
+%token FILL "fill"
+%token FILLL "filll"
+%type <st> stretch
+%type <o> order
+@
+
+@<scanning rules@>=
+::@=fil@> :< return FIL; >:
+::@=fill@> :< return FILL; >:
+::@=filll@> :< return FILLL; >:
+@
+
+@s stretch symbol
+@s stretch_t int
+@<parsing rules@>=
+
+order: PT {$$=normal_o;} | FIL {$$=fil_o;} @+| FILL {$$=fill_o;} @+| FILLL {$$=filll_o;};
+
+stretch: number order { $$.f=$1; $$.o=$2; };
+@
+
+\writecode
+
+@<write functions@>=
+void hwrite_order(order_t o)
+{ switch (o)
+ { case normal_o: hwritef("pt"); @+break;
+ case fil_o: hwritef("fil"); @+break;
+ case fill_o: hwritef("fill"); @+break;
+ case filll_o: hwritef("filll"); @+break;
+ default: QUIT("Illegal order %d",o); @+ break;
+ }
+}
+
+void hwrite_stretch(stretch_t *s)
+{ hwrite_float64(s->f);
+ hwrite_order(s->o);
+}
+@
+
+\section{Simple Nodes}\hascode
+\label{simple}
+\subsection{Penalties}
+Penalties\index{penalty} are very simple nodes. They specify the cost of breaking a
+line or page at the present position. For the internal representation
+we use an |int32_t|. The full range of integers is, however, not
+used. Instead penalties must be between -20000 and +20000.
+(\TeX\ specifies a range of -10000 to +10000, but plain \TeX\ uses the value -20000
+when it defines the supereject control sequence.)
+The more general node is called an integer node;
+it shares the same kind-value |int_kind=penalty_kind|
+but allows the full range of values.
+The info value of a penalty node is 1 or 2 and indicates the number of bytes
+used to store the integer. The info value 4 can be used for general
+integers (see section~\secref{definitions}) that need four byte of storage.
+
+\readcode
+@s PENALTY symbol
+@s INTEGER symbol
+@s penalty symbol
+@<symbols@>=
+%token PENALTY "penalty"
+%token INTEGER "int"
+%type <i> penalty
+@
+
+@<scanning rules@>=
+::@=penalty@> :< return PENALTY; >:
+::@=int@> :< return INTEGER; >:
+@
+
+@<parsing rules@>=
+penalty: integer {RNG("Penalty",$1,-20000,+20000);$$=$1;};
+content_node: start PENALTY penalty END { hput_tags($1,hput_int($3));@+};
+@
+
+\getcode
+@<cases to get content@>=
+@t\1\kern1em@>case TAG(penalty_kind,1): @+{int32_t p;@+ HGET_PENALTY(1,p);@+} @+break;
+case TAG(penalty_kind,2): @+{int32_t p;@+ HGET_PENALTY(2,p);@+} @+break;
+@
+
+@<get macros@>=
+#define @[HGET_PENALTY(I,P)@] \
+if (I==1) {int8_t n=HGET8; @+P=n;@+ } \
+else {int16_t n;@+ HGET16(n);@+RNG("Penalty",n,-20000,+20000); @+ P=n; @+}\
+hwrite_signed(P);
+@
+
+\putcode
+@<put functions@>=
+uint8_t hput_int(int32_t n)
+{ info_t info;
+ if (n>=0) @/
+ { @+if (n<0x80) { @+HPUT8(n); @+info=1;@+ }
+ else if (n<0x8000) {@+ HPUT16(n);@+ info=2;@+ }
+ else {@+ HPUT32(n);@+ info=4;@+ }
+ }
+ else@/
+ {@+ if (n>=-0x80) {@+ HPUT8(n);@+ info=1;@+ }
+ else if (n>=-0x8000) {@+ HPUT16(n);@+ info=2;@+ }
+ else {@+ HPUT32(n);@+ info=4;@+ }
+ }
+ return TAG(int_kind,info);
+}
+@
+
+
+
+
+\subsection{Languages}
+To render a \HINT\ file on screen, information about the language is not necessary.
+Knowing the language is, however, very important for language translation and
+text to speech conversion which makes texts accessible to the visually-impaired.
+For this reason, \HINT\ offers the opportunity to add this information
+and encourages authors to supply this information.
+
+Language information by itself is not sufficient to decode text. It must be supplemented
+by information about the character encoding (see section~\secref{fonts}).
+
+To represent language information, the world wide web has set universally
+accepted standards. The Internet Engineering Task Force IETF has defined tags for identifying
+languages\cite{rfc5646}: short strings like ``en'' for English
+or ``de'' for Deutsch, and longer ones like ``sl-IT-nedis'', for the specific variant of
+the Nadiza dialect of Slovenian that is spoken in Italy.
+We assume that any \HINT\ file
+will contain only a small number of different languages and all language nodes can be
+encoded using a reference to a predefined node from the
+definition section (see section~\secref{reference}).
+In the definition section, a language node will just
+contain the language tag as given in~\cite{iana:language} (see section~\secref{definitions}).
+
+\readcode
+@s LANGUAGE symbol
+@s language symbol
+
+@<symbols@>=
+%token LANGUAGE "language"
+@
+
+@<scanning rules@>=
+::@=language@> :< return LANGUAGE; >:
+@
+
+When encoding language nodes in the short format,
+we use the info value |b000| for language nodes in the definition section
+and for language nodes in the content section that contain just a one-byte
+reference (see section~\secref{reference}).
+We use the info value |1| to |7| as a shorthand for
+the references {\tt *0} and {\tt *6} to the predefined language nodes.
+
+
+\goodbreak
+\vbox{\getcode\vskip -\baselineskip\writecode}
+@<cases to get content@>=
+@t\1\kern1em@>case TAG(language_kind,1): REF(language_kind,0); @+hwrite_ref(0); @+break;
+case TAG(language_kind,2): REF(language_kind,1); @+hwrite_ref(1); @+break;
+case TAG(language_kind,3): REF(language_kind,2); @+hwrite_ref(2); @+break;
+case TAG(language_kind,4): REF(language_kind,3); @+hwrite_ref(3); @+break;
+case TAG(language_kind,5): REF(language_kind,4); @+hwrite_ref(4); @+break;
+case TAG(language_kind,6): REF(language_kind,5); @+hwrite_ref(5); @+break;
+case TAG(language_kind,7): REF(language_kind,6); @+hwrite_ref(6); @+break;
+@
+
+\putcode
+@<put functions@>=
+uint8_t hput_language(uint8_t n)
+{ if (n<7) return TAG(language_kind,n+1);
+ HPUT8(n); return TAG(language_kind,0);
+}
+@
+
+
+
+\subsection{Rules}
+Rules\index{rule} are simply black rectangles having a height, a depth, and a
+width. All of these dimensions can also be negative but a rule will
+not be visible unless its width is positive and its height plus depth
+is positive.
+
+As a specialty, rules can have ``running dimensions''\index{running dimension}. If any of the
+three dimensions is a running dimension, its actual value will be
+determined by running the rule up to the boundary of the innermost
+enclosing box. The width is never running in an horizontal\index{horizontal list} list; the
+height and depth are never running in a vertical\index{vertical list} list. In the long
+format, we use a vertical bar ``{\tt \VB}'' or a horizontal bar
+``{\tt \_}'' (underscore character) to indicate a running
+dimension. Of course the vertical bar is meant to indicate a running
+height or depth while the horizontal bar stands for a running
+width. The parser, however, makes no distinction between the two and
+you can use either of them. In the short format, we follow \TeX\ and
+implement a running dimension by using the special value
+$-2^{30}=|0xC0000000|$.
+
+
+@<hint macros@>=
+#define RUNNING_DIMEN 0xC0000000
+@
+
+It could have been possible to allow extended dimensions in a rule node,
+but in most circumstances, the mechanism of running dimensions is sufficient
+and simpler to use. If a rule is needed that requires an extended dimension as
+its length, it is always possible to put it inside a suitable box and use a
+running dimension.
+
+
+To make the short format encoding more compact, the first info bit
+|b100| will be zero to indicate a running height, bit |b010| will be
+zero to indicate a running depth, and bit |b001| will be zero to
+indicate a running width.
+
+Because leaders\index{leaders} (see section~\secref{leaders}) may contain a rule
+node, we also provide functions to read and write a complete rule
+node. While parsing the symbol ``{\sl rule\/}'' will just initialize a variable of type
+\&{rule\_t} (the writing is done with a separate routine),
+parsing a {\sl rule\_node\/} will always include writing it.
+
+% Currently no predefined rules.
+%Further, a {\sl rule\_node} will permit the
+%use of a predefined rule (see section~\secref{reference}),
+
+
+@<hint types@>=
+typedef struct {@+
+dimen_t h,d,w; @+
+} rule_t;
+@
+
+\readcode
+@s RULE symbol
+@s RUNNING symbol
+@s rule_dimension symbol
+@s rule symbol
+@s rule_node symbol
+@<symbols@>=
+%token RULE "rule"
+%token RUNNING "|"
+%type <d> rule_dimension
+%type <r> rule
+@
+
+@<scanning rules@>=
+::@=rule@> :< return RULE; >:
+::@="|"@> :< return RUNNING; >:
+::@="_"@> :< return RUNNING; >:
+@
+
+@<parsing rules@>=
+rule_dimension: dimension@+ | RUNNING {$$=RUNNING_DIMEN;};
+rule: rule_dimension rule_dimension rule_dimension @/
+ { $$.h=$1; @+ $$.d=$2; @+ $$.w=$3;
+ if ($3==RUNNING_DIMEN && ($1==RUNNING_DIMEN || $2==RUNNING_DIMEN))
+ QUIT("Incompatible running dimensions 0x%x 0x%x 0x%x",@|$1,$2,$3); };
+rule_node: start RULE rule END { hput_tags($1,hput_rule(&($3))); };
+content_node: rule_node;
+@
+
+\writecode
+@<write functions@>=
+static void hwrite_rule_dimension(dimen_t d, char c)
+{ @+if (d==RUNNING_DIMEN) hwritef(" %c",c);
+ else hwrite_dimension(d);
+}
+
+void hwrite_rule(rule_t *r)
+{ @+hwrite_rule_dimension(r->h,'|');
+ hwrite_rule_dimension(r->d,'|');
+ hwrite_rule_dimension(r->w,'_');
+}
+@
+\getcode
+@<cases to get content@>=
+@t\1\kern1em@>
+case TAG(rule_kind,b011): {rule_t r;@+ HGET_RULE(b011,r); @+hwrite_rule(&(r));@+ } @+break;
+case TAG(rule_kind,b101): {rule_t r;@+ HGET_RULE(b101,r); @+hwrite_rule(&(r));@+ } @+break;
+case TAG(rule_kind,b001): {rule_t r;@+ HGET_RULE(b001,r); @+hwrite_rule(&(r));@+ } @+break;
+case TAG(rule_kind,b110): {rule_t r;@+ HGET_RULE(b110,r); @+hwrite_rule(&(r));@+ } @+break;
+case TAG(rule_kind,b111): {rule_t r;@+ HGET_RULE(b111,r); @+hwrite_rule(&(r));@+ } @+break;
+@
+
+@<get macros@>=
+#define @[HGET_RULE(I,R)@]@/\
+if ((I)&b100) HGET32((R).h); @+else (R).h=RUNNING_DIMEN;\
+if ((I)&b010) HGET32((R).d); @+else (R).d=RUNNING_DIMEN;\
+if ((I)&b001) HGET32((R).w); @+else (R).w=RUNNING_DIMEN;
+@
+
+@<get functions@>=
+void hget_rule_node(void)
+{ @<read the start byte |a|@>@;
+ if (KIND(a)==rule_kind) @/
+ { @+rule_t r; @+HGET_RULE(INFO(a),r); @/
+ hwrite_start();@+ hwritef("rule"); @+hwrite_rule(&r); @+hwrite_end();
+ }
+ else
+ QUIT("Rule expected at 0x%x got %s",node_pos,NAME(a));
+ @<read and check the end byte |z|@>@;
+}
+@
+
+\putcode
+@<put functions@>=
+uint8_t hput_rule(rule_t *r)
+{ info_t info=b000;
+ if (r->h!=RUNNING_DIMEN) { HPUT32(r->h); @+info|=b100; @+}
+ if (r->d!=RUNNING_DIMEN) { HPUT32(r->d); @+info|=b010; @+}
+ if (r->w!=RUNNING_DIMEN) { HPUT32(r->w); @+info|=b001; @+}
+ return TAG(rule_kind,info);
+}
+@
+
+
+\subsection{Kerns}
+A kern\index{kern} is a bit of white space with a certain length. If the kern is part of a
+horizontal list, the length is measured in the horizontal direction,
+if it is part of a vertical list, it is measured in the vertical
+direction. The length of a kern is mostly given as a dimension
+but provisions are made to use extended dimensions as well.
+
+The typical
+use of a kern is its insertion between two characters to make the natural
+distance between them a bit wider or smaller. In the latter case, the kern
+has a negative length. The typographic optimization just described is called
+``kerning'' and has given the kern node its name.
+Kerns inserted from font information or math mode calculations are normal kerns,
+while kerns inserted from \TeX's {\tt \BS kern} or {\tt \BS/}
+commands are explicit kerns.
+Kern nodes do not disappear at a line break unless they are explicit\index{explicit kern}.
+
+In the long format, explicit kerns are marked with an ``!'' sign
+and in the short format with the |b100| info bit.
+The two low order info bits are: 0 for a reference to a dimension, 1 for a reference to
+an extended dimension, 2 for an immediate dimension, and 3 for an immediate extended dimension node.
+To distinguish in the long format between a reference to a dimension and a reference to an extended dimension,
+the latter is prefixed with the keyword ``{\tt xdimen}'' (see section~\secref{reference}).
+
+@<hint types@>=
+typedef struct {@+
+bool x;@+
+xdimen_t d;@+
+} kern_t;
+@
+
+\readcode
+@s KERN symbol
+@s EXPLICIT symbol
+@s kern symbol
+@s explicit symbol
+@<symbols@>=
+%token KERN "kern"
+%token EXPLICIT "!"
+%type <b> explicit
+%type <kt> kern
+@
+
+@<scanning rules@>=
+::@=kern@> :< return KERN; >:
+::@=!@> :< return EXPLICIT; >:
+@
+
+@<parsing rules@>=
+explicit: {$$=false;} @+| EXPLICIT {$$=true;};
+kern: explicit xdimen {$$.x=$1; $$.d=$2;};
+content_node: start KERN kern END { hput_tags($1,hput_kern(&($3)));}
+@
+
+\writecode
+@<write functions@>=
+void hwrite_explicit(bool x)
+{ @+if (x) hwritef(" !"); @+}
+
+void hwrite_kern(kern_t *k)
+{ @+hwrite_explicit(k->x);
+ if (k->d.h==0.0 && k->d.v==0.0 && k->d.w==0) hwrite_ref(zero_dimen_no);
+ else hwrite_xdimen(&(k->d));
+}
+@
+
+
+\getcode
+@<cases to get content@>=
+@t\1\kern1em@>
+case TAG(kern_kind,b010): @+ {@+kern_t k; @+HGET_KERN(b010,k);@+ } @+break;
+case TAG(kern_kind,b011): @+ {@+kern_t k; @+HGET_KERN(b011,k);@+ } @+break;
+case TAG(kern_kind,b110): @+ {@+kern_t k; @+HGET_KERN(b110,k);@+ } @+break;
+case TAG(kern_kind,b111): @+ {@+kern_t k; @+HGET_KERN(b111,k);@+ } @+break;
+@
+
+@<get macros@>=
+#define @[HGET_KERN(I,K)@] \
+K.x=(I)&b100;\
+if (((I)&b011)==2) {HGET32(K.d.w);@+ K.d.h=K.d.v=0.0;@+}\
+else if (((I)&b011)==3) hget_xdimen_node(&(K.d));\
+hwrite_kern(&k);
+@
+
+\putcode
+@<put functions@>=
+uint8_t hput_kern(kern_t *k)
+{ info_t info;
+ if (k->x) info=b100; @+else info=b000;
+ if (k->d.h==0.0 && k->d.v==0.0)
+ { if (k->d.w==0) HPUT8(zero_dimen_no);
+ else {HPUT32(k->d.w); info=info|2;@+}
+ }
+ else {hput_xdimen_node(&(k->d));info=info|3;@+}
+ return TAG(kern_kind,info);
+}
+@
+
+
+
+\subsection{Glue}\label{glue}
+
+%Glue considerations
+
+%So what are the cases:
+%\itemize
+%\item reference to a dimen (common)
+%\item reference to a xdimen
+%\item reference to a dimen plus and minus
+%\item reference to a xdimen plus and minus
+%\item reference to a dimen plus
+%\item reference to a xdimen plus
+%\item reference to a dimen minus
+%\item reference to a xdimen minus
+%\item dimen
+%\item xdimen
+%\item dimen plus and minus
+%\item xdimen plus and minus (covers all other cases)
+%\item dimen plus
+%\item xdimen plus
+%\item dimen minus
+%\item xdimen minus
+%\item plus and minus
+%\item plus
+%\item minus
+%\item zero glue (rare, can be replaced by a reference to the zero glue)
+%\item reference to a predefined glue (common)
+%\enditemize
+%This is a total of 21 cases. Can we use the info bits to specify 7 common
+%cases and one catch all? First the use of an extended dimension in a glue
+%is probably not very common. More typically is the use of a fill glue
+%that extends to the boundaries of the enclosing box.
+
+%Here is the statistics for ctex:
+%total 58937 glue entries
+%total 49 defined glues (so 200 still available)
+%There are three font specific glues defined for each font used in texts.
+%The explicit glue nodes are the following:
+%\itemize
+%\item 35\% is predefined zero glue
+%\item 30\% are 39 other predefined glue most of them less than 1%
+%\item 8\% (4839) is one glue with 25pt pure stretch with order 0
+%\item 25\% (14746) is one glue with 100pt stretch and 10pt shrink with order 0
+%\item 2\% (1096) is one glue with 10pt no stretch and shrink
+%\item 0\% (13) are 7 different glues with no stretch and shrink
+%\item 0\% (3) different glues with width!=0 and some stretch of order 0
+%\item 0\% (27) 20 different glues with stretch and shrink
+%\enditemize
+
+%Some more glue with 1fil is insider 55 leaders
+%one vset has an extent 1 no stretch and shrink
+%56 hset specify an extent 2 and 1 fil stretch
+
+
+We have seen in section~\secref{stretch} how to deal with
+stretchability\index{stretchability} and
+shrinkability\index{shrinkability} and we will need this now.
+Glue\index{glue} has a natural width---which in general can be an
+extended dimension---and in addition it can stretch and shrink. It
+might have been possible to allow an extended dimension also for the
+stretch\-ability or shrink\-ability of a glue, but this seems of
+little practical relevance and so simplicity won over generality.
+Even with that restriction, it is an understatement to regard glue
+nodes as "simple" nodes.
+%, and we could equally well list them in
+%section~\secref{composite} as composite nodes.
+
+To use the info bits in the short format wisely, I collected some
+statistical data using the \TeX book as an example. It turns out that
+about 99\% of all the 58937 glue nodes (not counting the interword
+glues used inside texts) could be covered with only 43 predefined
+glues. So this is by far the most common case; we reserve the info
+value |b000| to cover it and postpone the description of such glue
+nodes until we describe references in section~\secref{reference}.
+
+We expect the remaining cases to contribute not too much to the file
+size, and hence, simplicity is a more important aspect than efficiency
+when allocating the remaining info values.
+
+Looking at the glues in more detail, we find that the most common
+cases are those where either one, two, or all three glue components
+are zero. We use the two lowest bits to indicate the presence of a
+nonzero stretchability or shrinkability and reserve the info values
+|b001|, |b010|, and |b011| for those cases where the width of the glue
+is zero. The zero glue, where all components are zero, is defined as
+a fixed, predefined glue instead of reserving a special info value for
+it. The cost of one extra byte when encoding it seems not too high a
+price to pay. After reserving the info value |b111| for the most
+general case of a glue, we have only three more info values left:
+|b100|, |b101|, and |b110|. Keeping things simple implies using the
+two lowest info bits---as before---to indicate a nonzero
+stretchability or shrinkability. For the width, three choices remain:
+using a reference to a dimension, using a reference to an extended
+dimension, or using an immediate value. Since references to glues are
+already supported, an immediate width seems best for glues that are
+not frequently reused, avoiding the overhead of references.
+
+% It also makes parsing simpler because we avoid the confusion
+% between references to dimensions
+% and references to glues and references to extended dimensions.
+
+Here is a summary of the info bits and the implied layout
+of glue nodes in the short format:
+\itemize
+\item |b000|: reference to a predefined glue
+\item |b001|: zero width and nonzero shrinkability
+\item |b010|: zero width and nonzero stretchability
+\item |b011|: zero width and nonzero stretchability and shrinkability
+\item |b100|: nonzero width
+\item |b101|: nonzero width and nonzero shrinkability
+\item |b110|: nonzero width and nonzero stretchability
+\item |b111|: extended dimension and nonzero stretchability and shrinkability
+\enditemize
+
+
+@<hint basic types@>=
+typedef struct {@+
+xdimen_t w; @+
+stretch_t p, m;@+
+} glue_t;
+@
+
+
+To test for a zero glue,
+we implement a macro:
+@<hint macros@>=
+#define @[ZERO_GLUE(G)@] ((G).w.w==0 && (G).w.h==0.0 && (G).w.v==0.0 && (G).p.f==0.0 && (G).m.f==0.0)
+@
+
+Because other nodes (leaders, baselines, and fonts)
+contain glue nodes as parameters, we provide functions
+to read and write a complete glue node in the same way as we did
+for rule nodes.
+Further, such an internal {\sl glue\_node\/} has the special property that
+in the short format a node for the zero glue might be omitted entirely.
+
+\readcode
+@s GLUE symbol
+@s glue symbol
+@s glue_node symbol
+@s PLUS symbol
+@s MINUS symbol
+@s plus symbol
+@s minus symbol
+
+@<symbols@>=
+%token GLUE "glue"
+%token PLUS "plus"
+%token MINUS "minus"
+%type <g> glue
+%type <b> glue_node
+%type <st> plus minus
+@
+
+@<scanning rules@>=
+::@=glue@> :< return GLUE; >:
+::@=plus@> :< return PLUS; >:
+::@=minus@> :< return MINUS; >:
+@
+
+@<parsing rules@>=
+plus: { $$.f=0.0; $$.o=0; } | PLUS stretch {$$=$2;};
+minus: { $$.f=0.0; $$.o=0; } | MINUS stretch {$$=$2;};
+glue: xdimen plus minus {$$.w=$1; $$.p=$2; $$.m=$3; };
+content_node: start GLUE glue END {if (ZERO_GLUE($3)) {HPUT8(zero_skip_no);
+ hput_tags($1,TAG(glue_kind,0)); } else hput_tags($1,hput_glue(&($3))); };
+glue_node: start GLUE glue END @/
+ {@+ if (ZERO_GLUE($3)) { hpos--; $$=false;@+}@/
+ else { hput_tags($1,hput_glue(&($3))); $$=true;@+}@+ };
+@
+
+\writecode
+@<write functions@>=
+void hwrite_plus(stretch_t *p)
+{ @+if (p->f!=0.0) { hwritef(" plus");@+hwrite_stretch(p); @+}
+}
+void hwrite_minus(stretch_t *m)
+{@+ if (m->f!=0.0) { hwritef(" minus");@+hwrite_stretch(m); @+}
+}
+
+void hwrite_glue(glue_t *g)
+{ hwrite_xdimen(&(g->w)); @+
+ hwrite_plus(&g->p); @+hwrite_minus(&g->m);
+}
+
+void hwrite_ref_node(kind_t k, uint8_t n);
+void hwrite_glue_node(glue_t *g)
+{@+
+ if (ZERO_GLUE(*g)) hwrite_ref_node(glue_kind,zero_skip_no);
+ else @+{ hwrite_start(); @+hwritef("glue"); @+hwrite_glue(g); @+hwrite_end();@+}
+}
+@
+
+\getcode
+@<cases to get content@>=
+@t\1\kern1em@>
+case TAG(glue_kind,b001): { glue_t g;@+ HGET_GLUE(b001,g);@+ hwrite_glue(&g);@+}@+break;
+case TAG(glue_kind,b010): { glue_t g;@+ HGET_GLUE(b010,g);@+ hwrite_glue(&g);@+}@+break;
+case TAG(glue_kind,b011): { glue_t g;@+ HGET_GLUE(b011,g);@+ hwrite_glue(&g);@+}@+break;
+case TAG(glue_kind,b100): { glue_t g;@+ HGET_GLUE(b100,g);@+ hwrite_glue(&g);@+}@+break;
+case TAG(glue_kind,b101): { glue_t g;@+ HGET_GLUE(b101,g);@+ hwrite_glue(&g);@+}@+break;
+case TAG(glue_kind,b110): { glue_t g;@+ HGET_GLUE(b110,g);@+ hwrite_glue(&g);@+}@+break;
+case TAG(glue_kind,b111): { glue_t g;@+ HGET_GLUE(b111,g);@+ hwrite_glue(&g);@+}@+break;
+@
+
+@<get macros@>=
+#define @[HGET_GLUE(I,G)@] {\
+ if((I)!=b111) { if ((I)&b100) HGET32((G).w.w);@+ else (G).w.w=0;}\
+ if((I)&b010) HGET_STRETCH((G).p) @+else (G).p.f=0.0, (G).p.o=0;\
+ if((I)&b001) HGET_STRETCH((G).m) @+else (G).m.f=0.0, (G).m.o=0;\
+ if((I)==b111) hget_xdimen_node(&((G).w)); else (G).w.h=(G).w.v=0.0;@+}
+@
+
+The |hget_glue_node| can cope with a glue node that is omitted and
+will supply a zero glue instead.
+
+@<get functions@>=
+void hget_glue_node(void)
+{ @<read the start byte |a|@>@;
+ if (KIND(a)!=glue_kind)
+ {@+ hpos--; hwrite_ref_node(glue_kind,zero_skip_no);@+return; @+}
+ if (INFO(a)==b000)
+ { uint8_t n=HGET8;@+ REF(glue_kind,n);@+hwrite_ref_node(glue_kind,n); @+}
+ else
+ { @+glue_t g; @+HGET_GLUE(INFO(a),g);@+ hwrite_glue_node(&g);@+}
+ @<read and check the end byte |z|@>@;
+}
+@
+
+
+\putcode
+@<put functions@>=
+uint8_t hput_glue(glue_t *g)
+{ info_t info=b000;
+ if (ZERO_GLUE(*g)) { HPUT8(zero_skip_no); @+ info=b000; }
+ else if ( (g->w.w==0 && g->w.h==0.0 && g->w.v==0.0))
+ { if (g->p.f!=0.0) { hput_stretch(&g->p); @+info|=b010; @+}
+ if (g->m.f!=0.0) { hput_stretch(&g->m); @+info|=b001; @+}
+ }
+ else if ( g->w.h==0.0 && g->w.v==0.0 && (g->p.f==0.0 || g->m.f==0.0))
+ { HPUT32(g->w.w); @+ info=b100;
+ if (g->p.f!=0.0) { hput_stretch(&g->p); @+info|=b010; @+}
+ if (g->m.f!=0.0) { hput_stretch(&g->m); @+info|=b001; @+}
+ }
+ else@/
+ { hput_stretch(&g->p);@+ hput_stretch(&g->m);
+ hput_xdimen_node(&(g->w));
+ info=b111;
+ }
+ return TAG(glue_kind,info);
+}
+@
+
+\section{Lists}\hascode\label{lists}
+When a node contains multiple other nodes, we package these nodes into
+a list\index{list} node. It is important to note that list nodes
+never occur as individual nodes, they only occur as parts of other
+nodes. In total, we have three different types of lists: plain lists
+that use the kind-value |list_kind|, text\index{text} lists that use
+the kind-value |text_kind|, and parameter\index{parameter} lists that use the
+kind-value |param_kind|. A description of the first two types of
+lists follows here. Parameter lists are described in section~\secref{paramlist}.
+
+Because lists are of variable size, it is not possible in the short
+format to tell from the kind and info bits of a tag byte the size of
+the list node. So advancing from the beginning of a list node to the
+next node after the list is not as simple as usual. To solve this
+problem, we store the size of the list immediately after the start
+byte and before the end byte. Alternatively we could require programs
+to traverse the entire list. The latter solution is more compact but
+inefficient for list with many nodes; our solution will cost some
+extra bytes, but the amount of extra bytes will only grow
+logarithmically with the size of the \HINT\ file. It would be
+possible to allow both methods so that a \HINT\ file could balance
+size and time trade-offs by making small lists---where the size can be
+determined easily by reading the entire list---without size
+information and making large lists with size information so that they
+can be skipped easily without reading them. But the added complexity
+seems too high a price to pay.
+
+
+Now consider the problem of reading a content stream starting at an arbitrary
+position $i$ in the middle of the stream. This situation occurs
+naturally when resynchronizing\index{resynchronization} a content stream after
+an error has been detected, but implementing links poses a similar problem.
+We can inspect the byte at position $i$ and see
+if it is a valid tag. If yes, we are faced with the problem of
+verifying that this is not a mere coincidence.
+So we determine the size $s$ of the node. If the byte in question is a start byte,
+we should find a matching byte $s$ bytes later in the stream; if it is an end byte,
+we should find the matching byte $s$ bytes earlier in the stream; if we
+find no matching byte, this was neither a start nor an end byte.
+If we find exactly one matching byte, we can be quite confident (error
+probability 1/256 if assuming equal probability of all byte values)
+that we have found a tag, and we know whether
+it is the beginning or the end tag. If we find two matching byte, we
+have most likely the start or the end of a node, but we do not know which
+of the two. To find out which of the two possibilities is true
+or to reduce the probability of an error, we can
+check the start and end byte of the node immediately preceding a start byte or
+immediately following an end byte in a similar way.
+By testing two more byte, this additional check will reduce the error
+probability further to $1/2^{24}$ (under the same assumption as before). So
+checking more nodes is rarely necessary. This whole schema
+would, however, not work if we happen to find a tag byte that indicated
+either the begin or the end of a list without specifying the size
+of the list. Sure, we can verify the bytes before and after it to
+find out whether the byte following it is the begin of a node and the
+byte preceding it is the end of a node, but we still don't know if the
+byte itself starts a node list or ends a node list. Even reading along
+in either direction until finding a matching tag will not answer the
+question. The situation is better if we specify a
+size: we can read the suspected size after or before the tag and check if we
+find a matching tag and size at the position indicated.
+In the short format, we use the |info| value to indicate the number of
+byte used to store the list size: A list with $0<|info|\le 5$
+uses $|info|-1$ byte to store the size.
+The info value zero is reserved for references to predefined lists
+(which are currently not implemented).
+
+Storing the list size immediately preceding the end tag creates a new
+problem: If we try to recover from an error, we might not know the
+size of the list and searching for the end of a list, we might be
+unable to tell the difference between the bytes that encode the list
+size and the start tag of a possible next node. If we parse the
+content backward, the problem is completely symmetric.
+
+To solve the problem, we insert an additional byte immediately before
+the final size and after the initial size marking the size boundary.
+We choose the byte values |0xFF|, |0xFE|, |0xFD|, and |0xFC| which can
+not be confused with valid tag bytes and indicate that the size is
+stored using 1, 2, 3, or 4 byte respectively. Under regular
+circumstances, these bytes are simply skipped. When searching for the
+list end (or start) these bytes would correspond to
+|TAG(penalty_kind,i)| with $7 \ge \hbox{|i|} \ge 4$ and can not be
+confused with valid penalty nodes which use only the info values 0, 1,
+and~2. An empty list uses the info value 1 and has neither a size bytes
+nor boundary bytes; it consists only of the two tags.
+
+We are a bit lazy when it comes to the internal representation of a list.
+Since we need the representation as a short format byte sequence anyway,
+it consists of the position |p| of the start of the byte sequence
+combined with an integer |s| giving the size of the byte sequence.
+If the list is empty, |s| is zero.
+
+@<hint types@>=
+typedef struct {@+
+kind_t k; @+
+uint32_t p;@+
+uint32_t s;@+
+} list_t;
+@
+
+The major drawback of this choice of representation is that it ties
+together the reading of the long format and the writing of the short
+format; these are no longer independent.
+So starting with the present section, we have to take the short format
+representation of a node into account already when we parse the long
+format representation.
+
+In the long format, we may start a list node with an
+estimate\index{estimate} of the size needed to store the list in the
+short format. We do not want to require the exact size because this
+would make editing of long format \HINT\ files almost impossible. Of
+course this makes it also impossible to derive the exact |s| value of
+the internal representation from the long format
+representation. Therefore we start by parsing the estimate of the list
+size and use it to reserve the necessary number of byte to store the
+size. Then we parse the |content_list|. As a side effect---and this
+is an important point---this will write the list content in short
+format into the output buffer. As mentioned above, whenever a node
+contains a list, we need to consider this side effect when we give the
+parsing rules. We will see examples for this in
+section~\secref{composite}.
+
+The function |hput_list| will be called {\it after} the short format
+of the list is written to the output. Before we pass the internal
+representation of the list to the |hput_list|
+function, we update |s| and |p|. Further, we pass the position in the stream where the
+list size and its boundary mark is supposed to be.
+Before |hput_list| is called, space for the tag, the size, and the boundary mark
+is allocated based on the estimate. The function
+|hsize_bytes| computes the number of byte required to store the list
+size, and the function |hput_list_size| will later write the list
+size. If the estimate turns out to be wrong, the list data can be moved
+to make room for a larger or smaller size field.
+
+
+If the long format does not specify a size estimate, a suitable default must be chosen.
+A statistical analysis shows
+%
+%statistics about list sizes using my old prototype
+%
+%name type size_byte list_count total_size
+%hello.hnt text 1 6 748
+% text 2 2 1967
+% list 1 65 3245
+% list 2 1 352
+%web2w.hnt text 1 1043 121925
+% text 2 1344 859070
+% list 1 19780 725514
+% list 2 487 199243
+%ctex.hnt text 1 9121 4241128
+% text 2 12329 7872687
+% text 3 1 75010
+% list 1 121557 4600743
+% list 2 222 147358
+%
+that most plain lists need only a single byte to store the size; and even the
+total amount of data contained in these lists exceeds the amount of data stored
+in longer lists by a factor of about 3. Hence if we do not have an estimate,
+we reserve only a single byte to store the size of a list.
+The statistics looks different for lists stored as a text: The number of texts
+that require two byte for the size is slightly larger than the number of texts that
+need only one byte, and the total amount of data stored in these texts is larger
+by a factor of 2 to 7 than the total amount of data found in all other texts.
+Hence as a default, we reserve two byte to store the size for texts.
+
+
+\subsection{Plain Lists}\label{plainlists}
+Plain list nodes start and end with a tag of kind |list_kind|.
+
+Not uncommon are empty\index{empty list} lists; these are the only lists that can be
+stored using $|info|=1$; such a list has zero bytes of size
+information, and no boundary bytes either; implicitly its size is zero.
+The |info| value 0 is not used since we do not use predefined plain lists.
+
+Writing the long format uses the fact that the function
+|hget_content_node|, as implemented in the \.{stretch} program, will
+output the node in the long format.
+
+\readcode
+@s list symbol
+@s content_list symbol
+@s estimate symbol
+@s position symbol
+
+@<symbols@>=
+%type <l> list
+%type <u> position content_list
+@
+
+@<parsing rules@>=
+position: {$$=hpos-hstart;};
+content_list: @+ position @+
+ | content_list content_node;
+estimate: {hpos+=2; } @+
+ | UNSIGNED {hpos+=hsize_bytes($1)+1; } ;
+list: start estimate content_list END @/
+ {@+$$.k=list_kind;@+ $$.p=$3; @+ $$.s=(hpos-hstart)-$3;
+ hput_tags($1,hput_list($1+1, &($$)));@+};
+@
+
+\writecode
+@<write functions@>=
+void hwrite_list(list_t *l)
+{ uint32_t h=hpos-hstart, e=hend-hstart; /* save |hpos| and |hend| */
+ hpos=l->p+hstart;@+ hend=hpos+l->s;
+ if (l->k==list_kind ) @<write a list@>@;
+ else if (l->k==text_kind) @<write a text@>@;
+ else QUIT("List expected got %s", content_name[l->k]);
+ hpos=hstart+h;@+ hend=hstart+e; /* restore |hpos| and |hend| */
+}
+@
+
+@<write a list@>=
+{@+if (l->s==0) hwritef(" <>");@/
+ else@/
+ {@+DBG(DBGNODE,"Write list at 0x%x size=%u\n", l->p, l->s);
+ @+hwrite_start();@+
+ if (section_no==2) hwrite_label();
+ if (l->s>0xFF) hwritef("%d",l->s);
+ while(hpos<hend)
+ hget_content_node();
+ hwrite_end();
+ }
+}
+@
+\getcode
+@<shared get functions@>=
+void hget_size_boundary(info_t info)
+{ uint32_t n;
+ if (info<2) return;
+ n=HGET8;
+ if (n-1!=0x100-info) QUIT(@["Size boundary byte 0x%x with info value %d at " SIZE_F@],
+ n, info,hpos-hstart-1);
+}
+
+uint32_t hget_list_size(info_t info)
+{ uint32_t n=0;
+ if (info==1) return 0;
+ else if (info==2) n=HGET8;
+ else if (info==3) HGET16(n);
+ else if (info==4) HGET24(n);
+ else if (info==5) HGET32(n);
+ else QUIT("List info %d must be 1, 2, 3, 4, or 5",info);
+ return n;
+}
+
+void hget_list(list_t *l)
+{@+if (KIND(*hpos)!=list_kind && @/
+ KIND(*hpos)!=text_kind &&@| KIND(*hpos)!=param_kind) @/
+ QUIT("List expected at 0x%x", (uint32_t)(hpos-hstart));
+ else
+ {
+ @<read the start byte |a|@>@;
+ l->k=KIND(a);
+ HGET_LIST(INFO(a),*l);
+ @<read and check the end byte |z|@>@;
+ DBG(DBGNODE,"Get list at 0x%x size=%u\n", l->p, l->s);
+ }
+}
+@
+
+@<shared get macros@>=
+#define @[HGET_LIST(I,L)@] \
+ (L).s=hget_list_size(I); hget_size_boundary(I);\
+ (L).p=hpos-hstart; \
+ hpos=hpos+(L).s; hget_size_boundary(I);\
+ { uint32_t s=hget_list_size(I); \
+ if (s!=(L).s) \
+ QUIT(@["List sizes at 0x%x and " SIZE_F " do not match 0x%x != 0x%x"@],node_pos+1,hpos-hstart-I-1,(L).s,s);}
+@
+
+\putcode
+
+@<put functions@>=
+uint8_t hsize_bytes(uint32_t n)
+{ @+if (n==0) return 0;
+ else if (n<0x100) return 1;
+ else if (n<0x10000) return 2;
+ else if (n<0x1000000) return 3;
+ else return 4;
+}
+
+void hput_list_size(uint32_t n, int i)
+{ @+if (i==0) ;
+ else if (i==1) HPUT8(n);
+ else if (i==2) HPUT16(n);
+ else if (i==3) HPUT24(n);
+ else HPUT32(n);
+}
+
+uint8_t hput_list(uint32_t start_pos, list_t *l)
+{ @+if (l->s==0)
+ { hpos=hstart+start_pos; return TAG(l->k,1);@+}
+ else@/
+ { uint32_t list_end=hpos-hstart;
+ int i=l->p -start_pos-1; /* number of byte allocated for size */
+ int j=hsize_bytes(l->s); /* number of byte needed for size */
+ DBG(DBGNODE,"Put list at 0x%x size=%u\n", l->p, l->s);
+ if (i>j && l->s> 0x100) j=i; /* avoid moving large lists */
+ if (i!=j)@/
+ { int d= j-i;
+ DBG(DBGNODE,"Moving %u byte by %d\n", l->s,d);
+ if (d>0) HPUTX(d);
+ memmove(hstart+l->p+d,hstart+l->p,l->s);
+ @<adjust label positions after moving a list@>@;
+ l->p=l->p+d;@+
+ list_end=list_end+d;
+ }
+ hpos=hstart+start_pos; @+ hput_list_size(l->s,j);@+ HPUT8(0x100-j);
+ hpos=hstart+list_end;@+ HPUT8(0x100-j);@+ hput_list_size(l->s,j);
+ return TAG(l->k,j+1);
+ }
+}
+
+@
+
+
+
+\subsection{Texts}\label{text}
+A Text\index{text} is a list of nodes with a representation optimized
+for character nodes. In the long format, a sequence of characters
+like ``{\tt Hello}'' is written ``\.{<glyph 'H'} \.{*0>} \.{<glyph} \.{'e'}
+\.{*0>} \.{<glyph 'l' *0>} \.{<glyph 'l' *0>} \.{<glyph 'o' *0>}'', and
+even in the short format it requires 4 byte per character! As a text,
+the same sequence is written ``{\tt\,"Hello"\,}'' in the long format and the
+short format requires usually just 1 byte per character. Indeed
+except the bytes with values from |0x00| to |0x20|, which are
+considered control\index{control code} codes, all bytes and all
+\hbox{UTF-8}\index{UTF8} multibyte sequences are simply considered
+character\index{character code} codes. They are equivalent to a glyph
+node in the ``current font''. The current\index{current font}
+font\index{font} is font number 0 at the beginning of a text and it
+can be changed using the control codes. We introduce the concept of a
+``current font'' because we do not expect the font to change too
+often, and it allows for a more compact representation if we do not
+store the font with every character code. It has an important
+disadvantage though: storing only font changes prevents us from
+parsing a text backwards; we always have to start at the beginning of
+the text, where the font is known to be font number~0.
+
+Defining a second format for encoding lists of nodes adds another
+difficulty to the problem we had discussed at the beginning of
+section~\secref{lists}. When we try to recover from an error and start
+reading a content stream at an arbitrary position, the first thing we
+need to find out is whether at this position we have the tag byte of
+an ordinary node or whether we have a position inside a text.
+
+Inside a text, character nodes start with a byte in the range
+|0x21|--|0xF7|. This is a wide range and it overlaps considerably with
+the range of valid tag bytes. It is however possible to choose the
+kind-values in such a way that the control codes do not overlap with
+the valid tag bytes that start a node. For this reason, the values
+|text_kind==0|, |list_kind==1|, |param_kind==2|, |xdimen_kind==3|, and
+|adjust_kind==4| were chosen on page~\pageref{kinddef}. Texts, lists,
+parameter lists, and extended dimensions occur only {\it inside} of
+content nodes, but are not content nodes in their own right; so the
+values |0x00| to |0x1F| are not used as tag bytes of content
+nodes. The value |0x20| would, as a tag byte, indicate an adjust node
+(|adjust_kind==4|) with info value zero. Because there are no
+predefined adjustments, |0x20| is not used as a tag byte either.
+(An alternative choice would be to use the kind value 4 for paragraph
+nodes because there are no predefined paragraphs.)
+
+The largest byte that starts an UTF8 code is |0xF7|; hence, there are
+eight possible control codes, from |0xF8| to |0xFF|, available. The
+first three values |0xF8|, |0xF9|, and |0xFA| are actually used for
+penalty nodes with info values, 0, 1, and 2. The last four |0xFC|,
+|0xFD|, |0xFE|, and |0xFF| are used as boundary marks for the text
+size and therefore we use only |0xFB| as control code.
+
+In the long format, we do not provide a syntax for specifying a size
+estimate\index{estimate} as we did for plain lists, because we expect
+text to be quite short. We allocate two byte for the size and hope
+that this will prove to be sufficient most of the time. Further, we
+will disallow the use of non-printable ASCII codes, because these
+are---by definition---not very readable, and we will give special
+meaning to some of the printable ASCII codes because we will need a
+notation for the beginning and ending of a text, for nodes inside a
+text, and the control codes.
+
+Here are the details:
+\itemize
+
+\item In the long format, a text starts and ends with a
+double\index{double quote} quote character ``{\tt "}''. In the short
+format, texts are encoded similar to lists using the kind-value
+|text_kind|.
+
+\item Arbitrary nodes can be embedded inside a text. In the long
+format, they are enclosed in pointed brackets \.{<} \dots \.{>} as
+usual. In the short format, an arbitrary node can follow the control
+code $|txt_node|=|0x1E|$. Because text may occur in nodes, the scanner
+needs to be able to parse texts nested inside nodes nested inside
+nodes nested inside texts \dots\ To accomplish this, we use the
+``stack'' option of \.{flex} and include the pushing and popping of the
+stack in the macros |SCAN_START| and |SCAN_END|.
+
+\item The space\index{space character} character ``\.{\ }'' with ASCII
+value |0x20| stands in both formats for the font specific interword
+glue node (control code |txt_glue|).
+
+\item The hyphen\index{hyphen character} character ``\.{-}'' in the
+long format and the control code $|txt_hyphen|=|0x1F|$ in the short
+format stand for the font specific discretionary hyphenation node.
+
+\item In the long format, the backslash\index{backslash} character
+``\.{\\}'' is used as an escape character. It is used to introduce
+notations for control codes, as described below, and to access the
+character codes of those ASCII characters that otherwise carry a
+special meaning. For example ``{\tt \BS "}'' denotes the character code
+of the double quote character ``{\tt "}''; and similarly ``\.{\\\\}'',
+``\.{\\<}'', ``\.{\\>}'', ``\.{\\\ }'', and ``\.{\\-}'' denote the
+character codes of ``\.{\\}'', ``\.{<}'', ``\.{>}'', ``\.{\ }'', and
+``\.{-}'' respectively.
+
+
+\item In the long format, a TAB-character (ASCII code
+|0x09|)\index{tab character} is silently converted to a
+space\index{space character} character (ASCII code |0x20|);
+a NL-character\index{newline character} (ASCII code |0x0A|), together
+with surrounding spaces, TAB-characters,
+and CR-characters\index{carriage return character} (ASCII code |0x0D|),
+is silently converted to a single space character. All other ASCII
+characters in the range |0x00| to |0x1F| are not allowed inside a
+text. This rule avoids the problems arising from ``invisible''
+characters embedded in a text and it allows to break texts into lines,
+even with indentation\index{indentation}, at word boundaries.
+
+To allow breaking a text into lines without inserting spaces, a
+NL-character together with surrounding spaces, TAB-characters, and
+CR-characters is completely ignored if the whole group of spaces,
+TAB-characters, CR-characters, and the NL-character is preceded by a
+backslash character.
+
+For example, the text ``\.{"There\ is\ no\ more\ gas\ in\ the\
+tank."}''\hfil\break can be written as \medskip
+
+\qquad\vbox{\hsize=0.5\hsize\noindent
+\.{"There\ is\ }\hfil\break
+\.{\hbox to 2em {$\rightarrow$\hfill}no more g\\\ \ }\hfil\break
+\.{\hbox to 2em {$\rightarrow$\hfill}as in the tank."}
+}\hss
+
+To break long lines when writing a long format file, we use the
+variable |txt_length| to keep track of the approximate length of the
+current line.
+
+\item The control codes $|txt_font|=|0x00|$, |0x01|, |0x02|, \dots,
+and |0x07| are used to change the current font to font
+number 0, 1, 2, \dots, and 7. In the long format these control
+codes are written \.{\\0}, \.{\\1}, \.{\\2}, \dots, and \.{\\7}.
+
+\item The control code $|txt_global|=|0x08|$ is followed by a second
+parameter byte. If the value of the parameter byte is $n$, it will set
+the current font to font number $n$. In the long format, the two byte
+sequence is written ``\.{\\F}$n$\.{\\}'' where $n$ is the decimal
+representation of the font number.
+
+
+\item The control codes |0x09|, |0x0A|, |0x0B|, |0x0C|, |0x0E|,
+|0x0E|, |0x0F|, and |0x10| are also followed by a second parameter
+byte. They are used to reference the global definitions of
+penalty\index{penalty}, kern\index{kern}, ligature\index{ligature},
+disc\index{discretionary hyphen}, glue\index{glue}, language\index{language},
+rule\index{rule}, and image\index{image} nodes. The parameter byte
+contains the reference number. For example, the byte sequence |0x09|
+|0x03| is equivalent to the node \.{<penalty *3>}.
+In the long format these two-byte sequences are written,
+``\.{\\P}$n$\.{\\}'' (penalty),
+``\.{\\K}$n$\.{\\}'' (kern),
+``\.{\\L}$n$\.{\\}'' (ligature),
+``\.{\\D}$n$\.{\\}'' (disc),
+``\.{\\G}$n$\.{\\}'' (glue),
+``\.{\\S}$n$\.{\\}'' (speak or German ``Sprache''),
+``\.{\\R}$n$\.{\\}'' (rule), and
+``\.{\\I}$n$\.{\\}'' (image), where $n$ is the decimal representation
+ of the parameter value.
+
+
+\item The control codes from $|txt_local|=|0x11|$ to |0x1C| are used
+to reference one of the 12 font specific parameters\index{font
+parameter}. In the long format they are written ``\.{\\a}'',
+``\.{\\b}'', ``\.{\\c}'', \dots, ``\.{\\j}'', ``\.{\\k}'',``\.{\\l}''.
+
+
+\item The control code $|txt_cc|=|0x1D|$ is used as a prefix for an
+arbitrary character code represented as an UTF-8 multibyte sequence.
+Its main purpose is providing a method for including character codes
+less or equal to |0x20| which otherwise would be considered control
+codes. In the long format, the byte sequence is written ``\.{\\C}$n$\.{\\}''
+where $n$ is the decimal representation of the character code.
+
+
+\item The control code $|txt_node|=|0x1E|$ is used as a prefix for an
+arbitrary node in short format. In the long format, it is written
+``\.{<}'' and is followed by the node content in long format
+terminated by ``\.{>}''.
+
+\item The control code $|txt_hyphen|=|0x1F|$ is used to access the
+font specific discretionary hyphen\index{hyphen}. In the long format
+it is simply written as ``\.{-}''.
+
+\item The control code $|txt_glue|=|0x20|$ is the space character, it
+is used to access the font specific interword\index{interword glue}
+glue. In the long format, we use the space character\index{space
+character} ``\.{\ }'' as well.
+
+\item The control code $|txt_ignore|=|0xFB|$ is ignored, its position
+can be used in a link to specify a position between two characters. In
+the long format it is written as ``\.{\\@@}''.
+
+\enditemize
+For the control codes, we define an enumeration type
+and for references, a reference type.
+@<hint types@>=
+typedef enum { @+txt_font=0x00, txt_global=0x08, txt_local=0x11,
+ txt_cc=0x1D, txt_node=0x1E, txt_hyphen=0x1F,
+ txt_glue=0x20, txt_ignore=0xFB} txt_t;
+@
+
+\readcode
+@s TXT symbol
+@s TXT_START symbol
+@s TXT_END symbol
+@s TXT_FONT symbol
+@s TXT_LOCAL symbol
+@s TXT_GLOBAL symbol
+@s TXT_FONT_GLUE symbol
+@s TXT_FONT_HYPHEN symbol
+@s TXT_CC symbol
+@s TXT_IGNORE symbol
+@s text symbol
+@<scanning definitions@>=
+%x TXT
+@
+
+@<symbols@>=
+%token TXT_START TXT_END TXT_IGNORE
+%token TXT_FONT_GLUE TXT_FONT_HYPHEN
+%token <u> TXT_FONT TXT_LOCAL
+%token <rf> TXT_GLOBAL
+%token <u> TXT_CC
+%type <u> text
+@
+
+@<scanning rules@>=
+::@=\"@> :< SCAN_TXT_START; return TXT_START; >:
+
+<TXT>{
+::@=\"@> :< SCAN_TXT_END; return TXT_END; >:
+
+::@="<"@> :< SCAN_START; return START; >:
+::@=">"@> :< QUIT("> not allowed in text mode");>:
+
+::@=\\\\@> :< yylval.u='\\'; return TXT_CC; >:
+::@=\\\"@> :< yylval.u='"'; return TXT_CC; >:
+::@=\\"<"@> :< yylval.u='<'; return TXT_CC; >:
+::@=\\">"@> :< yylval.u='>'; return TXT_CC; >:
+::@=\\" "@> :< yylval.u=' '; return TXT_CC; >:
+::@=\\"-"@> :< yylval.u='-'; return TXT_CC; >:
+::@=\\"@@"@> :< return TXT_IGNORE; >:
+
+::@=[ \t\r]*(\n[ \t\r]*)+@> :< return TXT_FONT_GLUE; >:
+::@=\\[ \t\r]*\n[ \t\r]*@> :< ; >:
+
+::@=\\[0-7]@> :< yylval.u=yytext[1]-'0'; return TXT_FONT; >:
+
+::@=\\F[0-9]+\\@> :< SCAN_REF(font_kind); return TXT_GLOBAL; >:
+::@=\\P[0-9]+\\@> :< SCAN_REF(penalty_kind); return TXT_GLOBAL; >:
+::@=\\K[0-9]+\\@> :< SCAN_REF(kern_kind); return TXT_GLOBAL; >:
+::@=\\L[0-9]+\\@> :< SCAN_REF(ligature_kind); return TXT_GLOBAL; >:
+::@=\\D[0-9]+\\@> :< SCAN_REF(disc_kind); return TXT_GLOBAL; >:
+::@=\\G[0-9]+\\@> :< SCAN_REF(glue_kind); return TXT_GLOBAL; >:
+::@=\\S[0-9]+\\@> :< SCAN_REF(language_kind); return TXT_GLOBAL; >:
+::@=\\R[0-9]+\\@> :< SCAN_REF(rule_kind); return TXT_GLOBAL; >:
+::@=\\I[0-9]+\\@> :< SCAN_REF(image_kind); return TXT_GLOBAL; >:
+
+
+::@=\\C[0-9]+\\@> :< SCAN_UDEC(yytext+2); return TXT_CC; >:
+
+::@=\\[a-l]@> :< yylval.u=yytext[1]-'a'; return TXT_LOCAL; >:
+::@=" "@> :< return TXT_FONT_GLUE; >:
+::@="-"@> :< return TXT_FONT_HYPHEN; >:
+
+::@={UTF8_1}@> :< SCAN_UTF8_1(yytext); return TXT_CC; >:
+::@={UTF8_2}@> :< SCAN_UTF8_2(yytext); return TXT_CC; >:
+::@={UTF8_3}@> :< SCAN_UTF8_3(yytext); return TXT_CC; >:
+::@={UTF8_4}@> :< SCAN_UTF8_4(yytext); return TXT_CC; >:
+}
+@
+
+@<scanning macros@>=
+#define @[SCAN_REF(K)@] @[yylval.rf.k=K;@+ yylval.rf.n=atoi(yytext+2)@;@]
+static int scan_level=0;
+#define SCAN_START @[yy_push_state(INITIAL);@+if (1==scan_level++) hpos0=hpos;@]
+#define SCAN_END @[if (scan_level--) yy_pop_state(); @/else QUIT("Too many '>' in line %d",yylineno)@]
+#define SCAN_TXT_START @[BEGIN(TXT)@;@]
+#define SCAN_TXT_END @[BEGIN(INITIAL)@;@]
+@
+@s txt symbol
+
+@<parsing rules@>=
+list: TXT_START position @|
+ {hpos+=4; /* start byte, two size byte, and boundary byte */ }
+ text TXT_END@|
+ { $$.k=text_kind;$$.p=$4; $$.s=(hpos-hstart)-$4;
+ hput_tags($2,hput_list($2+1, &($$)));@+};
+text: position @+| text txt;
+
+txt: TXT_CC { hput_txt_cc($1); }
+ | TXT_FONT { REF(font_kind,$1); hput_txt_font($1); }
+ | TXT_GLOBAL { REF($1.k,$1.n); hput_txt_global(&($1)); }
+ | TXT_LOCAL { RNG("Font parameter",$1,0,11); hput_txt_local($1); }
+ | TXT_FONT_GLUE { HPUTX(1); HPUT8(txt_glue); }
+ | TXT_FONT_HYPHEN { HPUTX(1);HPUT8(txt_hyphen); }
+ | TXT_IGNORE { HPUTX(1);HPUT8(txt_ignore); }
+ | { HPUTX(1); HPUT8(txt_node);} content_node;
+@
+
+The following function keeps track of the position in the current line.
+It the line gets too long it will break the text at the next space
+character. If no suitable space character comes along,
+the line will be broken after any regular character.
+
+\writecode
+@<write a text@>=
+{@+if (l->s==0) hwritef(" \"\"");
+ else@/
+ { int pos=nesting+20; /* estimate */
+ hwritef(" \"");
+ while(hpos<hend)@/
+ { int i=hget_txt();
+ if (i<0)
+ { if (pos++<70) hwritec(' ');
+ else hwrite_nesting(), pos=nesting;
+ }
+ else if (i==1 && pos>=100)@/
+ { hwritec('\\'); @+hwrite_nesting(); @+pos=nesting; @+}
+ else
+ pos+=i;
+ }
+ hwritec('"');
+ }
+}
+@
+
+
+The function returns the number of characters written
+because this information is needed in |hget_txt| below.
+
+@<write functions@>=
+int hwrite_txt_cc(uint32_t c)
+{@+ if (c<0x20)
+ return hwritef("\\C%d\\",c);
+ else@+
+ switch(c)
+ { case '\\': return hwritef("\\\\");
+ case '"': return hwritef("\\\"");
+ case '<': return hwritef("\\<");
+ case '>': return hwritef("\\>");
+ case ' ': return hwritef("\\ ");
+ case '-': return hwritef("\\-");
+ default: return option_utf8?hwrite_utf8(c):hwritef("\\C%d\\",c);
+ }
+}
+@
+
+\getcode
+@<get macros@>=
+#define @[HGET_GREF(K,S)@] {uint8_t n=HGET8;@+ REF(K,n); @+ return hwritef("\\" S "%d\\",n);@+}
+
+@
+
+The function |hget_txt| reads a text element and writes it immediately.
+To enable the insertion of line breaks when writing a text, we need to keep track
+of the number of characters in the current line. For this purpose
+the function |hget_txt| returns the number of characters written.
+It returns $-1$ if a space character needs to be written
+providing a good opportunity for a break.
+
+@<get functions@>=
+int hget_txt(void)
+{@+ if (*hpos>=0x80 && *hpos<=0xF7)
+ { if (option_utf8)
+ return hwrite_utf8(hget_utf8());
+ else
+ return hwritef("\\C%d\\",hget_utf8());
+ }
+ else @/
+ { uint8_t a;
+ a=HGET8;
+ switch (a)
+ { case txt_font+0: return hwritef("\\0");
+ case txt_font+1: return hwritef("\\1");
+ case txt_font+2: return hwritef("\\2");
+ case txt_font+3: return hwritef("\\3");
+ case txt_font+4: return hwritef("\\4");
+ case txt_font+5: return hwritef("\\5");
+ case txt_font+6: return hwritef("\\6");
+ case txt_font+7: return hwritef("\\7");
+ case txt_global+0: HGET_GREF(font_kind,"F");
+ case txt_global+1: HGET_GREF(penalty_kind,"P");
+ case txt_global+2: HGET_GREF(kern_kind,"K");
+ case txt_global+3: HGET_GREF(ligature_kind,"L");
+ case txt_global+4: HGET_GREF(disc_kind,"D");
+ case txt_global+5: HGET_GREF(glue_kind,"G");
+ case txt_global+6: HGET_GREF(language_kind,"S");
+ case txt_global+7: HGET_GREF(rule_kind,"R");
+ case txt_global+8: HGET_GREF(image_kind,"I");
+ case txt_local+0: return hwritef("\\a");
+ case txt_local+1: return hwritef("\\b");
+ case txt_local+2: return hwritef("\\c");
+ case txt_local+3: return hwritef("\\d");
+ case txt_local+4: return hwritef("\\e");
+ case txt_local+5: return hwritef("\\f");
+ case txt_local+6: return hwritef("\\g");
+ case txt_local+7: return hwritef("\\h");
+ case txt_local+8: return hwritef("\\i");
+ case txt_local+9: return hwritef("\\j");
+ case txt_local+10: return hwritef("\\k");
+ case txt_local+11: return hwritef("\\l");
+ case txt_cc: return hwrite_txt_cc(hget_utf8());
+ case txt_node: { int i;
+ @<read the start byte |a|@>@;
+ i=hwritef("<");
+ i+= hwritef("%s",content_name[KIND(a)]);@+ hget_content(a);
+ @<read and check the end byte |z|@>@;
+ hwritec('>');@+ return i+10; /* just an estimate */
+ }
+ case txt_hyphen: hwritec('-'); @+return 1;
+ case txt_glue: return -1;
+ case '<': return hwritef("\\<");
+ case '>': return hwritef("\\>");
+ case '"': return hwritef("\\\"");
+ case '-': return hwritef("\\-");
+ case txt_ignore: return hwritef("\\@@");
+ default: hwritec(a); @+return 1;
+ }
+ }
+}
+
+@
+
+
+\putcode
+@<put functions@>=
+
+void hput_txt_cc(uint32_t c)
+{ @+ if (c<=0x20) { HPUTX(2); HPUT8(txt_cc);@+ HPUT8(c); @+ }
+ else hput_utf8(c);
+}
+
+void hput_txt_font(uint8_t f)
+{@+ if (f<8) HPUTX(1),HPUT8(txt_font+f);
+ else QUIT("Use \\F%d\\ instead of \\%d for font %d in a text",f,f,f);
+}
+
+void hput_txt_global(ref_t *d)
+{ @+ HPUTX(2);
+ switch (d->k)
+ { case font_kind: HPUT8(txt_global+0);@+ break;
+ case penalty_kind: HPUT8(txt_global+1);@+ break;
+ case kern_kind: HPUT8(txt_global+2);@+ break;
+ case ligature_kind: HPUT8(txt_global+3);@+ break;
+ case disc_kind: HPUT8(txt_global+4);@+ break;
+ case glue_kind: HPUT8(txt_global+5);@+ break;
+ case language_kind: HPUT8(txt_global+6);@+ break;
+ case rule_kind: HPUT8(txt_global+7);@+ break;
+ case image_kind: HPUT8(txt_global+8);@+ break;
+ default: QUIT("Kind %s not allowed as a global reference in a text",NAME(d->k));
+ }
+ HPUT8(d->n);
+}
+
+void hput_txt_local(uint8_t n)
+{ HPUTX(1);
+ HPUT8(txt_local+n);
+}
+@
+
+
+@<hint types@>=
+typedef struct { @+kind_t k; @+int n; @+} ref_t;
+@
+
+
+\section{Composite Nodes}\hascode
+\label{composite}
+The nodes that we consider in this section can contain one or more list nodes.
+When we implement the parsing\index{parsing} routines
+for composite nodes in the long format, we have to take into account
+that parsing such a list node will already write the list node
+to the output. So we split the parsing of composite nodes into several parts
+and store the parts immediately after parsing them. On the parse stack, we will only
+keep track of the info value.
+This new strategy is not as transparent as our previous strategy used for
+simple nodes where we had a clean separation of reading and writing:
+reading would store the internal representation of a node and writing the internal
+representation to output would start only after reading is completed.
+The new strategy, however, makes it easier to reuse
+the grammar\index{grammar} rules for the component nodes.
+
+Another rule applies to composite nodes: in the short format, the subnodes
+will come at the end of the node, and especially a list node that contains content nodes
+comes last. This helps when traversing the content section as we will see in
+appendix~\secref{fastforward}.
+
+\subsection{Boxes}\label{boxnodes}
+The central structuring elements of \TeX\ are boxes\index{box}.
+Boxes have a height |h|, a depth |d|, and a width |w|.
+The shift amount |a| shifts the contents of the box, the glue ratio\index{glue ratio} |r| is a factor
+applied to the glue inside the box, the glue order |o| is its order of stretchability\index{stretchability},
+and the glue sign |s| is $-1$ for shrinking\index{shrinkability}, 0 for rigid, and $+1$ for stretching.
+Most importantly, a box contains a list |l| of content nodes inside the box.
+
+
+@<hint types@>=
+typedef struct @/{@+ dimen_t h,d,w,a;@+ float32_t r;@+ int8_t s,o; @+list_t l; @+} box_t;
+@
+
+There are two types of boxes: horizontal\index{horizontal box} boxes
+and vertical\index{vertical box} boxes.
+The difference between the two is simple:
+a horizontal box aligns the reference\index{reference point}
+points of its content nodes horizontally, and a positive shift amount\index{shift amount} |a|
+shifts the box down;
+a vertical box aligns\index{alignment} the reference\index{reference point}
+points vertically, and a positive shift amount |a| shifts the box right.
+
+Not all box parameters are used frequently. In the short format, we use the info bits
+to indicated which of the parameters are used.
+Where as the width of a horizontal box is most of the time (80\%) nonzero,
+other parameters are most of the time zero,
+like the shift amount (99\%) or the glue settings (99.8\%).
+The depth is zero in about 77\%, the height in about 53\%,
+and both together are zero in about 47\%. The results for vertical boxes,
+which constitute about 20\% of all boxes, are similar,
+except that the depth is zero in about 89\%,
+but the height and width are almost never zero.
+For this reason we use bit |b001| to indicate a nonzero depth,
+bit |b010| for a nonzero shift amount, and |b100| for nonzero glue settings.
+Glue sign and glue order can be packed as two nibbles in a single byte.
+% A different use of the info bits for vertical and horizontal boxes is possible,
+% but does not warrant the added complexity.
+
+
+
+\goodbreak
+\readcode
+@s HBOX symbol
+@s VBOX symbol
+@s box symbol
+@s boxparams symbol
+@s hbox_node symbol
+@s vbox_node symbol
+@s box_dimen symbol
+@s box_shift symbol
+@s box_glue_set symbol
+@<symbols@>=
+%token HBOX "hbox"
+%token VBOX "vbox"
+%token SHIFTED "shifted"
+%type <info> box box_dimen box_shift box_glue_set
+
+@
+@<scanning rules@>=
+::@=hbox@> :< return HBOX; >:
+::@=vbox@> :< return VBOX; >:
+::@=shifted@> :< return SHIFTED; >:
+@
+
+@<parsing rules@>=@/
+
+box_dimen: dimension dimension dimension @/
+ {$$= hput_box_dimen($1,$2,$3); };
+box_shift: {$$=b000;} @+
+ | SHIFTED dimension {$$=hput_box_shift($2);};
+
+box_glue_set: {$$=b000;}
+ | PLUS stretch { $$=hput_box_glue_set(+1,$2.f,$2.o); }
+ | MINUS stretch { $$=hput_box_glue_set(-1,$2.f,$2.o); };
+
+
+box: box_dimen box_shift box_glue_set list {$$=$1|$2|$3; };
+
+hbox_node: start HBOX box END { hput_tags($1, TAG(hbox_kind,$3)); };
+vbox_node: start VBOX box END { hput_tags($1, TAG(vbox_kind,$3)); };
+content_node: hbox_node @+ | vbox_node;
+@
+
+\writecode
+@<write functions@>=
+void hwrite_box(box_t *b)
+{ hwrite_dimension(b->h);
+ hwrite_dimension(b->d);
+ hwrite_dimension(b->w);
+ if (b->a!=0) { hwritef(" shifted"); @+hwrite_dimension(b->a); @+}
+ if (b->r!=0.0 && b->s!=0 )@/
+ { @+if (b->s>0) @+hwritef(" plus"); @+else @+hwritef(" minus");
+ @+hwrite_float64(b->r); @+hwrite_order(b->o);
+ }
+ hwrite_list(&(b->l));
+}
+@
+
+\getcode
+@<cases to get content@>=
+@t\1\kern1em@>
+case TAG(hbox_kind,b000): {box_t b; @+HGET_BOX(b000,b); @+hwrite_box(&b);@+} @+ break;
+case TAG(hbox_kind,b001): {box_t b; @+HGET_BOX(b001,b); @+hwrite_box(&b);@+} @+ break;
+case TAG(hbox_kind,b010): {box_t b; @+HGET_BOX(b010,b); @+hwrite_box(&b);@+} @+ break;
+case TAG(hbox_kind,b011): {box_t b; @+HGET_BOX(b011,b); @+hwrite_box(&b);@+} @+ break;
+case TAG(hbox_kind,b100): {box_t b; @+HGET_BOX(b100,b); @+hwrite_box(&b);@+} @+ break;
+case TAG(hbox_kind,b101): {box_t b; @+HGET_BOX(b101,b); @+hwrite_box(&b);@+} @+ break;
+case TAG(hbox_kind,b110): {box_t b; @+HGET_BOX(b110,b); @+hwrite_box(&b);@+} @+ break;
+case TAG(hbox_kind,b111): {box_t b; @+HGET_BOX(b111,b); @+hwrite_box(&b);@+} @+ break;
+case TAG(vbox_kind,b000): {box_t b; @+HGET_BOX(b000,b); @+hwrite_box(&b);@+} @+ break;
+case TAG(vbox_kind,b001): {box_t b; @+HGET_BOX(b001,b); @+hwrite_box(&b);@+} @+ break;
+case TAG(vbox_kind,b010): {box_t b; @+HGET_BOX(b010,b); @+hwrite_box(&b);@+} @+ break;
+case TAG(vbox_kind,b011): {box_t b; @+HGET_BOX(b011,b); @+hwrite_box(&b);@+} @+ break;
+case TAG(vbox_kind,b100): {box_t b; @+HGET_BOX(b100,b); @+hwrite_box(&b);@+} @+ break;
+case TAG(vbox_kind,b101): {box_t b; @+HGET_BOX(b101,b); @+hwrite_box(&b);@+} @+ break;
+case TAG(vbox_kind,b110): {box_t b; @+HGET_BOX(b110,b); @+hwrite_box(&b);@+} @+ break;
+case TAG(vbox_kind,b111): {box_t b; @+HGET_BOX(b111,b); @+hwrite_box(&b);@+} @+ break;
+@
+
+@<get macros@>=
+#define @[HGET_BOX(I,B)@] \
+HGET32(B.h);\
+if ((I)&b001) HGET32(B.d); @+ else B.d=0;\
+HGET32(B.w);\
+if ((I)&b010) HGET32(B.a); @+else B.a=0;\
+if ((I)&b100) @/{ B.r=hget_float32();@+ B.s=HGET8; @+ B.o=B.s&0xF; @+B.s=B.s>>4;@+ }\
+else { B.r=0.0;@+ B.o=B.s=0;@+ }\
+hget_list(&(B.l));
+@
+
+@<get functions@>=
+void hget_hbox_node(void)
+{ box_t b;
+ @<read the start byte |a|@>@;
+ if (KIND(a)!=hbox_kind) QUIT("Hbox expected at 0x%x got %s",node_pos,NAME(a));
+ HGET_BOX(INFO(a),b);@/
+ @<read and check the end byte |z|@>@;
+ hwrite_start();@+
+ hwritef("hbox");@+
+ hwrite_box(&b);@+
+ hwrite_end();
+}
+
+
+void hget_vbox_node(void)
+{ box_t b;
+ @<read the start byte |a|@>@;
+ if (KIND(a)!=vbox_kind) QUIT("Vbox expected at 0x%x got %s",node_pos,NAME(a));
+ HGET_BOX(INFO(a),b);@/
+ @<read and check the end byte |z|@>@;
+ hwrite_start();@+
+ hwritef("vbox");@+
+ hwrite_box(&b);@+
+ hwrite_end();
+}
+@
+
+\putcode
+@<put functions@>=
+
+info_t hput_box_dimen(dimen_t h, dimen_t d, dimen_t w)
+{ info_t i;
+ @+HPUT32(h);
+ if (d!=0) { HPUT32(d); @+i=b001;@+ } @+else@+ i=b000;
+ HPUT32(w);
+ return i;
+}
+info_t hput_box_shift(dimen_t a)
+{ @+if (a!=0) { @+ HPUT32(a); @+return @+ b010;@+} @+ else @+return b000;
+}
+
+info_t hput_box_glue_set(int8_t s, float32_t r, order_t o)
+{ @+if (r!=0.0 && s!=0 )
+ { hput_float32(r);@+
+ HPUT8((s<<4)|o);@+
+ return b100;@+
+ }
+ else return b000;
+}
+
+@
+
+\subsection{Extended Boxes}
+Hi\TeX\ produces two kinds of extended\index{extended box} horizontal
+boxes, |hpack_kind| and |hset_kind|, and the same for vertical boxes
+using |vpack_kind| and |vset_kind|. Let us focus on horizontal boxes;
+the handling of vertical boxes is completely parallel.
+
+The \\{hpack} procedure of Hi\TeX\ produces an extended box of |hset_kind|
+either if it is given an extended\index{extended dimension} dimension as its width
+or if it discovers that the width of its content is an extended
+dimension. After the final width of the box has been computed in the
+viewer, it just remains to set the glue; a very simple operation
+indeed.
+
+If the \\{hpack} procedure of Hi\TeX\ can not determine the natural
+dimensions of the box content because it contains
+paragraphs\index{paragraph} or other extended boxes, it produces a box
+of |hpack_kind|. Now the viewer needs to traverse the list of content
+nodes to determine the natural\index{natural dimension}
+dimensions. Even the amount of stretchability\index{stretchability}
+and shrinkability\index{shrinkability} has to be determined in the
+viewer. For example, the final stretchability of a paragraph with some
+stretchability in the baseline\index{baseline skip} skip will depend
+on the number of lines which, in turn, depends on \.{hsize}. It is
+not possible to merge these traversals of the box content with the
+traversal necessary when displaying the box. The latter needs to
+convert glue nodes into positioning instructions which requires a
+fixed glue\index{glue ratio} ratio. The computation of the glue ratio,
+however, requires a complete traversal of the content.
+
+In the short format of a box node of type |hset_kind|, |vset_kind|,
+|hpack_kind|, or |vpack_kind|, the info bit |b100| indicates, if set,
+a complete extended dimension, and if unset, a reference to a
+predefined extended dimension for the target size; the info bit |b010|
+indicates a nonzero shift amount. For a box of type |hset_kind| or
+|vset_kind|, the info bit |b001| indicates, if set, a nonzero depth.
+For a box of type |hpack_kind| or |vpack_kind|, the info bit |b001|
+indicates, if set, an additional target size, and if unset, an exact
+target size. For a box of type |vpack_kind| also the maximum depth is
+given.
+
+\readcode
+@s xbox symbol
+@s hpack symbol
+@s vpack symbol
+@s box_goal symbol
+@s HPACK symbol
+@s HSET symbol
+@s VPACK symbol
+@s VSET symbol
+@s TO symbol
+@s ADD symbol
+@s box_flex symbol
+@s vxbox_node symbol
+@s hxbox_node symbol
+@s DEPTH symbol
+
+@<symbols@>=
+%token HPACK "hpack"
+%token HSET "hset"
+%token VPACK "vpack"
+%token VSET "vset"
+%token DEPTH "depth"
+%token ADD "add"
+%token TO "to"
+%type <info> xbox box_goal hpack vpack
+@
+
+@<scanning rules@>=
+::@=hpack@> :< return HPACK; >:
+::@=hset@> :< return HSET; >:
+::@=vpack@> :< return VPACK; >:
+::@=vset@> :< return VSET; >:
+::@=add@> :< return ADD; >:
+::@=to@> :< return TO; >:
+::@=depth@> :< return DEPTH; >:
+@
+
+@<parsing rules@>=
+box_flex: plus minus { hput_stretch(&($1));hput_stretch(&($2)); };
+xbox: box_dimen box_shift box_flex xdimen_ref list {$$=$1|$2;}
+ | box_dimen box_shift box_flex xdimen_node list {$$=$1|$2|b100;};
+
+box_goal: TO xdimen_ref {$$=b000;}
+ | ADD xdimen_ref {$$=b001;}
+ | TO xdimen_node {$$=b100;}
+ | ADD xdimen_node {$$=b101;};
+
+hpack: box_shift box_goal list {$$=$2;};
+vpack: box_shift MAX DEPTH dimension {HPUT32($4);} @/ box_goal list {$$= $1|$6;};
+
+vxbox_node: start VSET xbox END { hput_tags($1, TAG(vset_kind,$3)); }
+ | start VPACK vpack END { hput_tags($1, TAG(vpack_kind,$3)); };
+
+
+hxbox_node: start HSET xbox END { hput_tags($1, TAG(hset_kind,$3)); }
+ | start HPACK hpack END { hput_tags($1, TAG(hpack_kind,$3)); };
+
+content_node: vxbox_node | hxbox_node;
+ @
+
+\getcode
+@<cases to get content@>=
+@t\1\kern1em@>
+case TAG(hset_kind,b000): HGET_SET(b000); @+ break;
+case TAG(hset_kind,b001): HGET_SET(b001); @+ break;
+case TAG(hset_kind,b010): HGET_SET(b010); @+ break;
+case TAG(hset_kind,b011): HGET_SET(b011); @+ break;
+case TAG(hset_kind,b100): HGET_SET(b100); @+ break;
+case TAG(hset_kind,b101): HGET_SET(b101); @+ break;
+case TAG(hset_kind,b110): HGET_SET(b110); @+ break;
+case TAG(hset_kind,b111): HGET_SET(b111); @+ break;@#
+
+case TAG(vset_kind,b000): HGET_SET(b000); @+ break;
+case TAG(vset_kind,b001): HGET_SET(b001); @+ break;
+case TAG(vset_kind,b010): HGET_SET(b010); @+ break;
+case TAG(vset_kind,b011): HGET_SET(b011); @+ break;
+case TAG(vset_kind,b100): HGET_SET(b100); @+ break;
+case TAG(vset_kind,b101): HGET_SET(b101); @+ break;
+case TAG(vset_kind,b110): HGET_SET(b110); @+ break;
+case TAG(vset_kind,b111): HGET_SET(b111); @+ break;@#
+
+case TAG(hpack_kind,b000): HGET_PACK(hpack_kind,b000); @+ break;
+case TAG(hpack_kind,b001): HGET_PACK(hpack_kind,b001); @+ break;
+case TAG(hpack_kind,b010): HGET_PACK(hpack_kind,b010); @+ break;
+case TAG(hpack_kind,b011): HGET_PACK(hpack_kind,b011); @+ break;
+case TAG(hpack_kind,b100): HGET_PACK(hpack_kind,b100); @+ break;
+case TAG(hpack_kind,b101): HGET_PACK(hpack_kind,b101); @+ break;
+case TAG(hpack_kind,b110): HGET_PACK(hpack_kind,b110); @+ break;
+case TAG(hpack_kind,b111): HGET_PACK(hpack_kind,b111); @+ break;@#
+
+case TAG(vpack_kind,b000): HGET_PACK(vpack_kind,b000); @+ break;
+case TAG(vpack_kind,b001): HGET_PACK(vpack_kind,b001); @+ break;
+case TAG(vpack_kind,b010): HGET_PACK(vpack_kind,b010); @+ break;
+case TAG(vpack_kind,b011): HGET_PACK(vpack_kind,b011); @+ break;
+case TAG(vpack_kind,b100): HGET_PACK(vpack_kind,b100); @+ break;
+case TAG(vpack_kind,b101): HGET_PACK(vpack_kind,b101); @+ break;
+case TAG(vpack_kind,b110): HGET_PACK(vpack_kind,b110); @+ break;
+case TAG(vpack_kind,b111): HGET_PACK(vpack_kind,b111); @+ break;
+@
+
+
+@<get macros@>=
+#define @[HGET_SET(I)@] @/\
+ { dimen_t h; @+HGET32(h); @+hwrite_dimension(h);@+}\
+ { dimen_t d; @+if ((I)&b001) HGET32(d); @+ else d=0;@+hwrite_dimension(d); @+}\
+ { dimen_t w; @+HGET32(w); @+hwrite_dimension(w);@+} \
+if ((I)&b010) { dimen_t a; @+HGET32(a); hwritef(" shifted"); @+hwrite_dimension(a);@+}\
+ { stretch_t p; @+HGET_STRETCH(p);@+hwrite_plus(&p);@+}\
+ { stretch_t m; @+HGET_STRETCH(m);@+hwrite_minus(&m);@+}\
+ if ((I)&b100) {xdimen_t x;@+ hget_xdimen_node(&x); @+hwrite_xdimen_node(&x);@+} else HGET_REF(xdimen_kind)@;\
+ { list_t l; @+hget_list(&l);@+ hwrite_list(&l); @+}
+@#
+
+#define @[HGET_PACK(K,I)@] @/\
+ if ((I)&b010) { dimen_t d; @+HGET32(d); hwritef(" shifted"); @+hwrite_dimension(d); @+ }\
+ if (K==vpack_kind) { dimen_t d; @+HGET32(d); hwritef(" max depth");@+hwrite_dimension(d); @+ }\
+ if ((I)&b001) hwritef(" add");@+ else hwritef(" to");\
+ if ((I)&b100) {xdimen_t x;@+ hget_xdimen_node(&x);@+hwrite_xdimen_node(&x);@+}\
+ else @+HGET_REF(xdimen_kind);\
+ { list_t l; @+hget_list(&l);@+ hwrite_list(&l); @+}
+@
+
+
+\subsection{Leaders}\label{leaders}
+Leaders\index{leaders} are a special type of glue that is best explained by a few
+examples.
+Where as ordinary glue fills its designated space with \hfil\ whiteness,\break
+leaders fill their designated space with either a rule \xleaders\hrule\hfil\ or\break
+some sort of repeated\leaders\hbox to 15pt{$\hss.\hss$}\hfil content.\break
+In multiple leaders, the dots\leaders\hbox to 15pt{$\hss.\hss$}\hfil are usually aligned\index{alignment} across lines,\break
+as in the last\leaders\hbox to 15pt{$\hss.\hss$}\hfil three lines.\break
+Unless you specify centered\index{centered}\cleaders\hbox to 15pt{$\hss.\hss$}\hfil leaders\break
+or you specify expanded\index{expanded}\xleaders\hbox to 15pt{$\hss.\hss$}\hfil leaders.\break
+The former pack the repeated content tight and center
+the repeated content in the available space, the latter distributes
+the extra space between all the repeated instances.
+
+In the short format, the two lowest info bits store the type
+of leaders: 1 for aligned, 2 for centered, and 3 for expanded.
+The |b100| info bit is usually set and only zero in the unlikely
+case that the glue is zero and therefore not present.
+
+\readcode
+@s LEADERS symbol
+@s ALIGN symbol
+@s CENTER symbol
+@s EXPAND symbol
+@s leaders symbol
+@s ltype symbol
+@<symbols@>=
+%token LEADERS "leaders"
+%token ALIGN "align"
+%token CENTER "center"
+%token EXPAND "expand"
+%type <info> leaders
+%type <info> ltype
+@
+
+@<scanning rules@>=
+::@=leaders@> :< return LEADERS; >:
+::@=align@> :< return ALIGN; >:
+::@=center@> :< return CENTER; >:
+::@=expand@> :< return EXPAND; >:
+@
+@<parsing rules@>=
+ltype: {$$=1;} | ALIGN {$$=1;} @+| CENTER {$$=2;} @+| EXPAND {$$=3;};
+leaders: glue_node ltype rule_node {@+if ($1) $$=$2|b100;@+else $$=$2; @+}
+ | glue_node ltype hbox_node {@+if ($1) $$=$2|b100;@+else $$=$2;@+}
+ | glue_node ltype vbox_node {@+if ($1) $$=$2|b100;@+else $$=$2;@+};
+content_node: start LEADERS leaders END @| {@+ hput_tags($1, TAG(leaders_kind, $3));}
+@
+
+\writecode
+@<write functions@>=
+void hwrite_leaders_type(int t)
+{@+
+ if (t==2) hwritef(" center");
+ else if (t==3) hwritef(" expand");
+}
+@
+
+\getcode
+@<cases to get content@>=
+@t\1\kern1em@>
+case TAG(leaders_kind,1): @+ HGET_LEADERS(1); @+break;
+case TAG(leaders_kind,2): @+ HGET_LEADERS(2); @+break;
+case TAG(leaders_kind,3): @+ HGET_LEADERS(3); @+break;
+case TAG(leaders_kind,b100|1): @+ HGET_LEADERS(b100|1); @+break;
+case TAG(leaders_kind,b100|2): @+ HGET_LEADERS(b100|2); @+break;
+case TAG(leaders_kind,b100|3): @+ HGET_LEADERS(b100|3); @+break;
+@
+@<get macros@>=
+#define @[HGET_LEADERS(I)@]@/ \
+if ((I)&b100) hget_glue_node();\
+hwrite_leaders_type((I)&b011);\
+if (KIND(*hpos)==rule_kind) hget_rule_node(); \
+else if (KIND(*hpos)==hbox_kind) hget_hbox_node(); \
+else hget_vbox_node();
+@
+
+\subsection{Baseline Skips}
+Baseline\index{baseline skip} skips are small amounts of glue inserted
+between two consecutive lines of text. To get nice looking pages, the
+amount of glue\index{glue} inserted must take into account the depth
+of the line above the glue and the height of the line below the glue
+to achieve a constant distance of the baselines. For example, if we
+have the lines
+\medskip
+
+\qquad\vbox{\hsize=0.5\hsize\noindent
+``There is no\hfil\break
+more gas\hfil\break
+in the tank.''
+}\hss
+
+\medskip\noindent
+\TeX\ will insert 7.69446pt of baseline skip between the first and the
+second line and 3.11111pt of baseline skip between the second and the
+third line. This is due to the fact that the first line has no
+descenders, its depth is zero, the second line has no ascenders but
+the ``g'' descends below the baseline, and the third line has
+ascenders (``t'', ``h'',\dots) so it is higher than the second line.
+\TeX's choice of baseline skips ensures that the baselines are exactly
+12pt apart in both cases.
+
+Things get more complicated if the text contains mathematical formulas because then
+a line can get so high or deep that it is impossible to keep the distance between
+baselines constant without two adjacent lines touching each other. In such cases,
+\TeX\ will insert a small minimum line skip glue\index{line skip glue}.
+
+For the whole computation, \TeX\ uses three parameters: {\tt base\-line\-skip},
+{\tt line\-skip\-limit},\index{line skip limit} and
+{\tt lineskip}. {\tt baselineskip} is a glue value; its size is the
+normal distance of two baselines. \TeX\ adjusts the size of the
+{\tt baselineskip} glue for the height and the depth of the two lines and
+then checks the result against {\tt lineskiplimit}. If the result is
+smaller than {\tt lineskiplimit} it will use the {\tt lineskip} glue
+instead.
+
+Because the depth and the height of lines depend on the outcome
+of the line breaking\index{line breaking}
+routine, baseline computations must be done in the viewer.
+The situation gets even more complicated because \TeX\ can manipulate the insertion
+of baseline skips in various ways. Therefore \HINT\ requires the insertion of
+baseline nodes wherever the viewer is supposed to perform a baseline skip
+computation.
+
+In the short format of a baseline definition, we store only
+the nonzero components and use the
+info bits to mark them: |b100| implies $|bs|\ne0$,
+|b010| implies $|ls|\ne 0$, and |b001| implies $|lslimit|\ne 0$.
+If the baseline has only zero components, we put a reference to baseline number 0
+in the output.
+
+@<hint basic types@>=
+typedef struct {@+
+glue_t bs, ls;@+
+dimen_t lsl;@+
+} baseline_t;
+@
+
+
+
+\readcode
+@s BASELINE symbol
+@s baseline symbol
+@<symbols@>=
+%token BASELINE "baseline"
+%type <info> baseline
+@
+@<scanning rules@>=
+::@=baseline@> :< return BASELINE; >:
+@
+
+@<parsing rules@>=
+baseline: dimension { if ($1!=0) HPUT32($1); }
+ glue_node glue_node @/{ $$=b000; if ($1!=0) $$|=b001;
+ if ($3) $$|=b100;
+ if ($4) $$|=b010;
+ @+};
+content_node: start BASELINE baseline END @/
+{ @+if ($3==b000) HPUT8(0); @+hput_tags($1,TAG(baseline_kind, $3)); };
+@
+
+\getcode
+@<cases to get content@>=
+@t\1\kern1em@>
+case TAG(baseline_kind,b001): { baseline_t b;@+ HGET_BASELINE(b001,b);@+ }@+break;
+case TAG(baseline_kind,b010): { baseline_t b;@+ HGET_BASELINE(b010,b);@+ }@+break;
+case TAG(baseline_kind,b011): { baseline_t b;@+ HGET_BASELINE(b011,b);@+ }@+break;
+case TAG(baseline_kind,b100): { baseline_t b;@+ HGET_BASELINE(b100,b);@+ }@+break;
+case TAG(baseline_kind,b101): { baseline_t b;@+ HGET_BASELINE(b101,b);@+ }@+break;
+case TAG(baseline_kind,b110): { baseline_t b;@+ HGET_BASELINE(b110,b);@+ }@+break;
+case TAG(baseline_kind,b111): { baseline_t b;@+ HGET_BASELINE(b111,b);@+ }@+break;
+@
+
+@<get macros@>=
+#define @[HGET_BASELINE(I,B)@] \
+ if((I)&b001) HGET32((B).lsl); @+else B.lsl=0; hwrite_dimension(B.lsl);\
+ if((I)&b100) hget_glue_node(); \
+ else {B.bs.p.o=B.bs.m.o=B.bs.w.w=0; @+B.bs.w.h=B.bs.w.v=B.bs.p.f=B.bs.m.f=0.0; @+hwrite_glue_node(&(B.bs));@+}\
+ if((I)&b010) hget_glue_node(); \
+ else {B.ls.p.o=B.ls.m.o=B.ls.w.w=0; @+B.ls.w.h=B.ls.w.v=B.ls.p.f=B.ls.m.f=0.0; @+hwrite_glue_node(&(B.ls));@+}
+@
+
+
+\putcode
+@<put functions@>=
+uint8_t hput_baseline(baseline_t *b)
+{ info_t info=b000;
+ if (!ZERO_GLUE(b->bs)) @+info|=b100;
+ if (!ZERO_GLUE(b->ls)) @+ info|=b010;
+ if (b->lsl!=0) { @+ HPUT32(b->lsl); @+info|=b001; @+}
+ return TAG(baseline_kind,info);
+}
+@
+
+
+
+\subsection{Ligatures}
+Ligatures\index{ligature} occur only in horizontal lists. They specify characters
+that combine the glyphs of several characters into one specialized
+glyph. For example in the word ``{\it difficult\/}'' the three letters
+``{\it f{}f{}i\/}'' are combined into the ligature ``{\it ffi\/}''.
+Hence, a ligature is very similar to a simple glyph node; the
+characters that got replaced are, however, retained in the ligature
+because they might be needed for example to support searching. Since
+ligatures are therefore only specialized list of characters and since
+we have a very efficient way to store such lists of characters, namely
+as a |text|, input and output of ligatures is quite simple.
+
+The info value zero is reserved for references to a ligature. If the
+info value is between 1 and 6, it gives the number of bytes used to encode
+the characters in UTF8. Note that a ligature will always include a
+glyph byte, so the minimum size is 1. A typical ligature like ``{\it fi\/}''
+will need 3 byte: the ligature character ``{\it fi\/}'', and
+the replacement characters ``f'' and ''i''. More byte might be
+required if the character codes exceed |0x7F| since we use the UTF8
+encoding scheme for larger character codes. If the info value is 7,
+a full text node follows the font byte. In the long
+format, we give the font, the character code, and then the replacement
+characters represented as a text.
+
+@<hint types@>=
+typedef struct{@+uint8_t f; @+list_t l;@+} lig_t;
+@
+
+\readcode
+@s ref symbol
+@s LIGATURE symbol
+@s ligature symbol
+@s cc_list symbol
+@s lig_cc symbol
+@<symbols@>=
+%token LIGATURE "ligature"
+%type <u> lig_cc
+%type <lg> ligature
+%type <u> ref
+@
+@<scanning rules@>=
+::@=ligature@> :< return LIGATURE; >:
+@
+
+@<parsing rules@>=@/
+cc_list:@+ | cc_list TXT_CC { hput_utf8($2); };
+lig_cc: UNSIGNED {RNG("UTF-8 code",$1,0,0x1FFFFF);$$=hpos-hstart; hput_utf8($1); };
+lig_cc: CHARCODE {$$=hpos-hstart; hput_utf8($1); };
+ref: REFERENCE { HPUT8($1); $$=$1; };
+ligature: ref { REF(font_kind,$1);} lig_cc TXT_START cc_list TXT_END @/
+ { $$.f=$1; $$.l.p=$3; $$.l.s=(hpos-hstart)-$3;
+ RNG("Ligature size",$$.l.s,0,255);};
+content_node: start LIGATURE ligature END {hput_tags($1,hput_ligature(&($3)));};
+@
+
+\writecode
+@<write functions@>=
+void hwrite_ligature(lig_t *l)
+{ uint32_t pos=hpos-hstart;
+ hwrite_ref(l->f);
+ hpos=l->l.p+hstart;
+ hwrite_charcode(hget_utf8());
+ hwritef(" \"");
+ while (hpos<hstart+l->l.p+l->l.s)
+ hwrite_txt_cc(hget_utf8());
+ hwritec('"');
+ hpos=hstart+pos;
+}
+@
+
+\getcode
+@<cases to get content@>=
+@t\1\kern1em@>
+case TAG(ligature_kind,1):@+ {lig_t l; @+HGET_LIG(1,l);@+} @+break;
+case TAG(ligature_kind,2):@+ {lig_t l; @+HGET_LIG(2,l);@+} @+break;
+case TAG(ligature_kind,3):@+ {lig_t l; @+HGET_LIG(3,l);@+} @+break;
+case TAG(ligature_kind,4):@+ {lig_t l; @+HGET_LIG(4,l);@+} @+break;
+case TAG(ligature_kind,5):@+ {lig_t l; @+HGET_LIG(5,l);@+} @+break;
+case TAG(ligature_kind,6):@+ {lig_t l; @+HGET_LIG(6,l);@+} @+break;
+case TAG(ligature_kind,7):@+ {lig_t l; @+HGET_LIG(7,l);@+} @+break;
+@
+@<get macros@>=
+#define @[HGET_LIG(I,L)@] @/\
+(L).f=HGET8;REF(font_kind,(L).f);\
+if ((I)==7) hget_list(&((L).l)); \
+else { (L).l.s=(I); (L).l.p=hpos-hstart; @+ hpos+=(L).l.s;} \
+hwrite_ligature(&(L));
+@
+
+\putcode
+@<put functions@>=
+uint8_t hput_ligature(lig_t *l)
+{ @+if (l->l.s < 7) return TAG(ligature_kind,l->l.s);
+ else@/
+ { uint32_t pos=l->l.p;
+ hput_tags(pos,hput_list(pos+1, &(l->l)));
+ return TAG(ligature_kind,7);
+ }
+}
+@
+
+
+\subsection{Discretionary breaks}\label{discbreak}\index{discretionary break}
+\HINT\ is capable to break lines into paragraphs. It does this
+primarily at interword spaces but it might also break a line in the
+middle of a word if it finds a discretionary\index{discretionary break}
+line break there. These discretionary breaks are usually
+provided by an automatic hyphenation algorithm but they might be also
+explicitly\index{explicit} inserted by the author of a
+document.
+
+When a line break occurs at such a discretionary break, the line
+before the break ends with a |pre_break| list of nodes, the line after
+the break starts with a |post_break| list of nodes, and the next
+|replace_count| nodes after the discretionary break will be
+ignored. Both lists must consist entirely of glyphs\index{glyph},
+kerns\index{kern}, boxes\index{box}, rules\index{rule}, or
+ligatures\index{ligature}. For example, an ordinary discretionary
+break will have a |pre_break| list containing ``-'', an empty
+|post_break| list, and a |replace_count| of zero.
+
+The long format starts with an optional ``{\tt !}'', indicating an
+explicit discretionary break, followed by the replace-count.
+Then comes the pre-break list followed by the post-break list.
+The replace-count can be omitted if it is zero;
+an empty post-break list may be omitted as well.
+Both list may be omitted only if both are empty.
+
+In the short format, the three components of a disc node are stored
+in this order: |replace_count|, |pre_break| list, and |post_break| list.
+The |b100| bit in the info value indicates the presence of a replace-count,
+the |b010| bit the presence of a |pre_break| list,
+and the |b001| bit the presence of a |post_break| list.
+Since the info value |b000| is reserved for references, at least one
+of these must be specified; so we represent a node with empty lists
+and a replace\index{replace count} count of zero using the info value
+|b100| and a zero byte for the replace count.
+
+Replace counts must be in the range 0 to 31; so the short format can
+set the high bit of the replace count to indicate an explicit\index{explicit} break.
+
+@<hint types@>=
+typedef struct disc_t@+ {@+ bool x; @+list_t p,q;@+ uint8_t r;@+ } disc_t;
+@
+
+
+\readcode
+@s DISC symbol
+@s disc symbol
+@s disc_node symbol
+@s replace_count symbol
+
+@<symbols@>=
+%token DISC "disc"
+%type <dc> disc
+%type <u> replace_count
+@
+@<scanning rules@>=
+::@=disc@> :< return DISC; >:
+@
+
+@<parsing rules@>=@/
+replace_count: explicit {@+ if ($1) {$$=0x80; HPUT8(0x80);@+}@+ else $$=0x00;@+}
+ | explicit UNSIGNED { RNG("Replace count",$2,0,31);
+ $$=($2)|(($1)?0x80:0x00); @+ if ($$!=0) HPUT8($$);@+};
+disc: replace_count list list { $$.r=$1;$$.p=$2; $$.q=$3;
+ if ($3.s==0) { hpos=hpos-2;@+ if ($2.s==0) hpos=hpos-2; @+}@+}
+ | replace_count list { $$.r=$1;$$.p=$2; if ($2.s==0) hpos=hpos-2;@+ $$.q.s=0; }
+ | replace_count { $$.r=$1;$$.p.s=0; $$.q.s=0; };
+
+
+disc_node: start DISC disc END
+ {hput_tags($1,hput_disc(&($3)));};
+
+content_node: disc_node;
+@
+
+\writecode
+@<write functions@>=
+void hwrite_disc(disc_t *h)
+{ @+hwrite_explicit(h->x);
+ if (h->r!=0) hwritef(" %d",h->r);
+ if (h->p.s!=0 || h->q.s!=0) hwrite_list(&(h->p));
+ if (h->q.s!=0) hwrite_list(&(h->q));
+}
+void hwrite_disc_node(disc_t *h)
+{ @+ hwrite_start(); @+hwritef("disc"); @+ hwrite_disc(h); @+hwrite_end();}
+@
+
+\getcode
+@<cases to get content@>=
+@t\1\kern1em@>
+case TAG(disc_kind,b001): {disc_t h; @+HGET_DISC(b001,h);@+ hwrite_disc(&h); @+} @+break;
+case TAG(disc_kind,b010): {disc_t h; @+HGET_DISC(b010,h);@+ hwrite_disc(&h); @+} @+break;
+case TAG(disc_kind,b011): {disc_t h; @+HGET_DISC(b011,h);@+ hwrite_disc(&h); @+} @+break;
+case TAG(disc_kind,b100): {disc_t h; @+HGET_DISC(b100,h);@+ hwrite_disc(&h); @+} @+break;
+case TAG(disc_kind,b101): {disc_t h; @+HGET_DISC(b101,h);@+ hwrite_disc(&h); @+} @+break;
+case TAG(disc_kind,b110): {disc_t h; @+HGET_DISC(b110,h);@+ hwrite_disc(&h); @+} @+break;
+case TAG(disc_kind,b111): {disc_t h; @+HGET_DISC(b111,h);@+ hwrite_disc(&h); @+} @+break;
+@
+
+@<get macros@>=
+#define @[HGET_DISC(I,Y)@]\
+if ((I)&b100) {uint8_t r=HGET8; (Y).r=r&0x7F; @+ RNG("Replace count",(Y).r,0,31); @+(Y).x=(r&0x80)!=0; @+}\
+@+else { (Y).r=0; @+ (Y).x=false;@+}\
+if ((I)&b010) hget_list(&((Y).p)); else { (Y).p.p=hpos-hstart; @+(Y).p.s=0; @+(Y).p.k=list_kind; @+}\
+if ((I)&b001) hget_list(&((Y).q)); else { (Y).q.p=hpos-hstart; @+(Y).q.s=0; @+(Y).q.k=list_kind; @+}
+@
+
+@<get functions@>=
+void hget_disc_node(disc_t *h)
+{ @<read the start byte |a|@>@;
+ if (KIND(a)!=disc_kind || INFO(a)==b000)
+ QUIT("Hyphen expected at 0x%x got %s,%d",node_pos,NAME(a),INFO(a));
+ HGET_DISC(INFO(a),*h);
+ @<read and check the end byte |z|@>@;
+}
+@
+
+When |hput_disc| is called, the node is already written to the output,
+but empty lists might have been deleted, and the info value needs to be determined.
+Because the info value |b000| is reserved for references, a zero reference
+count is written to avoid this case.
+\putcode
+@<put functions@>=
+uint8_t hput_disc(disc_t *h)
+{ info_t info=b000;
+ if (h->r!=0) info|=b100;
+ if (h->q.s!=0) info|=b011;
+ else if (h->p.s!=0) info|=b010;
+ if (info==b000) { @+info|=b100; @+HPUT8(0);@+}
+ return TAG(disc_kind,info);
+}
+@
+\subsection{Paragraphs}
+The most important procedure that the \HINT\ viewer inherits from \TeX\ is the
+line breaking routine. If the horizontal size of the paragraph is not known,
+breaking the paragraph\index{paragraph} into lines must be postponed and this is done by creating
+a paragraph node. The paragraph node must contain all information that \TeX's
+line breaking\index{line breaking} algorithm needs to do its job.
+
+Besides the horizontal list describing the content of the paragraph and
+the extended dimension describing the horizontal size,
+this is the set of parameters that guide the line breaking algorithm:
+
+\itemize
+\item
+Integer parameters:\hfill\break
+{\tt pretolerance} (badness tolerance before hyphenation),\hfill\break
+{\tt tolerance} (badness tolerance after hyphenation),\hfill\break
+{\tt line\_penalty} (added to the badness of every line, increase to get fewer lines),\hfill\break
+{\tt hy\-phen\_pe\-nal\-ty} (penalty for break after hyphenation break),\hfill\break
+{\tt ex\_hy\-phen\_pe\-nal\-ty} (penalty for break after explicit\index{explicit} break),\hfill\break
+{\tt doub\-le\_hy\-phen\_de\-merits} (demerits for double hyphen break),\hfill\break
+{\tt final\_hyphen\_de\-me\-rits} (demerits for final hyphen break),\hfill\break
+{\tt adj\_demerits} (demerits for adjacent incompatible lines),\hfill\break
+{\tt looseness} (make the paragraph that many lines longer than its optimal size),\hfill\break
+{\tt inter\_line\_penalty} (additional penalty between lines),\hfill\break
+{\tt club\_pe\-nal\-ty} (penalty for creating a club line),\hfill\break
+{\tt widow\_penalty} (penalty for creating a widow line),\hfill\break
+{\tt display\_widow\_penalty} (ditto, just before a display),\hfill\break
+{\tt bro\-ken\_pe\-nal\-ty} (penalty for breaking a page at a broken line),\hfill\break
+{\tt hang\_af\-ter} (start/end hanging indentation at this line).
+\item
+Dimension parameters:\hfill\break
+{\tt line\_skip\_limit} (threshold for {\tt line\_skip} instead of {\tt base\-line\_skip}),\hfill\break
+{\tt hang\_in\-dent} (amount of hanging indentation),\hfill\break
+{\tt emergency\_stretch} (stretchability added to every line in the final pass of line breaking).
+\item
+Glue parameters:\hfill\break
+{\tt baseline\_skip} (desired glue between baselines),\hfill\break
+{\tt line\_skip} (interline glue if {\tt baseline\_skip} is infeasible),\hfill\break
+{\tt left\_skip} (glue at left of justified lines),\hfill\break
+{\tt right\_skip} (glue at right of justified lines),\hfill\break
+{\tt par\_fill\_skip} (glue on last line of paragraph).
+\enditemize
+
+
+For a detailed explanation of these parameters and how they influence
+line breaking, you should consult the {\TeX}book\cite{DK:texbook};
+\TeX's {\tt parshape} feature is currently not implemented. There are
+default values for all of these parameters (see section~\secref{defaults}),
+and therefore it might not be necessary to specify any of them.
+Any local adjustments are contained in a list of
+parameters contained in the paragraph node.
+
+A further complication arises from displayed\index{displayed formula} formulas
+that interrupt a paragraph. Such displays are described in the next
+section.
+
+To summarize, a paragraph node in the long format specifies an
+extended dimension, a parameter list,
+and a node list. The extended dimension is given either as an
+|xdimen| node (info bit |b100|) or as a reference; similarly the parameter list
+can be embedded in the node (info bit |b010|) or again it is given by a reference.
+
+
+\readcode
+@s PAR symbol
+@s par symbol
+@s xdimen_ref symbol
+@s param_ref symbol
+@s par_dimen symbol
+
+
+@<symbols@>=
+%token PAR "par"
+%type <info> par
+@
+
+@<scanning rules@>=
+::@=par@> :< return PAR; >:
+@
+
+
+The following parsing rules are slightly more complicated than I would like them to be, but it seems more important
+to achieve a regular layout of the short format nodes where all sub nodes are located at the end of a node.
+In this case, I want to put a |param_ref| before an |xdimen| node, but otherwise have
+the |xdimen_ref| before a |param_list|.
+The |par_dimen| rule is introduced only to avoid a reduce/reduce conflict in the parser.
+The parsing of |empty_param_list| and |non_empty_param_list| is explained in
+section~\secref{paramlist}.
+
+@<parsing rules@>=
+par_dimen: xdimen { hput_xdimen_node(&($1)); };
+par: xdimen_ref param_ref list {$$=b000;}
+ | xdimen_ref empty_param_list non_empty_param_list list { $$=b010;}
+ | xdimen_ref empty_param_list list { $$=b010;}
+ | xdimen param_ref { hput_xdimen_node(&($1)); } list { $$=b100;}
+ | par_dimen empty_param_list non_empty_param_list list { $$=b110;}
+ | par_dimen empty_param_list list { $$=b110;};
+
+content_node: start PAR par END { hput_tags($1,TAG(par_kind,$3));};
+@
+
+\getcode
+@<cases to get content@>=
+@t\1\kern1em@>
+case TAG(par_kind,b000): @+HGET_PAR(b000);@+break;
+case TAG(par_kind,b010): @+HGET_PAR(b010);@+break;
+case TAG(par_kind,b100): @+HGET_PAR(b100);@+break;
+case TAG(par_kind,b110): @+HGET_PAR(b110);@+break;
+@
+
+@<get macros@>=
+#define @[HGET_PAR(I)@] @/\
+{ uint8_t n;\
+ if ((I)==b100) {n=HGET8; @+REF(param_kind,n);@+}\
+ if ((I)&b100) {xdimen_t x; @+hget_xdimen_node(&x); @+hwrite_xdimen(&x);@+} else HGET_REF(xdimen_kind);\
+ if ((I)&b010) { list_t l; @+hget_param_list(&l); @+hwrite_param_list(&l); @+} \
+ else if ((I)!=b100) HGET_REF(param_kind)@; else hwrite_ref(n);\
+ { list_t l; @+hget_list(&l);@+ hwrite_list(&l); @+}}
+@
+
+
+\subsection{Mathematics}\index{Mathematics}\index{displayed formula}
+\gdef\subcodetitle{Displayed Math}
+Being able to handle mathematics\index{mathematics} nicely is one
+of the primary features of \TeX\ and
+so you should expect the same from \HINT.
+We start here with the more complex case---displayed equations---and finish with the
+simpler case of mathematical formulas that are part of the normal flow of text.
+
+Displayed equations occur inside a paragraph\index{paragraph}
+node. They interrupt normal processing of the paragraph and the
+paragraph processing is resumed after the display. Positioning of the
+display depends on several parameters, the shape of the paragraph, and
+the length of the last line preceding the display. Displayed formulas
+often feature an equation number which can be placed either left or
+right of the formula. Also the size of the equation number will
+influence the placement of the formula.
+
+In a \HINT\ file, the parameter list is followed by a list of content
+nodes, representing the formula, and an optional horizontal box
+containing the equation number.
+
+In the short format, we use the info bit |b100| to indicate the
+presence of a parameter list (which might be empty---so it's actually the absence of a
+reference to a parameter list); the info bit |b010| to indicate the presence of
+a left equation number; and the info bit |b001| for a right
+equation\index{equation number} number.
+
+In the long format, we use ``{\tt eqno}'' or ``{\tt left eqno}'' to indicate presence and
+placement of the equation number.
+
+\readcode
+@s MATH symbol
+@s math symbol
+@<symbols@>=
+%token MATH "math"
+%type <info> math
+@
+
+@<scanning rules@>=
+::@=math@> :< return MATH; >:
+@
+
+@<parsing rules@>=
+math: param_ref list {$$=b000;}
+ | param_ref list hbox_node {$$=b001;}
+ | param_ref hbox_node list {$$=b010;}
+ | empty_param_list list {$$=b100;}
+ | empty_param_list list hbox_node {$$=b101;}
+ | empty_param_list hbox_node list {$$=b110;}
+ | empty_param_list non_empty_param_list list {$$=b100;}
+ | empty_param_list non_empty_param_list list hbox_node {$$=b101;}
+ | empty_param_list non_empty_param_list hbox_node list {$$=b110;};
+
+content_node: start MATH math END @/{ hput_tags($1,TAG(math_kind,$3));};
+@
+
+\getcode
+@<cases to get content@>=
+@t\1\kern1em@>
+case TAG(math_kind,b000): HGET_MATH(b000); @+ break;
+case TAG(math_kind,b001): HGET_MATH(b001); @+ break;
+case TAG(math_kind,b010): HGET_MATH(b010); @+ break;
+case TAG(math_kind,b100): HGET_MATH(b100); @+ break;
+case TAG(math_kind,b101): HGET_MATH(b101); @+ break;
+case TAG(math_kind,b110): HGET_MATH(b110); @+ break;
+@
+
+@<get macros@>=
+#define @[HGET_MATH(I)@] \
+if ((I)&b100) { list_t l; @+hget_param_list(&l); @+hwrite_param_list(&l); @+} \
+else HGET_REF(param_kind);\
+if ((I)&b010) hget_hbox_node(); \
+{ list_t l; @+hget_list(&l);@+ hwrite_list(&l); @+} \
+if ((I)&b001) hget_hbox_node();
+@
+
+\gdef\subcodetitle{Text Math}
+Things are much simpler if mathematical formulas are embedded in regular text.
+Here it is just necessary to mark the beginning and the end of the formula
+because glue inside a formula is not a possible point for a line break.
+To break the line within a formula you can insert a penalty node.
+
+In the long format, such a simple math node just consists of the keyword ``on''
+or ``off''. In the short format, there are two info values still unassigned:
+we use |b011| for ``off'' and |b111| for ``on''.
+
+
+\readcode
+@s ON symbol
+@s OFF symbol
+@s on_off symbol
+@<symbols@>=
+%token ON "on"
+%token OFF "off"
+%type <i> on_off
+@
+
+@<scanning rules@>=
+::@=on@> :< return ON; >:
+::@=off@> :< return OFF; >:
+@
+
+@<parsing rules@>=
+on_off: ON {$$=1;} | OFF {$$=0;};
+math: on_off { $$=b011|($1<<2); };
+@
+
+\getcode
+@<cases to get content@>=
+@t\1\kern1em@>
+case TAG(math_kind,b111): hwritef(" on");@+break;
+case TAG(math_kind,b011): hwritef(" off");@+break;
+@
+
+Note that \TeX\ allows math nodes to specify a width using the current value of
+mathsurround. If this width is nonzero, it is equivalent to inserting a
+kern node before the math on node and after the math off node.
+
+\subsection{Adjustments}\label{adjust}
+An adjustment\index{adjustment} occurs only in paragraphs\index{paragraph}.
+When the line breaking\index{line breaking} routine finds an adjustment, it inserts
+the vertical material contained in the adjustment node right after the current line.
+Adjustments simply contain a list node.
+
+\vbox{\readcode\vskip -\baselineskip\putcode}
+@s ADJUST symbol
+@<symbols@>=
+%token ADJUST "adjust"
+@
+
+@<scanning rules@>=
+::@=adjust@> :< return ADJUST; >:
+@
+
+@<parsing rules@>=
+content_node: start ADJUST list END { hput_tags($1,TAG(adjust_kind,1));};
+@
+
+\vbox{\getcode\vskip -\baselineskip\writecode}
+@<cases to get content@>=
+@t\1\kern1em@>
+case TAG(adjust_kind,1):@+ { list_t l;@+hget_list(&l); @+ hwrite_list(&l); @+} @+ break;
+@
+
+\subsection{Tables}\index{alignment}
+As long as a table contains no dependencies on \.{hsize} and \.{vsize},
+Hi\TeX\ can expand an alignment into a set of nested horizontal and
+vertical boxes and no special processing is required.
+As long as only the size of the table itself but neither the tabskip
+glues nor the table content depends on \.{hsize} or \.{vsize}, the table
+just needs an outer node of type |hset_kind| or |vset_kind|. If there
+is non aligned material inside the table that depends on \.{hsize} or
+\.{vsize}, a vpack or hpack node is still sufficient.
+
+While it is reasonable to restrict the tabskip glues to be ordinary
+glue values without \.{hsize} or \.{vsize} dependencies, it might be
+desirable to have content in the table that does depend on \.{hsize} or
+\.{vsize}. For the latter case, we need a special kind of table
+node. Here is why:
+
+As soon as the dimension of an item in the table is an extended
+dimension, it is no longer possible to compute the maximum natural with
+of a column, because it is not possible to compare extended dimensions
+without knowing \.{hsize} and \.{vsize}. Hence the computation of maximum
+widths needs to be done in the viewer. After knowing the width of the columns,
+the setting of tabskip glues is easy to compute.
+
+To implement these extended tables, we will need a table node that
+specifies a direction, either horizontal or vertical; a list of
+tabskip glues, with the provision that the last tabskip glue in the
+list is repeated as long as necessary; and a list of table content.
+The table's content is stacked, either vertical or
+horizontal, orthogonal to the alignment direction of the table.
+The table's content consists of nonaligned content, for example extra glue
+or rules, and aligned content.
+Each element of aligned content
+is called an outer item and it consist of a list of inner items.
+For example in a horizontal alignment, each row is an outer item
+and each table entry in that row is an inner item.
+An inner item contains a box node (of kind |hbox_kind|, |vbox_kind|,
+|hset_kind|, |vset_kind|, |hpack_kind|, or |vpack_kind|) followed by
+an optional span count.
+
+The glue of the boxes in the inner items will be reset so that all boxes in the same
+column reach the same maximum column with. The span counts will be replaced by
+the appropriate amount of empty boxes and tabskip glues. Finally the
+glue in the outer item will be set to obtain the desired size
+of the table.
+
+The definitions below specify just a |list| for the list of tabskip glues and a
+list for the outer table items.
+This is just for convenience; the first list must contain glue
+nodes and the second list must contain nonaligned content and inner item nodes.
+
+We reuse the |H| and |V| tokens, defined as part of the specification
+of extended dimensions, to indicate the alignment direction of the
+table. To tell a reference to an extended dimension from a reference
+to an ordinary dimension, we prefix the former with an |XDIMEN| token;
+for the latter, the |DIMEN| token is optional. The scanner will
+recognize not only ``item'' as an |ITEM| token but also ``row'' and
+''column''. This allows a more readable notation, for example by
+marking the outer items as rows and the inner items as columns.
+
+In the short format, the |b010| bit is used to mark a vertical table
+and the |b101| bits indicate how the table size is specified; an outer
+item node has the info value |b000|, an inner item node with info
+value |b111| contains an extra byte for the span count, otherwise the
+info value is equal to the span count.
+
+
+
+
+
+
+\readcode
+@s TABLE symbol
+@s ITEM symbol
+@s table symbol
+@s span_count symbol
+
+@<symbols@>=
+%token TABLE "table"
+%token ITEM "item"
+%type <info> table span_count
+@
+
+@<scanning rules@>=
+::@=table@> :< return TABLE; >:
+::@=item@> :< return ITEM; >:
+::@=row@> :< return ITEM; >:
+::@=column@> :< return ITEM; >:
+@
+
+@<parsing rules@>=
+span_count: UNSIGNED { $$=hput_span_count($1); };
+content_node: start ITEM content_node END { hput_tags($1,TAG(item_kind,1)); };
+content_node: start ITEM span_count content_node END {@+ hput_tags($1,TAG(item_kind,$3));};
+content_node: start ITEM list END { hput_tags($1,TAG(item_kind,b000));};
+
+table: H box_goal list list {$$=$2;};
+table: V box_goal list list {$$=$2|b010;};
+
+content_node: start TABLE table END { hput_tags($1,TAG(table_kind,$3));};
+@
+
+\getcode
+@<cases to get content@>=
+@t\1\kern1em@>
+case TAG(table_kind,b000): @+ HGET_TABLE(b000); @+ break;
+case TAG(table_kind,b001): @+ HGET_TABLE(b001); @+ break;
+case TAG(table_kind,b010): @+ HGET_TABLE(b010); @+ break;
+case TAG(table_kind,b011): @+ HGET_TABLE(b011); @+ break;
+case TAG(table_kind,b100): @+ HGET_TABLE(b100); @+ break;
+case TAG(table_kind,b101): @+ HGET_TABLE(b101); @+ break;
+case TAG(table_kind,b110): @+ HGET_TABLE(b110); @+ break;
+case TAG(table_kind,b111): @+ HGET_TABLE(b111); @+ break;@#
+
+case TAG(item_kind,b000): @+{@+ list_t l;@+ hget_list(&l);@+ hwrite_list(&l);@+ } @+ break;
+case TAG(item_kind,b001): hget_content_node(); @+ break;
+case TAG(item_kind,b010): hwritef(" 2");@+hget_content_node(); @+ break;
+case TAG(item_kind,b011): hwritef(" 3");@+hget_content_node(); @+ break;
+case TAG(item_kind,b100): hwritef(" 4");@+hget_content_node(); @+ break;
+case TAG(item_kind,b101): hwritef(" 5");@+hget_content_node(); @+ break;
+case TAG(item_kind,b110): hwritef(" 6");@+hget_content_node(); @+ break;
+case TAG(item_kind,b111): hwritef(" %u",HGET8);@+hget_content_node(); @+ break;
+@
+
+@<get macros@>=
+#define @[HGET_TABLE(I)@] \
+if(I&b010) hwritef(" v"); @+else hwritef(" h"); \
+if ((I)&b001) hwritef(" add");@+ else hwritef(" to");\
+if ((I)&b100) {xdimen_t x; hget_xdimen_node(&x); @+hwrite_xdimen_node(&x);@+} else HGET_REF(xdimen_kind)@;\
+{@+ list_t l; @+hget_list(&l);@+ hwrite_list(&l);@+ } /* tabskip */ \
+{@+ list_t l; @+hget_list(&l);@+ hwrite_list(&l);@+ } /* items */
+@
+
+
+\putcode
+@<put functions@>=
+info_t hput_span_count(uint32_t n)
+{ if (n==0) QUIT("Span count in item must not be zero");
+ else if (n<7) return n;
+ else if (n>0xFF) QUIT("Span count %d must be less than 255",n);
+ else
+ { HPUT8(n); return 7; }
+}
+@
+\section{Extensions to \TeX}\hascode
+
+\subsection{Images}
+Images behave pretty much like glue\index{glue}. They can stretch (or shrink)
+together with the surrounding glue to fill a horizontal or vertical box.
+Like glue, they stretch in the horizontal direction when filling an horizontal box
+and they stretch in the vertical direction as part of a vertical box.
+Stretchability and shrinkability are optional parts of an image node.
+
+Unlike glue, images have both a width and a height.
+The relation of height to width, the aspect ratio, is preserved by stretching and shrinking.
+
+While glue often has a zero width, images usually have a nonzero natural size and making
+them much smaller is undesirable.
+The natural width and height of an image are optional parts of an image node;
+typically this information is contained in the image data.
+
+The only required part of an image node is the number of the auxiliary section
+where the image data can be found.
+
+@<hint types@>=
+typedef struct {@+
+uint16_t n;@+
+dimen_t w,h;@+
+stretch_t p,m;@+
+} image_t;
+@
+
+
+\readcode
+@s IMAGE symbol
+@s image symbol
+@s image_dimen symbol
+@<symbols@>=
+%token IMAGE "image"
+%type <x> image image_dimen
+@
+
+@<scanning rules@>=
+::@=image@> :< return IMAGE; >:
+@
+
+@<parsing rules@>=
+image_dimen: dimension dimension {$$.w=$1; $$.h=$2;} | {$$.w=$$.h=0; };
+image: UNSIGNED image_dimen plus minus { $$.w=$2.w; $$.h=$2.h; $$.p=$3; $$.m=$4; RNG("Section number",$1,3,max_section_no);$$.n=$1; };
+content_node: start IMAGE image END { hput_tags($1,hput_image(&($3)));}
+@
+
+\writecode
+@<write functions@>=
+void hwrite_image(image_t *x)
+{ hwritef(" %u",x->n);
+ if (x->w!=0 ||x->h!=0) { hwrite_dimension(x->w); hwrite_dimension(x->h);@+}
+ hwrite_plus(&x->p);
+ hwrite_minus(&x->m);
+}
+@
+
+\getcode
+@<cases to get content@>=
+@t\1\kern1em@>
+case TAG(image_kind,b100): @+ { image_t x;@+HGET_IMAGE(b100,x);@+}@+break;
+case TAG(image_kind,b101): @+ { image_t x;@+HGET_IMAGE(b101,x);@+}@+break;
+case TAG(image_kind,b110): @+ { image_t x;@+HGET_IMAGE(b110,x);@+}@+break;
+case TAG(image_kind,b111): @+ { image_t x;@+HGET_IMAGE(b111,x);@+}@+break;
+@
+
+@<get macros@>=
+#define @[HGET_IMAGE(I,X)@] @/\
+HGET16((X).n);RNG("Section number",(X).n,3,max_section_no); \
+if (I&b010) {HGET32((X).w);HGET32((X).h);@+} \
+else (X).w=(X).h=0;\
+if (I&b001) {HGET_STRETCH((X).p);HGET_STRETCH((X).m);@+}\
+else { (X).p.f=(X).m.f=0.0; (X).p.o=(X).m.o=normal_o;@+}\
+hwrite_image(&(X));
+@
+
+
+\putcode
+@<put functions@>=
+uint8_t hput_image(image_t *x)
+{ info_t i=b100;
+ HPUT16(x->n);
+ if (x->w!=0 || x->h!=0)@+ {HPUT32(x->w); HPUT32(x->h); i|=b010;@+ }
+ if (x->p.f!=0.0 || x->m.f!=0.0)@+ {hput_stretch(&x->p); hput_stretch(&x->m); i|=b001;@+ }
+ return TAG(image_kind,i);
+}
+@
+
+
+\subsection{Positions, Outlines, Links, and Labels}\label{labels}
+\index{position}\index{outline}\index{link}\index{label}
+A viewer can usually not display the entire content section of
+a \HINT\ file. Instead it will display a page of content and will give
+its user various means to change the page. This might be as simple as
+a ``page down'' or ``page up'' button (or gesture) and as
+sophisticated as searching using regular expressions. More
+traditional ways to navigate the content include the use of a table of
+content or an index of keywords. All these methods of changing a page
+have in common that a part of the content that fits nicely in the
+screen area provided by the output device must be rendered given a
+position inside the content section.
+
+
+Let's assume that the viewer uses a \HINT\ file in short
+format---after all that's the format designed for precisely this use.
+A position inside the content section is then the position of the
+starting byte of a node. Such a position can be stored as a 32 bit
+number. Because even the smallest node contains two tag bytes,
+the position of any node is strictly smaller than the maximum 32 bit
+number which we can conveniently use as a ``non position''.
+
+@<hint macros@>=
+#define HINT_NO_POS 0xFFFFFFFF
+@
+
+To render a page starting at a given position is not difficult:
+We just read content nodes, starting at the given position and feed
+them to \TeX's page builder until the page is complete. To implement a
+``clickable'' table of content this is good enough. We store with
+every entry in the table of content the position of the section
+header, and when the user clicks the entry, the viewer can display a
+new page starting exactly with that section header.
+
+Things are slightly more complex if we want to implement a ``page
+down'' button. If we press this button, we want the next page to
+start exactly where the current page has ended. This is
+typically in the middle of a paragraph node, and it might even be in
+the middle of an hyphenated word in that paragraph. Fortunately,
+paragraph and table nodes are the only nodes that can be broken across page
+boundaries. But broken paragraph nodes are a common case non the less,
+and unless we want to search for the enclosing node, we need to
+augment in this case the primary 32 bit position inside the content
+section with a secondary position. Most of the
+time, 16 bit will suffice for this secondary position if we give it
+relative to the primary position. Further, if the list of nodes forming the
+paragraph is given as a text, we need to know the current font at the
+secondary position. Of course, the viewer can find it by scanning the
+initial part of the text, but when we think of a page down button, the
+viewer might already know it from rendering the previous page.
+
+Similar is the case of a ``page up'' button. Only here we need a page
+that ends precisely where our current page starts. Possibly even with
+the initial part of a hyphenated word. Here we need a reverse version
+of \TeX's page builder that assembles a ``good'' page from the bottom
+up instead of from the top down. Sure the viewer can cache the start
+position of the previous page (or the rendering of the entire page) if
+the reader has reached the current page using the page down
+button. But this is not possible in all cases. The reader might have
+reached the current page using the table of content or even an index
+or a search form.
+
+This is the most complex case to consider: a link from an index or a
+search form to the position of a keyword in the main text. Let's assume
+someone looks up the word ``M\"unchen''. Should the viewer then
+generate a page that starts in the middle of a sentence with the word
+``M\"unchen''? Probably not! We want a page that shows at least the whole sentence if
+not the whole paragraph. Of course the program that generates the
+link could specify the position of the start of the paragraph instead
+of the position of the word. But that will not solve the problem. Just
+imagine reading the groundbreaking masterpiece of a German philosopher
+on a small hand-held device: the paragraph will most likely be very
+long and perhaps only part of the first sentence will fit on the small
+screen. So the desired keyword might not be found on the page that
+starts with the beginning of the paragraph; it might not even be on
+the next or next to next page. Only the viewer can decide what is the
+best fragment of content to display around the position of the given
+keyword.
+
+To summarize, we need three different ways to render a page for a given position:
+\itemize
+\item A page that starts exactly at the given position.
+\item A page that ends exactly at the given position.
+\item The ``best'' page that contains the given position somewhere in the middle.
+\enditemize
+
+\noindent
+A possible way to find the ``best'' page for the latter case
+could be the following:
+\itemize
+\item If the position is inside a paragraph, break the paragraph
+ into lines. One line will contain
+ the given position. Let's call this the destination line.
+\item If the paragraph will not fit entirely on the page,
+ start the page with the beginning of the
+ paragraph if that will place the destination line on the page, otherwise
+ start with a line in the paragraph that is about half a page
+ before the destination line.
+\item Else traverse the content list backward for about $2/3$ of the
+ page height and forward for about $2/3$ of the page height, searching
+ for the smallest negative penalty node. Use the penalty node found as
+ either the beginning or ending of the page.
+\item If there are several equally low negative penalty nodes. Prefer
+ penalties preceding the destination line over penalty nodes following
+ it. A good page start is more important than a good page end.
+\item If there are are still several equally low negative penalty
+ nodes, choose the one whose distance to the destination line is closest
+ to $1/2$ of the page height.
+\item If no negative penalty nodes could be found, start the page with
+ the paragraph containing the destination line.
+\item Once the page start (or end) is found, use \TeX's page builder
+ (or its reverse variant) to complete the page.
+\enditemize
+
+We call content nodes that reference some position inside the content section
+``link'' nodes. The position that is referenced is called the destination of the link.
+Link nodes occur always in pairs of an ``on'' link
+followed by a corresponding ``off'' link that both reference the same position
+%, the same nesting level, % not sure!
+and no other link nodes between them.
+The content between the two will constitute the visible part of the link.
+
+To encode a position inside the content section that can be used
+as the destination of a link node, an other kind of node is needed which
+we call a ``label''.
+
+Links are not the only way to navigate inside a large
+document. The user interface can also present an ``outline''
+of the document that can be used for navigation.
+An outline node implements an association between a name displayed by the
+user interface of the \HINT\ viewer and the destination position in the \HINT\ document.
+
+It is possible though that outline nodes, link nodes, and label nodes can share
+the same kind-value and we have |outline_kind==link_kind==label_kind|.
+To distinguish an outline node from a label node---both occur
+in the short format definition section---the |b100| info bit is set in an
+outline node.
+
+
+@<get functions@>=
+void hget_outline_or_label_def(info_t i, uint32_t node_pos)
+{ @+if (i&b100)
+ @<get and write an outline node@>@;
+ else
+ @<get and store a label node@>@;
+}
+@
+
+The next thing we need to implement is a new maximum number
+for outline nodes. We store this number in the variable
+|max_outline| and limit it to a 16 bit value.
+
+In the short format, the value of |max_outline| is stored with the
+other maximum values using the kind value |outline_kind==label_kind| and the info
+value |b100| for single byte and |b101| for a two byte value.
+
+\codesection{\getsymbol}{Reading the Short Format}\getindex{1}{7}{Special Maximum Values}
+@<cases of getting special maximum values@>=
+@t\1\kern1em@>
+case TAG(outline_kind,b100):
+case TAG(outline_kind,b101): max_outline=n;
+ DBG(DBGDEF|DBGLABEL,"max(outline) = %d\n",max_outline); break;
+@
+
+\codesection{\putsymbol}{Writing the Short Format}\putindex{1}{7}{Special Maximum Values}
+@<cases of putting special maximum values@>=
+if (max_outline>-1)
+{ uint32_t pos=hpos++-hstart;
+ DBG(DBGDEF|DBGLABEL,"max(outline) = %d\n",max_outline);
+ hput_tags(pos,TAG(outline_kind,b100|(hput_n(max_outline)-1)));
+}
+@
+
+\codesection{\wrtsymbol}{Writing the Long Format}\wrtindex{1}{7}{Special Maximum Values}
+@<cases of writing special maximum values@>=
+@t\1\kern1em@>
+case label_kind:
+if (max_ref[label_kind]>-1)@/
+{ hwrite_start();
+ hwritef("label %d",max_ref[label_kind]);
+ hwrite_end();@+
+}
+if (max_outline>-1)@/
+{ hwrite_start();
+ hwritef("outline %d", max_outline);
+ hwrite_end();@+
+}
+break;
+@
+
+\codesection{\redsymbol}{Reading the Long Format}\redindex{1}{7}{Special Maximum Values}
+@<parsing rules@>=
+max_value: OUTLINE UNSIGNED { max_outline=$2;
+ RNG("max outline",max_outline,0, 0xFFFF);
+ DBG(DBGDEF|DBGLABEL,"Setting max outline to %d\n",max_outline);
+ };
+@
+
+After having seen the maximum values, we now explain labels, then links,
+and finally outlines.
+
+
+To store labels, we define a data type |label_t| and an array |labels|
+indexed by the labels reference number.
+
+@<hint basic types@>=
+typedef struct
+{@+ uint32_t pos; /* position */
+ uint8_t where; /* where on the rendered page */
+ bool used; /* label used in a link or an outline */
+ int next; /* reference in a linked list */
+ uint32_t pos0;@+ uint8_t f; /* secondary position */
+} label_t;
+@
+
+The |where| field indicates where the label position
+should be on the rendered page: at the top,
+at the bottom, or somewhere in the middle.
+An undefined label has |where| equal to zero.
+
+@<hint macros@>=
+#define LABEL_UNDEF 0
+#define LABEL_TOP 1
+#define LABEL_BOT 2
+#define LABEL_MID 3
+@
+
+@<common variables@>=
+label_t *labels;
+int first_label=-1;
+@
+The variable |first_label| will be used together with the |next| field of
+a label to construct a linked list of labels.
+
+@<initialize definitions@>=
+if (max_ref[label_kind]>=0)@/
+ ALLOCATE(labels,max_ref[label_kind]+1,label_t);
+@
+
+The implementation of labels has to solve the
+problem of forward links:
+a link node that references a label
+that is not yet defined.
+We solve this problem by
+keeping all labels in the definition section.
+So for every label at least a definition is available
+before we start with the content section and we can fill
+in the position when the label is found.
+If we restrict labels to the definition section and
+do not have an alternative representation, the number of possible references
+is a hard limit on the number of labels in a document.
+Therefore label references are allowed to use 16 bit reference numbers.
+In the short format,
+the |b001| bit indicates a two byte reference number if set, and a one byte
+reference number otherwise.
+
+In the short format, the complete information about a label is in the definition section.
+In the long format, this is not possible because we do not have node positions.
+Therefore we will put label nodes at appropriate points in the content section
+and compute the label position when writing the short format.
+
+\gdef\subcodetitle{Labels}
+\readcode
+@s LABEL symbol
+@s BOT symbol
+@s MID symbol
+@s placement symbol
+
+@<symbols@>=
+%token LABEL "label"
+%token BOT "bot"
+%token MID "mid"
+%type <i> placement
+@
+
+@<scanning rules@>=
+::@=label@> :< return LABEL; >:
+::@=bot@> :< return BOT; >:
+::@=mid@> :< return MID; >:
+@
+
+A label node specifies the reference number and a placement.
+
+@<parsing rules@>=
+placement: TOP {$$=LABEL_TOP;} | BOT {$$=LABEL_BOT;} | MID {$$=LABEL_MID;} | {$$=LABEL_MID;};
+content_node: START LABEL REFERENCE placement END @|
+ { hset_label($3,$4); @+}
+@
+
+
+After parsing a label, the function |hset_label| is called.
+
+@<put functions@>=
+void hset_label(int n,int w )
+{ label_t *t;
+ REF_RNG(label_kind,n);
+ t=labels+n;@/
+ if (t->where!=LABEL_UNDEF)
+ MESSAGE("Duplicate definition of label %d\n",n);
+ t->where=w;
+ t->pos=hpos-hstart;
+ t->pos0=hpos0-hstart;
+ t->next=first_label; first_label=n;
+}
+@
+
+
+All that can be done by the above function
+is storing the data obtained in the |labels| array.
+The generation of the short format output is
+postponed until the entire content section has been parsed and
+the positions of all labels are known.
+
+One more complication needs to be considered: The |hput_list| function
+is allowed to move lists in the output stream and if positions
+inside the list were recorded in a label, these labels need an
+adjustment. To find out quickly if any labels are affected,
+the |hset_label| function
+constructs a linked list of labels starting with the reference number
+of the most recent label in |first_label| and the
+reference number of the label preceding label |i| in |labels[i].next|.
+Because labels are recorded with increasing positions,
+the list will be sorted with positions decreasing.
+
+@<adjust label positions after moving a list@>=
+{ int i;
+ for (i=first_label;i>=0 && labels[i].pos>=l->p;i=labels[i].next)
+ { DBG(DBGNODE|DBGLABEL,"Moving label *%d by %d\n", i,d);@/
+ labels[i].pos+=d;
+ if (labels[i].pos0>=l->p) labels[i].pos0+=d;
+ }
+}
+@
+
+
+The |hwrite_label| function\label{hwritelabel} is the reverse of the above parsing rule.
+Note that it is different from the
+usual |hwrite_|\dots\ functions. And we will see shortly why that is so.
+
+%see |hwrite_range|
+\writecode
+@<write functions@>=
+void hwrite_label(void) /* called in |hwrite_end| and at the start of a list */
+{@+ while (first_label>=0 && (uint32_t)(hpos-hstart)>=labels[first_label].pos)@/
+ { label_t *t=labels+first_label;
+ DBG(DBGLABEL,"Inserting label *%d\n", first_label);
+ hwrite_start();
+ hwritef("label *%d",first_label);
+ if (t->where==LABEL_TOP) hwritef(" top");
+ else if (t->where==LABEL_BOT) hwritef(" bot");
+ nesting--;hwritec('>'); /* avoid a recursive call to |hwrite_end| */
+ first_label=labels[first_label].next;
+ }
+}
+@
+
+The short format specifies the label positions in the definition section.
+This is not possible in the long format because there are no ``positions''
+in the long format. Therefore long format label nodes must
+be inserted in the content section just before those nodes
+that should come after the label. The function |hwrite_label| is called
+in |hwrite_end|. At that point |hpos| is the position of the next node
+and it can be compared with the positions of the labels taken from
+the definition section.
+Because |hpos| is strictly increasing while reading the content section,
+the comparison can be made efficient by sorting the labels.
+The sorting uses the |next| field in the
+array of |labels| to construct a linked list. After sorting, the value of
+|first_label| is the index of the label with the smallest position;
+and for each |i|, the value of |labels[i].next| is the index of
+the label with the next bigger position. If |labels[i].next| is negative,
+there is no next bigger position.
+Currently a simple insertion sort is used.
+The insertion sort will work well if the labels are already
+mostly in ascending order.
+If we expect lots of labels in random order,
+a more sophisticated sorting algorithm might be appropriate.
+
+
+
+@<write functions@>=
+void hsort_labels(void)
+{ int i;
+ if (max_ref[label_kind]<0)
+ { first_label=-1; return; @+} /* empty list */
+ first_label=max_ref[label_kind];
+ while (first_label>=0 && labels[first_label].where==LABEL_UNDEF)
+ first_label--;
+ if (first_label<0) return; /* no defined labels */
+ labels[first_label].next=-1;
+ DBG(DBGLABEL,"Sorting %d labels\n",first_label+1);
+ for (i=first_label-1; i>=0; i--) /* insert label |i| */
+ if (labels[i].where!=LABEL_UNDEF)@/
+ { uint32_t pos=labels[i].pos;
+ if (labels[first_label].pos >= pos)@/
+ { labels[i].next= first_label; first_label=i;@+ } /* new smallest */
+ else @/
+ { int j;
+ for (j= first_label;
+ labels[j].next>=0 && labels[labels[j].next].pos<pos;
+ j=labels[j].next) continue;
+ labels[i].next=labels[j].next; labels[j].next=i;
+ }
+ }
+}
+@
+
+
+The following code is used to get label information from the
+definition section and store it in the |labels| array.
+The |b010| bit indicates the presence of a secondary position for the label.
+
+\getcode
+@<get and store a label node@>=
+{ label_t *t;
+ int n;
+ if (i&b001) HGET16(n); @+else n=HGET8;
+ REF_RNG(label_kind,n);
+ t=labels+n;
+ if (t->where!=LABEL_UNDEF)
+ DBG(DBGLABEL,"Duplicate definition of label %d at 0x%x\n",n, node_pos);
+ HGET32(t->pos);
+ t->where=HGET8;
+ if (t->where==LABEL_UNDEF || t->where>LABEL_MID)
+ DBG(DBGLABEL,"Label %d where value invalid: %d at 0x%x\n",n,t->where,node_pos);
+ if (i&b010) /* secondary position */
+ { HGET32(t->pos0); t->f=HGET8;@+}
+ else t->pos0=t->pos;
+ DBG(DBGLABEL,"Defining label %d at 0x%x\n",n,t->pos);
+}
+@
+
+
+The function |hput_label| is simply the reverse of the above code.
+
+\putcode
+@<put functions@>=
+uint8_t hput_label(int n, label_t *l)
+{ info_t i=b000;
+ HPUTX(13);
+ if (n>0xFF) {i|=b001; HPUT16(n);@+}@+ else HPUT8(n);
+ HPUT32(l->pos);
+ HPUT8(l->where);
+ if (l->pos!=l->pos0)
+ { i|=b010; HPUT32(l->pos0); HPUT8(l->f); @+}
+ return TAG(label_kind,i);
+}
+@
+
+|hput_label_defs| is called by the parser after the entire content
+section has been processed; it appends the label definitions
+to the definition section.
+%Using the fact that the linked list
+%starting at |first_label| already contains all labels in
+%order of descending position, we could easily output the
+%labels in sorted order and reconstruct the sorting while reading
+%in the labels. The \HINT\ format however does not require
+%label nodes to be sorted and the |hsort_labels| function
+%can not be avoided.
+The outlines are stored after the labels because they reference the labels.
+@<put functions@>=
+extern void hput_definitions_end(void);
+extern uint8_t hput_outline(outline_t *t);
+void hput_label_defs(void)
+{ int n;
+ section_no=1;
+ hstart=dir[1].buffer;
+ hend=hstart+ dir[1].bsize;
+ hpos=hstart+dir[1].size;@/
+ @<output the label definitions@>@;
+ @<output the outline definitions@>@;
+ hput_definitions_end();
+}
+@
+
+@<output the label definitions@>=
+ for (n=0; n<=max_ref[label_kind]; n++)@/
+ { label_t *l=labels+n;
+ uint32_t pos;
+ if (l->used)@/
+ { pos=hpos++-hstart;
+ hput_tags(pos,hput_label(n,l));
+ if (l->where==LABEL_UNDEF)
+ MESSAGE("WARNING: Label *%d is used but not defined\n",n);
+ else
+ DBG(DBGDEF|DBGLABEL,"Label *%d defined 0x%x\n",n,pos);@/
+ }
+ else
+ { if (l->where!=LABEL_UNDEF)
+ { pos=hpos++-hstart;
+ hput_tags(pos,hput_label(n,l));
+ DBG(DBGDEF|DBGLABEL,"Label *%d defined but not used 0x%x\n",n,pos);@/
+ }
+ }
+ }
+@
+
+Links are simpler than labels. They are found only in the
+content section and resemble pretty much what we have seen for other
+content nodes. Let's look at them next.
+When reading a short format link node,
+we use again the |b001| info bit to indicate a 16 bit reference
+number to a label. The |b010| info bit indicates an ``on'' link.
+\gdef\subcodetitle{Links}
+\getcode
+@<get macros@>=
+#define @[HGET_LINK(I)@] @/\
+{ int n; if (I&b001) HGET16(n);@+ else n=HGET8; @+ hwrite_link(n,I&b010); @+}
+@
+
+@<cases to get content@>=
+@t\1\kern1em@>
+case TAG(link_kind,b000): @+ HGET_LINK(b000);@+ break;
+case TAG(link_kind,b001): @+ HGET_LINK(b001);@+ break;
+case TAG(link_kind,b010): @+ HGET_LINK(b010);@+ break;
+case TAG(link_kind,b011): @+ HGET_LINK(b011);@+ break;
+@
+
+The function |hput_link| will insert the link in the output stream and return
+the appropriate tag.
+
+\putcode
+@<put functions@>=
+uint8_t hput_link(int n, int on)
+{ info_t i;
+ REF_RNG(label_kind,n);
+ labels[n].used=true;
+ if (on) i=b010;@+ else i=b000;
+ if (n>0xFF) { i|=b001; HPUT16(n);@+} @+else HPUT8(n);
+ return TAG(link_kind,i);
+}
+@
+
+\readcode
+@s LINK symbol
+@<symbols@>=
+%token LINK "link"
+@
+@<scanning rules@>=
+::@=link@> :< return LINK; >:
+@
+
+@<parsing rules@>=
+content_node:start LINK REFERENCE on_off END
+ {@+ hput_tags($1,hput_link($3,$4));@+ };
+@
+
+\writecode
+@<write functions@>=
+void hwrite_link(int n, uint8_t on)
+{ REF_RNG(label_kind,n);
+ if (labels[n].where==LABEL_UNDEF)
+ MESSAGE("WARNING: Link to an undefined label %d\n",n);
+ hwrite_ref(n);
+ if (on) hwritef(" on");
+ else hwritef(" off");
+}
+@
+
+Now we look at the
+outline nodes which are found only in the definition section.
+Every outline node is associated with a label node, giving the position in the
+document, and a unique title that should tell the user
+what to expect when navigating to this position. For example
+an item with the title ``Table of Content'' should navigate
+to the page that shows the table of content.
+The sequence of outline nodes found in the definition section
+gets a tree structure by assigning to each item a depth level.
+
+@<hint types@>=
+typedef struct {@+
+uint8_t *t; /* title */
+int s; /* title size */
+int d; /* depth */
+uint16_t r; /* reference to a label */
+} outline_t;
+@
+
+@<shared put variables@>=
+outline_t *outlines;
+@
+
+@<initialize definitions@>=
+if (max_outline>=0)@/
+ ALLOCATE(outlines,max_outline+1,outline_t);
+@
+
+Child items follow their parent item and have a bigger depth level.
+In the short format, the first item must be a root item, with
+a depth level of 0. Further, if any item has the depth $d$, then the
+item following it must have either the same depth $d$ in which
+case it is a sibling, or the depth $d+1$ in which case it is a child,
+or a depth $d^\prime$ with $0\le d^\prime<d$ in which case it is a sibling
+of the latest ancestor with depth $d^\prime$. Because the depth is
+stored in a single byte, the maximum depth is |0xFF|.
+
+In the long format, the depth assignments are more flexible.
+We allow any signed integer, but insist that the depth
+assignments can be compressed to depth levels for the
+short format using the following algorithm:
+
+@<compress long format depth levels@>=
+n=0;@+
+while (n<=max_outline)
+ n=hcompress_depth(n,0);
+@
+Outline items must be listed in the order
+in which they should be displayed.
+The function |hcompress_depth(n,c)| will compress the subtree starting at
+|n| with root level |d| to a new tree with the same structure
+and root level |c|. It returns the outline number of the
+following subtree.
+
+@<put functions@>=
+int hcompress_depth(int n, int c)
+{ int d=outlines[n].d;
+ if (c>0xFF)
+ QUIT("Outline %d, depth level %d to %d out of range",n,d,c);
+ while (n<=max_outline)
+ if (outlines[n].d==d)
+ outlines[n++].d=c;
+ else if (outlines[n].d>d)
+ n=hcompress_depth(n,c+1);
+ else break;
+ return n;
+}
+@
+
+For an outline node, the |b001| bit indicates a two byte reference to a label.
+There is no reference number for an outline item itself:
+it is never referenced anywhere in an \HINT\ file.
+
+\gdef\subcodetitle{Outlines}
+\vbox{\getcode\vskip -\baselineskip\writecode}
+
+@<get and write an outline node@>=
+ { int r,d;
+ list_t l;
+ static int outline_no=-1;
+ hwrite_start();@+hwritef("outline");
+ ++outline_no;
+ RNG("outline",outline_no, 0, max_outline);
+ if (i&b001) HGET16(r);@+ else r=HGET8;
+ REF_RNG(link_kind,r);
+ if (labels[r].where==LABEL_UNDEF)@/
+ MESSAGE("WARNING: Outline with undefined label %d at 0x%x\n",@|r, node_pos);
+ hwritef(" *%d",r);@/
+ d=HGET8; hwritef(" %d",d);@/
+ hget_list(&l);hwrite_list(&l);@/
+ hwrite_end();
+ }
+@
+
+When parsing an outline definition in the long format,
+we parse the outline title as a |list| which will
+write the representation of the list to the output stream.
+Writing the outline definitions, however, must be postponed
+until the label have found their way into the definition
+section. So we save the list's representation in the
+outline node for later use and remove it again from the
+output stream.
+
+\readcode
+@s OUTLINE symbol
+
+@<symbols@>=
+%token OUTLINE "outline"
+@
+
+@<scanning rules@>=
+::@=outline@> :< return OUTLINE; >:
+@
+
+@<parsing rules@>=
+def_node: START OUTLINE REFERENCE integer position list END {
+ static int outline_no=-1;
+ $$.k=outline_kind; $$.n=$3;
+ if ($6.s==0) QUIT("Outline with empty title in line %d",yylineno);
+ outline_no++;
+ hset_outline(outline_no,$3,$4,$5);
+ };
+@
+
+@<put functions@>=
+void hset_outline(int m, int r, int d, uint32_t pos)
+{ outline_t *t;
+ RNG("Outline",m,0,max_outline);
+ t=outlines+m;
+ REF_RNG(label_kind,r);
+ t->r=r;
+ t->d=d;
+ t->s=hpos-(hstart+pos);
+ hpos=(hstart+pos);
+ ALLOCATE(t->t,t->s,uint8_t);
+ memmove(t->t,hpos,t->s);
+ labels[r].used=true;
+}
+@
+To output the title, we need to move the list back to the output stream.
+Before doing so, we allocate space (and make sure there is room left for the
+end tag of the outline node), and after doing so, we release
+the memory used to save the title.
+
+@<output the title of outline |*t|@>=
+ memmove(hpos,t->t,t->s);
+ hpos=hpos+t->s;
+ free(t->t);
+@
+
+We output all outline definitions from 0 to |max_outline| and
+check that every one of them has a title. Thereby we make sure
+that in the short format |max_outline| matches the number of
+outline definitions.
+
+\putcode
+@<put functions@>=
+uint8_t hput_outline(outline_t *t)
+{ info_t i=b100;
+ HPUTX(t->s+4);
+ if (t->r>0xFF) {i|=b001; @+HPUT16(t->r);@+} @+else HPUT8(t->r);
+ labels[t->r].used=true;
+ HPUT8(t->d);
+ @<output the title of outline |*t|@>@;
+ return TAG(outline_kind,i);
+}
+@
+
+@<output the outline definitions@>=
+@<compress long format depth levels@>@;
+for (n=0;n<=max_outline;n++)
+{ outline_t *t=outlines+n;
+ uint32_t pos;
+ pos=hpos++-hstart;
+ if (t->s==0 || t->t==NULL)
+ QUIT("Definition of outline %d has an empty title",n);
+ DBG(DBGDEF|DBGLABEL,"Outline *%d defined\n",n);@/
+ hput_tags(pos,hput_outline(t));
+}
+@
+
+\subsection{Colors}
+Colors\index{color} are certainly one of the features you will find in the final \HINT\ file format.
+Here some remarks must suffice.
+
+A \HINT\ viewer must be capable of rendering a page given just any valid
+position inside the content section. Therefore \HINT\ files are stateless;
+there is no need to search for preceding commands that might change a state
+variable.
+As a consequence, we can not just define a ``color change node''.
+Colors could be specified as an optional parameter of a glyph node, but the
+amount of data necessary would be considerable. In texts, on the other hand,
+a color change control code would be possible because we parse texts only in forward
+direction. The current font would then become a current color and font with the appropriate
+changes for positions.
+
+A more attractive alternative would be to specify colored fonts.
+This would require an optional
+color argument for a font. For example one could have a cmr10 font in black as
+font number 3, and a cmr10 font in blue as font number 4. Having 256 different fonts,
+this is definitely a possibility because rarely you would need that many fonts
+or that many colors. If necessary and desired, one could allow 16 bit font numbers
+of overcome the problem.
+
+Background colors could be associated with boxes as an optional parameter.
+
+
+\section{Replacing \TeX's Page Building Process}
+
+\TeX\ uses an output\index{output routine} routine to finalize the page. It uses the accumulated material
+from the page builder, found in {\tt box255}, attaches headers, footers, and floating material
+like figures, tables, and footnotes. The latter material is specified by insert nodes
+while headers and footers are often constructed using mark nodes.
+Running an output routine requires the full power of the \TeX\ engine and will not be
+part of the \HINT\ viewer. Therefore, \HINT\ replaces output routines by page templates\index{template}.
+As \TeX\ can use different output routines for different parts of a book---for example
+the index might use a different output routine than the main body of text---\HINT\
+will allow multiple page templates. To support different output media, the page
+templates will be named and a suitable user interface may offer the user a selection
+of possible page layouts. In this way, the page layout remains in the hands of the
+book designer, and the user has still the opportunity to pick a layout that best fits
+the display device.
+
+\TeX\ uses insertions to describe floating content that is not necessarily displayed
+where it is specified. Three examples may illustrate this:
+\itemize
+\item Footnotes\footnote*{Like this one.} are specified in the middle of the text but are displayed at the
+bottom of the page. Several
+footnotes\index{footnote} on the same page are collected and displayed together. The
+page layout may specify a short rule to separate footnotes from the
+main text, and if there are many short footnotes, it may use two columns
+to display them. In extreme cases, the page layout may demand a long
+footnote to be split and continued on the next page.
+
+\item Illustrations\index{illustration} may be displayed exactly where specified if there is enough
+room on the page, but may move to the top of the page, the bottom of the page,
+the top of next page, or a separate page at the end of the chapter.
+
+\item Margin notes\index{margin note} are displayed in the margin on the same page starting at the top
+of the margin.
+\enditemize
+
+\HINT\ uses page templates and content streams to achieve similar effects.
+But before I describe the page building\index{page building} mechanisms of \HINT, let me summarize \TeX's page builder.
+
+\TeX's page builder ignores leading glue\index{glue}, kern\index{kern}, and penalty\index{penalty} nodes until the first
+box\index{box} or rule\index{rule} is encountered;
+whatsit\index{whatsit node} nodes do not really contribute anything to a page; mark\index{mark node} nodes are recorded for later use.
+Once the first box, rule, or insert\index{insert node} arrives, \TeX\ makes copies of all parameters
+that influence the page building process and uses these copies. These parameters
+are the |page_goal| and the |page_max_depth|. Further, the variables
+|page_total|, |page_shrink|, |page_stretch|, |page_depth|,
+and {\it insert\_pe\-nal\-ties\/} are initialized to zero.
+The top skip\index{top skip} adjustment is made
+when the first box or rule arrives---possibly after an insert.
+
+Now the page builder accumulates material: normal material goes into {\tt box255}\index{box 255} and will change |page_total|, |page_shrink|,
+|page_stretch|, and |page_depth|. The latter is adjusted so that
+is does not exceed |page_max_depth|.
+
+The handling of inserts\index{insert node} is more complex.
+\TeX\ creates an insert class using \.{newinsert}. This reserves a number $n$
+and four registers: {\tt box\hair$n$} for the inserted material, {\tt count\hair$n$} for the
+magnification factor $f$, {\tt dimen\hair$n$} for the maximum size per page $d$, and {\tt skip\hair$n$} for the
+extra space needed on a page if there are any insertions of class $n$.
+
+For example plain \TeX\ allocates $n=254$ for footnotes\index{footnote} and sets
+{\tt count254} to~$1000$, {\tt dimen254} to 8in, and {\tt skip254} to {\tt \BS bigskipamount}.
+
+An insertion node will specify the insertion class $n$, some vertical material,
+its natural height plus depth $x$, a {\it split\-\_top\-\_skip}, a {\it split\-\_max\_depth},
+and a {\it floa\-ting\-\_pe\-nal\-ty}.
+
+
+Now assume that an insert node with subtype 254 arrives at the page builder.
+If this is the first such insert, \TeX\ will decrease the |page_goal|
+by the width of skip254 and adds its stretchability and shrinkability
+to the total stretchability and shrinkability of the page. Later,
+the output routine will add some space and the footnote rule to fill just that
+much space and add just that much shrinkability and stretchability to the page.
+Then \TeX\ will normally add the vertical material in the insert node to
+box254 and decrease the |page_goal| by $x\times f/1000$.
+
+Special processing is required if \TeX\ detects that there is not enough space on
+the current page to accommodate the complete insertion.
+If already a previous insert did not fit on the page, simply the |floating_penalty|
+as given in the insert node is added to the total |insert_penalties|.
+Otherwise \TeX\ will test that the total natural height plus depth of box254
+including $x$ does not exceed the maximum size $d$ and that the
+$|page_total| + |page_depth| + x\times f/1000 - |page_shrink| \le |page_goal|$.
+If one of these tests fails, the current insertion
+is split in such a way as to make the size of the remaining insertions just pass the tests
+just stated.
+
+Whenever a glue node, or penalty node, or a kern node that is followed by glue arrives
+at the page builder, it rates the current position as a possible end of the page based on
+the shrinkability of the page and the difference between |page_total| and |page_goal|.
+As the page fills, the page breaks tend to become better and better until the
+page starts to get overfull and the page breaks get worse and worse until
+they reach the point where they become |awful_bad|. At that point,
+the page builder returns to the best page break found so far and fires up the
+output routine.
+
+Let's look next at the problems that show up when implementing a replacement mechanism for \HINT.
+
+\enumerate
+\item
+An insertion node can not always specify its height $x$ because insertions may contain paragraphs that need
+to be broken in lines and the height of a paragraph depends in some non obvious way on
+its width.
+
+\item
+Before the viewer can compute the height $x$, it needs to know the width of the insertion. Just imagine
+displaying footnotes in two columns or setting notes in the margin. Knowing the width, it
+can pack the vertical material and derive its height and depth.
+
+\item
+\TeX's plain format provides an insert macro that checks whether there is still space
+on the current page, and if so, it creates a contribution to the main text body, otherwise it
+creates a topinsert. Such a decision needs to be postponed to the \HINT\ viewer.
+
+\item
+\HINT\ has no output routines that would specify something like the space and the rule preceding the footnote.
+
+\item
+\TeX's output routines have the ability to inspect the content of the boxes,
+split them, and distribute the content over the page.
+For example, the output routine for an index set in two column format might
+expect a box containing index entries up to a height of $2\times\.{vsize}$.
+It will split this box in the middle and display the top part in the left
+column and the bottom part in the right column. With this approach, the
+last page will show two partly filled columns of about equal size.
+
+\item
+\HINT\ has no mark nodes that could be used to create page headers or footers.
+Marks, like output routines, contain token lists and need the full \TeX\ interpreter
+for processing them. Hence, \HINT\ does not support mark nodes.
+\endenumerate
+
+Here now is the solution I have chosen for \HINT:
+
+Instead of output routines, \HINT\ will use page templates.
+Page templates are basically vertical boxes with placeholders marking the
+positions where the content of the box registers, filled by the page builder,
+should appear.
+To output the page, the viewer traverses the page template,
+replaces the placeholders by the appropriate box content, and
+sets the glue. Inside the page template, we can use insert nodes to act
+as placeholders.
+
+It is only natural to treat the page's main body, the
+inserts, and the marks using the same mechanism. We call this
+mechanism a content stream\index{stream}.
+Content streams are identified by a stream number in the range 0 to 254;
+the number 255 is used to indicate an invalid stream number.
+The stream number 0 is reserved for the main content stream; it is always defined.
+Besides the main content stream, there are three types of streams:
+\itemize
+\item normal streams correspond to \TeX's inserts and accumulate content on the page,
+\item first\index{first stream} streams correspond to \TeX's first marks and will contain only the first insertion of the page,
+\item last\index{last stream} streams correspond to \TeX's bottom marks and will contain only the last insertion of the page, and
+\item top\index{top stream} streams correspond to \TeX's top marks. Top streams are not yet implemented.
+\enditemize
+
+Nodes from the content section are considered contributions to stream 0 except
+for insert nodes which will specify the stream number explicitly.
+If the stream is not defined or is not used in the current page template, its content is simply ignored.
+
+The page builder needs a mechanism to redirect contributions from one content
+stream to another content stream based on the availability of space.
+Hence a \HINT\ content stream can optionally specify a preferred stream number,
+where content should go if there is still space available, a next stream number,
+where content should go if the present stream has no more space available, and
+a split ratio if the content is to be split between these two streams before
+filling in the template.
+
+Various stream parameters govern the treatment of contributions to the stream
+and the page building process.
+
+\itemize
+\item The magnification factor $f$: Inserting a box of height $h$ to this stream will contribute $h\times f/1000$
+to the height of the page under construction. For example, a stream
+that uses a two column format will have an $f$ value of 500; a stream
+that specifies notes that will be displayed in the page margin will
+have an $f$ value of zero.
+
+\item The height $h$: The extended dimension $h$ gives the maximum height this
+stream is allowed to occupy on the current page.
+To continue the previous example, a stream that will be split into two columns
+will have $h=2\cdot\.{vsize}$ , and a stream that specifies
+notes that will be displayed in the page margin will have
+$h=1\cdot\.{vsize}$. You can restrict the amount of space occupied by
+footnotes to the bottom quarter by setting the corresponding $h$ value
+to $h=0.25\cdot\.{vsize}$.
+
+\item The depth $d$: The dimension $d$ gives the maximum depth this
+stream is allowed to have after formatting.
+
+\item The width $w$: The extended dimension $w$ gives the width of this stream
+when formatting its content. For example margin notes
+should have the width of the margin less some surrounding space.
+
+\item The ``before'' list $b$: If there are any contributions to this
+stream on the current page, the material in list $b$
+is inserted {\it before\/} the material from the stream itself. For
+example, the short line that separates the footnotes from the main
+page will go, together with some surrounding space, into the list~$b$.
+
+\item The top skip glue $g$: This glue is inserted between the material
+from list $b$ and the first box of the stream, reduced
+by the height of the first box. Hence it specifies the distance between
+the material in $b$ and the first baseline of the stream content.
+
+\item The ``after'' list $a$: The list $a$ is treated like list $b$ but
+its material is placed {\it after\/} the material from the stream itself.
+
+\item The ``preferred'' stream number $p$: If $p\ne 255$, it is the number of
+the {\it preferred\/} stream. If stream $p$ has still
+enough room to accommodate the current contribution, move the
+contribution to stream $p$, otherwise keep it. For example, you can
+move an illustration to the main content stream, provided there is
+still enough space for it on the current page, by setting $p=0$.
+
+\item The ``next'' stream number $n$: If $n\ne 255$, it is the number of the
+{\it next\/} stream. If a contribution can not be
+accommodated in stream $p$ nor in the current stream, treat it as an
+insertion to stream $n$. For example, you can move contributions to
+the next column after the first column is full, or move illustrations
+to a separate page at the end of the chapter.
+
+\item The split ratio\index{split ratio} $r$: If $r$ is positive, both $p$ and $n$ must
+be valid stream numbers and contents is not immediately moved to stream $p$ or $n$ as described before.
+Instead the content is kept in the stream itself until the current page is complete.
+Then, before inserting the streams into the page template, the content of
+this stream is formatted as a vertical box, the vertical box is
+split into a top fraction and a bottom fraction in the ratio $r/1000$
+for the top and $(1000-r)/1000$ for the bottom, and finally the top
+fraction is moved to stream $p$ and the bottom fraction to stream
+$n$. You can use this feature for example to implement footnotes
+arranged in two columns of about equal size. By collecting all the
+footnotes in one stream and then splitting the footnotes with $r=500$
+before placing them on the page into a right and left column. Even
+three or more columns can be implemented by cascades of streams using
+this mechanism.
+\enditemize
+
+\subsection{Stream Definitions}
+\index{stream}
+There are four types of streams: normal streams that work like \TeX's inserts;
+and first, last, and top streams that work like \TeX's marks.
+For the latter types, the long format uses a matching keyword and the
+short format the two least significant info bits. All stream definitions
+start with the stream number.
+In definitions of normal streams after the number follows in this order
+\itemize
+\item the maximum insertion height,
+\item the magnification factor, and
+\item information about splitting the stream.
+ It consists of: a preferred stream, a next stream, and a split ratio.
+ An asterisk indicates a missing stream reference, in the
+ short format the stream number 255 serves the same purpose.
+\enditemize
+All stream definitions finish with
+\itemize
+\item the ``before'' list,
+\item an extended dimension node specifying the width of the inserted material,
+\item the top skip glue,
+\item the ``after'' list,
+\item and the total height, stretchability, and shrinkability of the material in
+ the ``before'' and ``after'' list.
+\enditemize
+
+A special case is the stream definition for stream 0, the main content stream.
+None of the above information is necessary for it so it is omitted.
+Stream definitions, including the definition of stream 0,
+occur only inside page template definitions\index{template}
+where they occur twice in two different roles:
+In the stream definition list, they define properties of the stream
+and in the template they mark the insertion point (see section~\secref{page}).
+In the latter case, stream nodes just contain the stream number.
+Because a template looks like ordinary vertical material,
+we like to use the same functions for parsing it.
+But stream definitions are very different from stream content
+nodes. To solve the problem for the long format,
+the scanner will return two different tokens
+when it sees the keyword ``{\tt stream}''.
+In the definition section, it will return
+|STREAMDEF| and in the content section |STREAM|.
+The same problem is solved in the short format
+by using the |b100| bit to mark a definition.
+
+\goodbreak
+\vbox{\readcode\vskip -\baselineskip\putcode}
+
+@s STREAM symbol
+@s STREAMDEF symbol
+@s TOP symbol
+@s FIRST symbol
+@s LAST symbol
+@s NOREFERENCE symbol
+@s stream_type symbol
+@s stream_info symbol
+@s stream_split symbol
+@s stream_link symbol
+@s stream_def_node symbol
+@s stream_ins_node symbol
+@s stream_ref symbol
+
+
+@<symbols@>=
+%token STREAM "stream"
+%token STREAMDEF "stream (definition)"
+%token FIRST "first"
+%token LAST "last"
+%token TOP "top"
+%token NOREFERENCE "*"
+%type <info> stream_type
+%type <u> stream_ref
+%type <rf> stream_def_node
+@
+
+@<scanning rules@>=
+::@=stream@> :< if (section_no==1) return STREAMDEF; else return STREAM;@+ >:
+::@=first@> :< return FIRST; >:
+::@=last@> :< return LAST; >:
+::@=top@> :< return TOP; >:
+::@=\*@> :< return NOREFERENCE; >:
+@
+
+@<parsing rules@>=
+stream_link: ref { REF_RNG(stream_kind,$1); } | NOREFERENCE {HPUT8(255);};
+stream_split: stream_link stream_link UNSIGNED @/{RNG("split ratio",$3,0,1000); HPUT16($3);};
+stream_info: xdimen_node UNSIGNED @/{RNG("magnification factor",$2,0,1000); HPUT16($2);} stream_split;
+
+stream_type: stream_info {$$=0;} |FIRST {$$=1;} @+ | LAST {$$=2;} @+ |TOP {$$=3;} ;
+
+stream_def_node: start STREAMDEF ref stream_type @/
+ list xdimen_node glue_node list glue_node END @/
+ {@+ DEF($$,stream_kind,$3); @+ hput_tags($1,TAG(stream_kind,$4|b100));};
+
+stream_ins_node: start STREAMDEF ref END@/
+ { RNG("Stream insertion",$3,0,max_ref[stream_kind]); hput_tags($1,TAG(stream_kind,b100));};
+
+content_node: stream_def_node @+ | stream_ins_node;
+@
+
+
+\goodbreak
+\vbox{\getcode\vskip -\baselineskip\writecode}
+
+
+
+@<get stream information for normal streams@>=
+{ xdimen_t x;
+ uint16_t f,r;
+ uint8_t n;
+ DBG(DBGDEF,"Defining normal stream %d at " SIZE_F "\n",*(hpos-1),hpos-hstart-2);
+ hget_xdimen_node(&x); @+hwrite_xdimen_node(&x);
+ HGET16(f); @+RNG("magnification factor",f,0,1000);@+ hwritef(" %d",f);
+ n=HGET8; if (n==255) hwritef(" *"); else { REF_RNG(stream_kind,n);@+hwrite_ref(n);@+}
+ n=HGET8; if (n==255) hwritef(" *"); else { REF_RNG(stream_kind,n);@+hwrite_ref(n);@+}
+ HGET16(r); RNG("split ratio",r,0,1000); hwritef(" %d",r);
+}
+@
+
+@<get functions@>=
+static bool hget_stream_def(void)
+{@+ if (KIND(*hpos)!=stream_kind || !(INFO(*hpos)&b100))
+ return false;
+ else
+ { ref_t df;
+ @<read the start byte |a|@>@;
+ DBG(DBGDEF,"Defining stream %d at " SIZE_F "\n",*hpos,hpos-hstart-1);
+ DEF(df,stream_kind,HGET8);
+ hwrite_start();@+hwritef("stream");@+@+hwrite_ref(df.n);
+ if (df.n>0)
+ { xdimen_t x; @+ list_t l;
+ if (INFO(a)==b100) @<get stream information for normal streams@>@;
+ else if (INFO(a)==b101) hwritef(" first");
+ else if(INFO(a)==b110) hwritef(" last");
+ else if (INFO(a)==b111) hwritef(" top");
+ hget_list(&l);@+ hwrite_list(&l);
+ hget_xdimen_node(&x); @+hwrite_xdimen_node(&x);
+ hget_glue_node();@+
+ hget_list(&l);@+ hwrite_list(&l);@+
+ hget_glue_node();
+ }
+ @<read and check the end byte |z|@>@;
+ hwrite_end();
+ return true;
+ }
+}
+
+@
+
+When stream definitions are part of the page template, we call them
+stream insertion points.
+They contain only the stream reference and
+are parsed by the usual content parsing functions.
+
+@<cases to get content@>=
+@t\1\kern1em@>
+case TAG(stream_kind,b100): {uint8_t n=HGET8;@+ REF_RNG(stream_kind,n); @+hwrite_ref(n); @+ break; @+}
+@
+
+
+\subsection{Stream Content}
+Stream\index{stream} nodes occur in the content section where they
+must not be inside other nodes except toplevel
+paragraph\index{paragraph} nodes. A normal stream node contains in this
+order: the stream reference number, the optional stream parameters,
+and the stream content. The content is either a vertical box or an
+extended vertical box. The stream parameters consists of the
+|floating_penalty|, the |split_max_depth|, and the
+|split_top_skip|. The parameterlist can be given
+explicitly or as a reference.
+
+In the short format, the info bits |b010| indicate
+a normal stream content node with an explicit parameter list
+and the info bits |b000| a normal stream with a parameter list reference.
+
+If the info bit |b001| is set, we have a content node of type top, first,
+or last. In this case, the short format has instead of the parameter list
+a single byte indicating the type.
+These types are currently not yet implemented.
+
+\goodbreak
+\vbox{\readcode\vskip -\baselineskip\putcode}
+
+@s stream symbol
+
+@<symbols@>=
+%type <info> stream
+@
+
+@<parsing rules@>=
+stream: empty_param_list list {$$=b010;}
+ | empty_param_list non_empty_param_list list {$$=b010;}
+ | param_ref list {$$=b000;};
+content_node: start STREAM stream_ref stream END
+ @/{@+hput_tags($1,TAG(stream_kind,$4)); @+};
+@
+
+\goodbreak
+\vbox{\getcode\vskip -\baselineskip\writecode}
+
+@<cases to get content@>=
+@t\1\kern1em@>
+case TAG(stream_kind,b000): HGET_STREAM(b000); @+ break;
+case TAG(stream_kind,b010): HGET_STREAM(b010); @+ break;
+@
+
+When we read stream numbers, we relax the define before use policy.
+We just check, that the stream number is in the correct range.
+\goodbreak
+@<get macros@>=
+#define @[HGET_STREAM(I)@] @/\
+ {uint8_t n=HGET8;@+ REF_RNG(stream_kind,n); @+hwrite_ref(n);@+}\
+if ((I)&b010) { list_t l; @+hget_param_list(&l); @+hwrite_param_list(&l); @+} \
+else HGET_REF(param_kind);\
+{ list_t l; @+hget_list(&l);@+ hwrite_list(&l); @+}
+@
+
+
+
+
+\subsection{Page Template Definitions}\label{page}
+A \HINT\ file can define multiple page templates\index{template}. Not only
+might an index demand a different page layout than the main body of text,
+also the front page or the chapter headings might use their own page templates.
+Further, the author of a \HINT\ file might define a two column format as
+an alternative to a single column format to be used if the display area
+is wide enough.
+
+To help in selecting the right page template, page template definitions start with
+a name and an optional priority\index{priority}; the default priority is 1.
+The names might appear in a menu from which the user
+can select a page layout that best fits her taste.
+Without user interaction, the
+system can pick the template with the highest priority. Of course,
+a user interface might provide means to alter priorities. Future
+versions might include sophisticated feature-vectors that
+identify templates that are good for large or small displays,
+landscape or portrait mode, etc \dots
+
+After the priority follows a glue node to specify the topskip glue
+and the dimension of the maximum page depth,
+an extended dimension to specify the page height and
+an extended dimension to specify the page width.
+
+Then follows the main part of a page template definition: the template.
+The template consists of a list of vertical material.
+To construct the page, this list will be placed
+into a vertical box and the glue will be set.
+But of course before doing so, the viewer will
+scan the list and replace all stream insertion points
+by the appropriate content streams.
+
+Let's call the vertical box obtained this way ``the page''.
+The page will fill the entire display area top to bottom and left to right.
+It defines not only the appearance of the main body of text
+but also the margins, the header, and the footer.
+Because the \.{vsize} and \.{hsize} variables of \TeX\ are used for
+the vertical and horizontal dimension of the main body of text---they
+do not include the margins---the page will usually be wider than \.{hsize}
+and taller than \.{vsize}. The dimensions of the page are part
+of the page template. The viewer, knowing the actual dimensions
+of the display area, can derive from them the actual values of \.{hsize}
+and \.{vsize}.
+
+Stream definitions are listed after the template.
+
+The page template with number 0 is always defined and has priority 0.
+It will display just the main content stream. It puts a small margin
+of $\.{hsize}/8 -4.5\hbox{pt}$ all around it.
+Given a letter size page, 8.5 inch wide, this formula yields a margin of 1 inch,
+matching \TeX's plain format. The margin will be positive as long as
+the page is wider than $1/2$ inch. For narrower pages, there will be no
+margin at all. In general, the \HINT\ viewer will never set {\tt hsize} larger
+than the width of the page and {\tt vsize} larger than its height.
+
+%8.5 in should give 1 inch margin 2/17
+%612pt should give 72pt margin
+%72pt = 612/8-4.5pt
+%This would give a positive margin starting at 36pt or 1/2 inch
+
+\goodbreak
+\vbox{\readcode\vskip -\baselineskip\putcode}
+
+@s PAGE symbol
+@s page_priority symbol
+@s page symbol
+@s stream_def_list symbol
+
+@<symbols@>=
+%token PAGE "page"
+@
+
+@<scanning rules@>=
+::@=page@> :< return PAGE; >:
+@
+
+@<parsing rules@>=
+page_priority: { HPUT8(1); }
+ | UNSIGNED { RNG("page priority",$1,0,255); HPUT8($1); };
+
+stream_def_list: | stream_def_list stream_def_node;
+
+page: string { hput_string($1);} page_priority glue_node dimension {@+HPUT32($5);@+}
+ xdimen_node xdimen_node
+ list stream_def_list ;
+@
+
+\goodbreak
+\vbox{\getcode\vskip -\baselineskip\writecode}
+@<get functions@>=
+void hget_page(void)
+{ char *n; uint8_t p; xdimen_t x; list_t l;
+ HGET_STRING(n);@+ hwrite_string(n);
+ p=HGET8; @+ if (p!=1) hwritef(" %d",p);
+ hget_glue_node();
+ hget_dimen(TAG(dimen_kind,b001));
+ hget_xdimen_node(&x); @+hwrite_xdimen_node(&x); /* page height */
+ hget_xdimen_node(&x); @+hwrite_xdimen_node(&x); /* page width */
+ hget_list(&l);@+ hwrite_list(&l);
+ while (hget_stream_def()) continue;
+}
+@
+
+\subsection{Page Ranges}\label{range}\index{page range}
+Not every template\index{template} is necessarily valid for the entire content
+section. A page range specifies a start position $a$ and an end
+position $b$ in the content section and the page template is valid if
+the start position $p$ of the page is within that range: $a\le p < b$.
+If paging backward this definition might cause problems because the
+start position of the page is known only after the page has been
+build. In this case, the viewer might choose a page template based on
+the position at the bottom of the page. If it turns out that this ``bottom template''
+is no longer valid when the page builder has found the start of the
+page, the viewer might display the page anyway with the bottom
+template, it might just display the page with the new ``top
+template'', or rerun the whole page building process using this time
+the ``top template''. Neither of these alternatives is guaranteed to
+produce a perfect result because changing the page template might
+change the amount of material that fits on the page. A good page
+template design should take this into account.
+
+The representation of page ranges differs significantly for the short
+format and the long format. The short format will include a list of page
+ranges in the definition section which consist of a page template number,
+a start position, and an end position. In the long format, the start
+and end position of a page
+range is marked with a page range node switching the availability of a
+page template on and off. Such a page range node must be a top level node.
+It is an error, to switch a page template
+off that was not switched on, or to switch a page template on that was
+already switched on. It is permissible to omit switching off a page
+template at the very end of the content section.
+
+While we parse a long format \HINT\ file, we store page ranges and generate
+the short format after reaching the end of the content section.
+While we parse a short format \HINT\ file,
+we check at the end of each top level node whether we should insert a
+page range node into the output.
+For the \.{shrink} program, it is best
+to store the start and end positions of all page ranges
+in an array sorted by the position\footnote*{For a \HINT\ viewer,
+a data structure which allows fast retrieval of all
+valid page templates for a given position is needed.}.
+To check the restrictions on the switching of page templates, we
+maintain for every page template an index into the range array
+which identifies the position where the template was switched on.
+A zero value instead of an index will identify templates that
+are currently invalid. When switching a range off again, we
+link the two array entries using this index. These links
+are useful when producing the range nodes in short format.
+
+A range node in short format contains the template number, the
+start position and the end position.
+A zero start position
+is not stored, the info bit |b100| indicates a nonzero start position.
+An end position equal to |HINT_NO_POS| is not stored,
+the info bit |b010| indicates a smaller end position.
+The info bit |b001| indicates that positions are stored using 2 byte
+otherwise 4 byte are used for the positions.
+
+@<hint types@>=
+typedef
+struct {@+uint8_t pg; @+uint32_t pos; @+ bool on; @+int link;@+} range_pos_t;
+@
+
+@<common variables@>=
+range_pos_t *range_pos;
+int next_range=1, max_range;
+int *page_on;
+@
+
+@<initialize definitions@>=
+ALLOCATE(page_on,max_ref[page_kind]+1,int);
+ALLOCATE(range_pos,2*(max_ref[range_kind]+1),range_pos_t);
+@
+
+@<hint macros@>=
+#define @[ALLOCATE(R,S,T)@] @/((R)=@[(T *)calloc((S),sizeof(T)),\
+ (((R)==NULL)?QUIT("Out of memory for " #R):0))
+#define @[REALLOCATE(R,S,T)@] @/((R)=@[(T *)realloc((R),(S)*sizeof(T)),\
+ (((R)==NULL)?QUIT("Out of memory for " #R):0))
+@
+
+\readcode
+@s RANGE symbol
+@<symbols@>=
+%token RANGE "range"
+@
+
+@<scanning rules@>=
+::@=range@> :< return RANGE; >:
+@
+@<parsing rules@>=
+
+content_node: START RANGE REFERENCE ON END @/{ REF(page_kind,$3);hput_range($3,true);}
+ | START RANGE REFERENCE OFF END @/{ REF(page_kind,$3);hput_range($3,false);};
+@
+
+
+\writecode
+@<write functions@>=
+void hwrite_range(void) /* called in |hwrite_end| */
+{ uint32_t p=hpos-hstart;
+ DBG(DBGRANGE,"Range check at pos 0x%x next at 0x%x\n",p,range_pos[next_range].pos);
+ while (next_range<max_range && range_pos[next_range].pos <= p)
+ { hwrite_start();
+ hwritef("range *%d ",range_pos[next_range].pg);
+ if (range_pos[next_range].on) hwritef("on"); else hwritef("off");
+ nesting--; @+hwritec('>'); /* avoid a recursive call to |hwrite_end| */
+ next_range++;
+ }
+}
+@
+
+\getcode
+@<get functions@>=
+void hget_range(info_t info, uint8_t pg)
+{ uint32_t from, to;
+ REF(page_kind,pg);
+ REF(range_kind,(next_range-1)/2);
+ if (info&b100) @+
+ { @+ if (info&b001) HGET32(from); @+else HGET16(from); @+}
+ else from=0;
+ if (info&b010) @+
+ { @+if (info&b001) HGET32(to); @+else HGET16(to); @+}
+ else to=HINT_NO_POS;
+ range_pos[next_range].pg=pg;
+ range_pos[next_range].on=true;
+ range_pos[next_range].pos=from;
+ DBG(DBGRANGE,"Range *%d from 0x%x\n",pg,from);
+ DBG(DBGRANGE,"Range *%d to 0x%x\n",pg,to);
+ next_range++;
+ if (to!=HINT_NO_POS) @/
+ { range_pos[next_range].pg=pg;
+ range_pos[next_range].on=false;
+ range_pos[next_range].pos=to;
+ next_range++;
+ }
+}
+@
+
+@<write functions@>=
+void hsort_ranges(void) /* simple insert sort by position */
+{ int i;
+ DBG(DBGRANGE,"Range sorting %d positions\n",next_range-1);
+ for(i=3; i<next_range; i++)@/
+ { int j = i-1;
+ if (range_pos[i].pos < range_pos[j].pos) @/
+ { range_pos_t t;
+ t= range_pos[i];
+ do {
+ range_pos[j+1] = range_pos[j];
+ j--;
+ } while (range_pos[i].pos < range_pos[j].pos);
+ range_pos[j+1] = t;
+ }
+ }
+ max_range=next_range; @+next_range=1; /* prepare for |hwrite_range| */
+}
+@
+
+\putcode
+@<put functions@>=
+void hput_range(uint8_t pg, bool on)
+{ if (((next_range-1)/2)>max_ref[range_kind])
+ QUIT("Page range %d > %d",(next_range-1)/2,max_ref[range_kind]);
+ if (on && page_on[pg]!=0)
+ QUIT(@["Template %d is switched on at 0x%x and " SIZE_F@],@|
+ pg, range_pos[page_on[pg]].pos, hpos-hstart);
+ else if (!on && page_on[pg]==0)
+ QUIT(@["Template %d is switched off at " SIZE_F " but was not on"@],@|
+ pg, hpos-hstart);
+ DBG(DBGRANGE,@["Range *%d %s at " SIZE_F "\n"@],pg,on?"on":"off",hpos-hstart);
+ range_pos[next_range].pg=pg;
+ range_pos[next_range].pos=hpos-hstart;
+ range_pos[next_range].on=on;
+ if (on) page_on[pg]=next_range;
+ else @/{ range_pos[next_range].link =page_on[pg];
+ range_pos[page_on[pg]].link=next_range;
+ page_on[pg]=0; }
+ next_range++;
+}
+
+void hput_range_defs(void)
+{ int i;
+ section_no=1;
+ hstart=dir[1].buffer;
+ hend=hstart+ dir[1].bsize;
+ hpos=hstart+dir[1].size;
+ for (i=1; i< next_range;i++)
+ if (range_pos[i].on)@/
+ { info_t info=b000;
+ uint32_t p=hpos++-hstart;
+ uint32_t from, to;
+ HPUT8(range_pos[i].pg);
+ from= range_pos[i].pos;
+ if (range_pos[i].link!=0) to = range_pos[range_pos[i].link].pos;
+ else to=HINT_NO_POS;
+ if (from!=0) @/
+ { info=info|b100;@+ if (from>0xFFFF) @+ info=info|b001;@+}
+ if (to!=HINT_NO_POS) @/
+ { info=info|b010;@+ if (to>0xFFFF) info=info|b001;@+ }
+ if (info & b100) @/
+ { @+if (info & b001) HPUT32(from); @+else HPUT16(from); @+}
+ if (info & b010) @/
+ { @+if (info & b001) HPUT32(to); @+else HPUT16(to); @+}
+ DBG(DBGRANGE,"Range *%d from 0x%x to 0x%x\n",@|range_pos[i].pg,from, to);
+ hput_tags(p,TAG(range_kind,info));
+ }
+ hput_definitions_end();
+ }
+@
+
+
+\section{File Structure}\hascode
+All \HINT\ files\index{file} start with a banner\index{banner} as
+described below. After that, they contain three mandatory
+sections\index{section}: the directory\index{directory section}
+section, the definition\index{definition section} section, and the
+content\index{content section} section. Usually, further
+optional\index{optional section} sections follow. In short format
+files, these contain auxiliary\index{auxiliary file} files
+(fonts\index{font}, images\index{image}, \dots) necessary for
+rendering the content. In long format files, the directory section
+will simply list the file names of the auxiliary files.
+
+
+
+\subsection{Banner}
+All \HINT\ files start with a banner\index{banner}. The banner contains only
+printable ASCII characters and spaces;
+its end is marked with a newline character\index{newline character}.
+The first four byte are the ``magic'' number by which you recognize a \HINT\
+file. It consists of the four ASCII codes `{\tt H}', `{\tt I}', `{\tt N}',
+and `{\tt T}' in the long format and `{\tt h}', `{\tt i}', `{\tt n}',
+and `{\tt t}' in the short format. Then follows a space, then
+the version number, a dot, the sub-version number, and another
+space. Both numbers are encoded as decimal ASCII strings. The
+remainder of the banner is simply ignored but may be used to contain
+other useful information about the file. The maximum size of the
+banner is 256 byte.
+@<hint macros@>=
+#define HINT_VERSION 1
+#define HINT_SUB_VERSION 3
+#define MAX_BANNER 256
+@
+
+\goodbreak
+To check the banner, we have the function |hcheck_banner|;
+it returns |true| if successful.
+
+@<common variables@>=
+char hbanner[MAX_BANNER+1];
+@
+
+@<function to check the banner@>=
+
+bool hcheck_banner(char *magic)
+{ int hbanner_size=0;
+ int v;
+ char *t;
+ t=hbanner;
+ if (strncmp(magic,hbanner,4)!=0)
+ { MESSAGE("This is not a %s file",magic); return false; }
+ else t+=4;
+ hbanner_size=(int)strnlen(hbanner,MAX_BANNER);
+ if(hbanner[hbanner_size-1]!='\n')
+ { MESSAGE("Banner exceeds maximum size=0x%x",MAX_BANNER); return false; }
+ if (*t!=' ')
+ { MESSAGE("Space expected in banner after %s",magic); return false; }
+ else t++;
+ v=strtol(t,&t,10);
+ if (v!=HINT_VERSION)
+ { MESSAGE("Wrong HINT version: got %d, expected %d",v,HINT_VERSION); return false; }
+ if (*t!='.')
+ { MESSAGE("Dot expected in banner after HINT version number"); return false; }
+ else t++;
+ v=strtol(t,&t,10);
+ if (v!=HINT_SUB_VERSION)
+ { MESSAGE("Wrong HINT subversion: got %d, expected %d",v,HINT_SUB_VERSION); return false; }
+ if (*t!=' ' && *t!='\n')
+ { MESSAGE("Space expected in banner after HINT subversion"); return false; }
+ LOG("%s file version %d.%d:%s",magic,HINT_VERSION, HINT_SUB_VERSION, t);
+ DBG(DBGDIR,"banner size=0x%x\n",hbanner_size);
+ return true;
+}
+@
+
+To read a short format file, we use the macro |HGET8|. It returns a single byte.
+We read the banner knowing that it ends with a newline character
+and is at most |MAX_BANNER| byte long. Because this is the first access to a yet unknown file,
+we are very careful and make sure we do not read past the end of the file.
+Checking the banner is a separate step.
+
+\getcode
+@<get file functions@>=
+void hget_banner(void)
+{ int i;
+ for (i=0;i<MAX_BANNER && hpos<hend;i++)@/
+ { hbanner[i]=HGET8;
+ if (hbanner[i]=='\n') break;
+ }
+ hbanner[++i]=0;
+}
+@
+
+To read a long format file, we use the function |fgetc|.
+\readcode
+@<read the banner@>=
+{ int i,c;
+ for (i=0;i<MAX_BANNER && (c=fgetc(hin))!=EOF;i++)@/
+ { hbanner[i]=c;
+ if (hbanner[i]=='\n') break;
+ }
+ hbanner[++i]=0;
+}
+@
+
+Writing the banner to a short format file is accomplished by calling
+|hput_banner| with the ``magic'' string |"hint"| as a first argument
+and a (short) comment as the second argument.
+\putcode
+@<function to write the banner@>=
+
+static size_t hput_banner(char *magic, char *s)
+{ return fprintf(hout,"%s %d.%d %s\n",magic,HINT_VERSION,HINT_SUB_VERSION,s);
+}
+@
+
+
+\writecode
+Writing the banner of a long format file is essentially the same as for a short
+format file calling |hput_banner| with |"HINT"| as a first argument.
+
+\subsection{Long Format Files}\gdef\subcodetitle{Banner}%
+
+After reading and checking the banner, reading a long format file is
+simply done by calling |yyparse|. The following rule gives the big picture:
+\readcode
+@s hint symbol
+@s content_section symbol
+
+@<parsing rules@>=
+hint: directory_section definition_section content_section ;
+@
+
+
+\subsection{Short Format Files}\gdef\subcodetitle{Primitives}%
+A short format\index{short format} file starts with the banner and continues
+with a list of sections. Each section has a maximum size
+of $2^{32}$ byte or 4GByte. This restriction ensures that positions\index{position}
+inside a section can be stored as 32 bit integers, a feature that
+we will need only for the so called ``content'' section, but it
+is also nice for implementers to know in advance what sizes to expect.
+The big picture is captured by the |put_hint| function:
+
+@<put functions@>=
+static size_t hput_root(void);
+static size_t hput_section(uint16_t n);
+static void hput_optional_sections(void);
+
+void hput_hint(char * str)
+{ size_t s;
+ DBG(DBGBASIC,"Writing hint output %s\n",str);
+ s=hput_banner("hint",str);
+ DBG(DBGDIR,@["Root entry at " SIZE_F "\n"@],s);
+ s+=hput_root();
+ DBG(DBGDIR,@["Directory section at " SIZE_F "\n"@],s);
+ s+=hput_section(0);
+ DBG(DBGDIR,@["Definition section at " SIZE_F "\n"@],s);
+ s+=hput_section(1);
+ DBG(DBGDIR,@["Content section at " SIZE_F "\n"@],s);
+ s+=hput_section(2);
+ DBG(DBGDIR,@["Auxiliary sections at " SIZE_F "\n"@],s);
+ hput_optional_sections();
+}
+@
+
+
+When we work on a section, we will have the entire section in
+memory and use three variables to access it: |hstart|
+points to the first byte of the section, |hend| points
+to the byte after the last byte of the section, and |hpos| points to the
+current position inside the section.\label{hpos}
+The auxiliary variable |hpos0| contains the |hpos| value of the
+last content node on nesting level zero.
+
+@<common variables@>=
+uint8_t *hpos=NULL, *hstart=NULL, *hend=NULL, *hpos0=NULL;
+@
+
+There are two sets of macros that read or write binary data at the current position
+and advance the stream position accordingly.\label{HPUT}\label{HGET}
+
+\getcode
+@<shared get macros@>=
+#define HGET_ERROR @/ QUIT(@["HGET overrun in section %d at " SIZE_F "\n"@],@|section_no,hpos-hstart)
+#define @[HEND@] @[((hpos<=hend)?0:(HGET_ERROR,0))@]
+
+#define @[HGET8@] ((hpos<hend)?*(hpos++):(HGET_ERROR,0))
+#define @[HGET16(X)@] ((X)=(hpos[0]<<8)+hpos[1],hpos+=2,HEND)
+#define @[HGET24(X)@] ((X)=(hpos[0]<<16)+(hpos[1]<<8)+hpos[2],hpos+=3,HEND)
+#define @[HGET32(X)@] ((X)=(hpos[0]<<24)+(hpos[1]<<16)+(hpos[2]<<8)+hpos[3],hpos+=4,HEND)
+#define @[HGETTAG(A)@] @[A=HGET8,DBGTAG(A,hpos-1)@]
+@
+
+\putcode
+@<put functions@>=
+void hput_error(void)
+{@+if (hpos<hend) return;
+ QUIT(@["HPUT overrun section %d pos=" SIZE_F "\n"@],@|section_no,hpos-hstart);
+}
+@
+
+@<put macros@>=
+extern void hput_error(void);
+#define @[HPUT8(X)@] (hput_error(),*(hpos++)=(X))
+#define @[HPUT16(X)@] (HPUT8(((X)>>8)&0xFF),HPUT8((X)&0xFF))
+#define @[HPUT24(X)@] (HPUT8(((X)>>16)&0xFF),HPUT8(((X)>>8)&0xFF),HPUT8((X)&0xFF))
+#define @[HPUT32(X)@] (HPUT8(((X)>>24)&0xFF),HPUT8(((X)>>16)&0xFF),HPUT8(((X)>>8)&0xFF),HPUT8((X)&0xFF))
+@
+
+The above macros test for buffer overruns\index{buffer overrun};
+allocating sufficient buffer space is done separately.
+
+Before writing a node, we will insert a test and increase the buffer if necessary.
+@<put macros@>=
+void hput_increase_buffer(uint32_t n);
+#define @[HPUTX(N)@] @[(((hend-hpos) < (N))? hput_increase_buffer(N):(void)0)@]
+#define HPUTNODE @[HPUTX(MAX_TAG_DISTANCE)@]
+#define @[HPUTTAG(K,I)@] @|@[(HPUTNODE,@+DBGTAG(TAG(K,I),hpos),@+HPUT8(TAG(K,I)))@]
+@
+
+Fortunately the only data types that have an unbounded size are
+strings\index{string} and texts\index{text}.
+For these we insert specific tests. For all other cases a relatively
+small upper bound on the maximum distance between two tags can be determined.
+Currently the maximum distance between tags is 26 byte as can be determined
+from the |hnode_size| array described in appendix~\secref{fastforward}.
+The definition below uses a slightly larger value leaving some room
+for future changes in the design of the short file format.
+
+@<hint macros@>=
+#define MAX_TAG_DISTANCE 32
+@
+
+\subsection{Mapping a Short Format File to Memory}
+In the following, we implement two alternatives to map a file into memory.
+The first implementation, opens the file, gets its size, allocates memory,
+and reads the file. The second implementation uses a call to |mmap|.
+
+Since modern computers with 64bit hardware have a huge address space,
+using |mmap| to map the entire file into virtual memory is the most efficient way
+to access a large file. ``Mapping'' is not the same as ``reading'' and it is
+not the same as allocating precious memory, all that is done by the
+operating system when needed. Mapping just reserves addresses.
+There is one disadvantage of mapping: it typically locks the underlying file
+and will not allow a separate process to modify it. This prevents using
+this method for previewing a \HINT\ file while editing and recompiling it.
+In this case, the first implementation, which has a copy of the file in memory,
+is the better choice. To select the second implementation, define the macro |USE_MMAP|.
+
+The following functions map and unmap a short format input
+file setting |hin_addr| to its address and |hin_size| to its size.
+The value |hin_addr==NULL| indicates, that no file is open.
+The variable |hin_time| is set to the time when the file was last modified.
+It can be used to detect modifications of the file and reload it.\label{map}
+
+@<common variables@>=
+char *hin_name=NULL;
+uint64_t hin_size=0;
+uint8_t *hin_addr=NULL;
+uint64_t hin_time=0;
+@
+
+@<map functions@>=
+#ifndef USE_MMAP
+void hget_unmap(void)
+{@+ if (hin_addr!=NULL) free(hin_addr);
+ hin_addr=NULL;
+ hin_size=0;
+}
+bool hget_map(void)
+{ FILE *f;
+ struct stat st;
+ size_t s,t,u;
+ f= fopen(hin_name,"rb");
+ if (f==NULL)@/
+ { MESSAGE("Unable to open file: %s", hin_name);@+ return false;@+ }
+ if (stat(hin_name,&st)<0)
+ { MESSAGE("Unable to obtain file size: %s", hin_name);
+ fclose(f);
+ return false;
+ }
+ if (st.st_size==0)
+ { MESSAGE("File %s is empty", hin_name);
+ fclose(f);
+ return false;
+ }
+ if (hin_addr!=NULL) hget_unmap();
+ hin_addr=malloc(st.st_size);
+ if (hin_addr==NULL)
+ { MESSAGE("Unable to allocate %ld byte for File %s", st.st_size,hin_name);
+ fclose(f);
+ return 0;
+ }
+ t=0;@+
+ u=st.st_size;
+ do{
+ s=fread(hin_addr+t,1,u,f);
+ if (s<=0)
+ { MESSAGE("Unable to read file %s",hin_name);
+ fclose(f);
+ free(hin_addr);
+ hin_addr=NULL;
+ return false;
+ }
+ t=t+s;@+
+ u=u-s;
+ } while (u>0);
+ hin_size=st.st_size;
+ hin_time=st.st_mtime;
+ return true;
+}
+
+#else
+
+#include <sys/mman.h>
+
+void hget_unmap(void)
+{@+ munmap(hin_addr,hin_size);
+ hin_addr=NULL;
+ hin_size=0;
+}
+
+bool hget_map(void)
+{ struct stat st;
+ int fd;
+ fd = open(hin_name, O_RDONLY, 0);
+ if (fd<0)@/
+ { MESSAGE("Unable to open file %s", hin_name);@+ return false;@+ }
+ if (fstat(fd, &st)<0)
+ { MESSAGE("Unable to get file size");
+ close(fd);
+ return false;
+ }
+ if (st.st_size==0)
+ { MESSAGE("File %s is empty",hin_name);
+ close(fd);
+ return false;
+ }
+ if (hin_addr!=NULL) hget_unmap();
+ hin_size=st.st_size;
+ hin_time=st.st_mtime;
+ hin_addr= mmap(NULL,hin_size,PROT_READ,MAP_PRIVATE,fd, 0);
+ if (hin_addr==MAP_FAILED)
+ { close(fd);
+ hin_addr=NULL;hin_size=0;
+ MESSAGE("Unable to map file into memory");
+ return 0;
+ }
+ close(fd);
+ return hin_size;
+}
+#endif
+
+@
+\subsection{Compression}
+The short file format offers the possibility to store sections in
+compressed\index{compression} form. We use the {\tt zlib}\index{zlib+{\tt zlib}} compression library\cite{zlib}\cite{RFC1950}
+to deflate\index{deflate} and inflate\index{inflate} individual sections. When one of the following
+functions is called, we can get the section buffer, the buffer size
+and the size actually used from the directory entry. If a section
+needs to be inflated, its size after decompression is found in the
+|xsize| field; if a section needs to be deflated, its size after
+compression will be known after deflating it.
+
+@s z_stream int
+
+@<get file functions@>=
+
+static void hdecompress(uint16_t n)
+{ z_stream z; /* decompression stream */
+ uint8_t *buffer;
+ int i;
+
+ DBG(DBGCOMPRESS,"Decompressing section %d from 0x%x to 0x%x byte\n",@|n, dir[n].size, dir[n].xsize);
+ z.zalloc = (alloc_func)0;@+
+ z.zfree = (free_func)0;@+
+ z.opaque = (voidpf)0;
+ z.next_in = hstart;
+ z.avail_in = hend-hstart;
+ if (inflateInit(&z)!=Z_OK)
+ QUIT("Unable to initialize decompression: %s",z.msg);
+ ALLOCATE(buffer,dir[n].xsize+MAX_TAG_DISTANCE,uint8_t);
+ DBG(DBGBUFFER,"Allocating output buffer size=0x%x, margin=0x%x\n",dir[n].xsize,MAX_TAG_DISTANCE);
+ z.next_out = buffer;
+ z.avail_out =dir[n].xsize+MAX_TAG_DISTANCE;
+ i= inflate(&z, Z_FINISH);
+ DBG(DBGCOMPRESS,"in: avail/total=0x%x/0x%lx "@|"out: avail/total=0x%x/0x%lx, return %d;\n",@|
+ z.avail_in,z.total_in, z.avail_out, z.total_out,i);
+ if (i!=Z_STREAM_END)
+ QUIT("Unable to complete decompression: %s",z.msg);
+ if (z.avail_in != 0)
+ QUIT("Decompression missed input data");
+ if (z.total_out != dir[n].xsize)
+ QUIT("Decompression output size mismatch 0x%lx != 0x%x",z.total_out, dir[n].xsize );
+ if (inflateEnd(&z)!=Z_OK)
+ QUIT("Unable to finalize decompression: %s",z.msg);
+ dir[n].buffer=buffer;
+ dir[n].bsize=dir[n].xsize;
+ hpos0=hpos=hstart=buffer;
+ hend=hstart+dir[n].xsize;
+}
+@
+
+
+@<put functions@>=
+static void hcompress(uint16_t n)
+{ z_stream z; /* compression stream */
+ uint8_t *buffer;
+ int i;
+ if (dir[n].size==0) { dir[n].xsize=0;@+ return; @+}
+ DBG(DBGCOMPRESS,"Compressing section %d of size 0x%x\n",n, dir[n].size);
+ z.zalloc = (alloc_func)0;@+
+ z.zfree = (free_func)0;@+
+ z.opaque = (voidpf)0;
+ if (deflateInit(&z,Z_DEFAULT_COMPRESSION)!=Z_OK)
+ QUIT("Unable to initialize compression: %s",z.msg);
+ ALLOCATE(buffer,dir[n].size+MAX_TAG_DISTANCE,uint8_t);
+ z.next_out = buffer;
+ z.avail_out = dir[n].size+MAX_TAG_DISTANCE;
+ z.next_in = dir[n].buffer;
+ z.avail_in = dir[n].size;
+ i=deflate(&z, Z_FINISH);
+ DBG(DBGCOMPRESS,"deflate in: avail/total=0x%x/0x%lx out: avail/total=0x%x/0x%lx, return %d;\n",@|
+ z.avail_in,z.total_in, z.avail_out, z.total_out,i);
+ if (z.avail_in != 0)
+ QUIT("Compression missed input data");
+ if (i!=Z_STREAM_END)
+ QUIT("Compression incomplete: %s",z.msg);
+ if (deflateEnd(&z)!=Z_OK)
+ QUIT("Unable to finalize compression: %s",z.msg);
+ DBG(DBGCOMPRESS,"Compressed 0x%lx byte to 0x%lx byte\n",@|z.total_in,z.total_out);
+ free(dir[n].buffer);
+ dir[n].buffer=buffer;
+ dir[n].bsize=dir[n].size+MAX_TAG_DISTANCE;
+ dir[n].xsize=dir[n].size;
+ dir[n].size=z.total_out;
+}
+@
+
+
+
+\subsection{Reading Short Format Sections}
+\gdef\subcodetitle{Sections}%
+
+After mapping the file at address |hin_addr| access to sections of the
+file is provided by decompressing them if necessary and
+setting the three pointers |hpos|, |hstart|, and
+|hend|.
+
+To read sections of a short format input file, we use the function |hget_section|.
+
+\getcode
+%\codesection{\getsymbol}\getindex{1}{3}{Files}
+
+@<get file functions@>=
+void hget_section(uint16_t n)
+{ DBG(DBGDIR,"Reading section %d\n",n);
+ RNG("Section number",n,0,max_section_no);
+ if (dir[n].buffer!=NULL && dir[n].xsize>0)
+ { hpos0=hpos=hstart=dir[n].buffer;
+ hend=hstart+dir[n].xsize;
+ }
+ else
+ { hpos0=hpos=hstart=hin_addr+dir[n].pos;
+ hend=hstart+dir[n].size;
+ if (dir[n].xsize>0) hdecompress(n);
+ }
+}
+@
+\subsection{Writing Short Format Sections}
+\gdef\subcodetitle{Sections}%
+
+To write a short format file, we allocate for each of the first three sections a
+suitable buffer\index{buffer}, then fill these buffers, and finally write them
+out in sequential order.
+
+@<put functions@>=
+#define BUFFER_SIZE 0x400
+void new_output_buffers(void)
+{ dir[0].bsize=dir[1].bsize=dir[2].bsize=BUFFER_SIZE;
+ DBG(DBGBUFFER,"Allocating output buffer size=0x%x, margin=0x%x\n",BUFFER_SIZE,MAX_TAG_DISTANCE);
+ ALLOCATE(dir[0].buffer,dir[0].bsize+MAX_TAG_DISTANCE,uint8_t);
+ ALLOCATE(dir[1].buffer,dir[1].bsize+MAX_TAG_DISTANCE,uint8_t);
+ ALLOCATE(dir[2].buffer,dir[2].bsize+MAX_TAG_DISTANCE,uint8_t);
+}
+
+void hput_increase_buffer(uint32_t n)
+{ size_t bsize;
+ uint32_t pos, pos0;
+ const double buffer_factor=1.4142136; /* $\sqrt 2$ */
+ pos=hpos-hstart; pos0=hpos0-hstart;
+ bsize=dir[section_no].bsize*buffer_factor+0.5;
+ if (bsize<pos+n) bsize=pos+n;
+ if (bsize>=HINT_NO_POS) bsize=HINT_NO_POS;
+ if (bsize<pos+n) QUIT(@["Unable to increase buffer size " SIZE_F " by 0x%x byte"@],@|hpos-hstart,n);
+ DBG(DBGBUFFER,@["Reallocating output buffer "@|" for section %d from 0x%x to " SIZE_F " byte\n"@],
+ section_no,dir[section_no].bsize,bsize);
+ REALLOCATE(dir[section_no].buffer,bsize,uint8_t);
+ dir[section_no].bsize=(uint32_t)bsize;
+ hstart=dir[section_no].buffer;
+ hend=hstart+bsize;
+ hpos0=hstart+pos0; hpos=hstart+pos;
+}
+
+static size_t hput_data(uint16_t n, uint8_t *buffer, uint32_t size)
+{ size_t s;
+ s=fwrite(buffer,1,size,hout);
+ if (s!=size)
+ QUIT(@["short write " SIZE_F " < %d in section %d"@],s,size,n);
+ return s;
+}
+
+static size_t hput_section(uint16_t n)
+{ return hput_data(n, dir[n].buffer,dir[n].size);
+}
+@
+
+
+
+
+\section{Directory Section}
+A \HINT\ file is subdivided in sections and
+each section can be identified by its section number.
+The first three sections, numbered 0, 1, and 2, are mandatory:
+directory\index{directory section} section, definition section, and content section.
+The directory section, which we explain now, lists all sections
+that make up a \HINT\ file.
+
+A document will often contain not only plain text but also other media
+for example illustrations. Illustrations are produced with specialized
+tools and stored in specialized files. Because a \HINT\ file in short format
+should be self contained, these special files are embedded in the \HINT\ file
+as optional sections.
+Because a \HINT\ file in long format should be readable, these special files
+are written to disk and only the file names are retained in the directory.
+Writing special files to disk has also the advantage that you can modify
+them individually before embedding them in a short format file.
+
+
+\subsection{Directories in Long Format}\gdef\subcodetitle{Directory Section}%
+The directory\index{directory section} section of a long format \HINT\ file starts
+with the ``\.{directory}'' keyword; then follows the maximum section number used and
+a list of directory entries, one for each optional section numbered 3 and above.
+Each entry consists of the keyword ``\.{section}'' followed by the
+section number, followed by the file name.
+The section numbers must be unique and fit into 16 bit.
+The directory entries must be ordered with strictly increasing section numbers.
+Keeping section numbers consecutive is recommended because it reduces the
+memory footprint if directories are stored as arrays indexed by the section
+number as we will do below.
+
+\readcode
+@s directory_section symbol
+@s entry_list symbol
+@s entry symbol
+@s DIRECTORY symbol
+@s SECTION symbol
+
+@<symbols@>=
+%token DIRECTORY "directory"
+%token SECTION "entry"
+@
+
+@<scanning rules@>=
+::@=directory@> :< return DIRECTORY; >:
+::@=section@> :< return SECTION; >:
+@
+
+@<parsing rules@>=
+directory_section: START DIRECTORY UNSIGNED @|{new_directory($3+1); new_output_buffers();} entry_list END ;
+entry_list: @,@+ | entry_list entry;
+entry: START SECTION UNSIGNED string END @/
+ { RNG("Section number",$3,3,max_section_no); hset_entry(&(dir[$3]), $3,0,0,$4);};
+@
+
+
+
+We use a dynamically allocated array
+of directory entries to store the directory.
+
+@<directory entry type@>=
+typedef struct {
+uint64_t pos;
+uint32_t size, xsize;
+uint16_t section_no;
+char *file_name;
+uint8_t *buffer;
+uint32_t bsize;
+} entry_t;
+@
+
+
+The function |new_directory| allocates the directory.
+
+@<directory functions@>=
+entry_t *dir=NULL;
+uint16_t section_no, max_section_no;
+void new_directory(uint32_t entries)
+{ DBG(DBGDIR,"Creating directory with %d entries\n", entries);
+ RNG("Directory entries",entries,3,0x10000);
+ max_section_no=entries-1;@+
+ ALLOCATE(dir,entries,entry_t);
+ dir[0].section_no=0; @+ dir[1].section_no=1; @+ dir[2].section_no=2;
+}
+@
+
+The function |hset_entry| fills in the appropriate entry.
+@<directory functions@>=
+void hset_entry(entry_t *e, uint16_t i, uint32_t size, uint32_t xsize, @|char *file_name)
+{ e->section_no=i;
+ e->size=size; @+e->xsize=xsize;
+ if (file_name==NULL || *file_name==0)
+ e->file_name=NULL;
+ else
+ e->file_name=strdup(file_name);
+ DBG(DBGDIR,"Creating entry %d: \"%s\" size=0x%x xsize=0x%x\n",@|i,file_name,size,xsize);
+}
+@
+
+
+Writing the auxiliary files depends on the {\tt -f} and the {\tt -g}
+option.
+
+@<without {\tt -f} skip writing an existing file@>=
+ if ( !option_force && access(file_name,F_OK)==0)
+ { MESSAGE("File '%s' exists.\n"@| "To rewrite the file use the -f option.\n",
+ file_name);
+ continue;
+ }
+@
+
+The above code uses the |access| function, and we need to make sure it is defined:
+@<make sure |access| is defined@>=
+#ifdef WIN32
+#include <io.h>
+#define @[access(N,M)@] @[_access(N, M )@]
+#define F_OK 0
+#else
+#include <unistd.h>
+#endif
+@
+
+With the {\tt -g} option, filenames are considered global, and files
+are written to the filesystem possibly overwriting the existing files.
+For example a font embedded in a \HINT\ file might replace a font of
+the same name in some operating systems font folder.
+If the \HINT\ file is {\tt shrink}ed on one system and
+{\tt stretch}ed on another system, this is usually not the desired behavior.
+Without the {\tt -g} option,\label{absrel} the files will be written in two local directories.
+The names of these directories are derived from the output file name,
+replacing the extension ``{\tt .HINT}'' with ``{\tt .abs}'' if the original
+filename contained an absolute path, and replacing it with ``{\tt .rel}''
+if the original filename contained a relative path. Inside these directories,
+the path as given in the filename is retained.
+When {\tt shrink}ing a \HINT\ file without the {\tt -g} option,
+the original filenames can be reconstructed.
+
+@<without {\tt -g} compute a local |file_name|@>=
+if (!option_global)
+{ int path_length=(int)strlen(file_name);
+ @<determine whether |file_name| is absolute or relative@>@;
+ if (file_name_length<stem_length+ext_length+path_length)
+ { file_name_length=stem_length+ext_length+path_length;
+ REALLOCATE(stem_name, file_name_length+1,char);
+ }
+ strcpy(stem_name+stem_length,aux_ext[name_type]);
+ strcpy(stem_name+stem_length+ext_length,file_name);
+ DBG(DBGDIR,"Replacing auxiliary file name:\n\t%s\n->\t%s\n",file_name,stem_name);
+ file_name=stem_name;
+}
+@
+
+@<determine whether |file_name| is absolute or relative@>=
+ enum {absolute=0, relative=1} name_type;
+ char *aux_ext[2]={".abs/",".rel/"};
+ int ext_length=5;
+ if (file_name[0]=='/')
+ { name_type=absolute;
+ file_name++; path_length--;
+ }
+ else if (path_length>3 && isalpha(file_name[0]) &&
+ file_name[1]==':' && file_name[2]=='/')
+ { name_type=absolute;
+ file_name[1]='_';
+ }
+ else
+ name_type=relative;
+@
+It remains to create the directories along the path we might have constructed.
+@<make sure the path in |file_name| exists@>=
+{ char *path_end;
+ path_end=file_name+1;
+ while (*path_end!=0)
+ { if(*path_end=='/')
+ { struct stat s;
+ *path_end=0;
+ if (stat(file_name,&s)==-1)
+ {
+#ifdef WIN32
+ if (mkdir(file_name)!=0)
+#else
+ @t\2\kern-1em@>if (mkdir(file_name,0777)!=0)
+#endif
+ QUIT("Unable to create directory %s",file_name);
+ DBG(DBGDIR,"Creating directory %s\n",file_name);
+ } else if (!(S_IFDIR&s.st_mode))
+ QUIT("Unable to create directory %s, file exists",file_name);
+ *path_end='/';
+ }
+ path_end++;
+ }
+}
+
+
+@
+
+\writecode
+@<write functions@>=
+@<make sure |access| is defined@>@;
+extern char *stem_name;
+extern int stem_length;
+
+void hget_section(uint16_t n);
+void hwrite_aux_files(void)
+{ int i;
+ DBG(DBGBASIC|DBGDIR,"Writing %d aux files\n",max_section_no-2);
+ for (i=3;i<=max_section_no;i++)
+ { FILE *f;
+ char * file_name=dir[i].file_name;
+ int file_name_length=0;
+
+
+ @<without {\tt -g} compute a local |file_name|@>@;
+ @<without {\tt -f} skip writing an existing file@>@;
+ @<make sure the path in |file_name| exists@>@;
+
+ f=fopen(file_name,"wb");
+ if (f==NULL)
+ QUIT("Unable to open file '%s' for writing",file_name);
+ else
+ { size_t s;
+ hget_section(i);
+ DBG(DBGDIR,"Writing file %s\n",file_name);
+ s=fwrite(hstart,1,dir[i].size,f);
+ if (s!=dir[i].size) QUIT("writing file %s",file_name);
+ fclose(f);
+ }
+ }
+}
+@
+
+We write the directory, and the directory entries
+in long format using the following functions.
+@<write functions@>=
+static void hwrite_entry(int i)
+{ hwrite_start();
+ hwritef("section %u",dir[i].section_no);@+ hwrite_string(dir[i].file_name);
+ hwrite_end();
+}
+
+void hwrite_directory(void)
+{ int i;
+ if (dir==NULL) QUIT("Directory not allocated");
+ section_no=0;
+ hwritef("<directory %u", max_section_no);@/
+ for (i=3;i<=max_section_no;i++)
+ hwrite_entry(i);
+ hwritef("\n>\n");
+}
+@
+
+\subsection{Directories in Short Format}
+The directory\index{directory section} section of a short format file contains entries
+for all sections including the directory section itself. After reading the
+directory section, enough information---position and size---is available to
+access any section directly. As usual, a directory entry starts and ends with
+a tag byte. The kind part of an entry's tag is not used; it is always zero.
+The value $s$ of the two least significant bits of the info part indicate
+that sizes are stored using $s+1$ byte. The most significant bit of the info
+part is 1 if the section is stored in compressed\index{compression} form. In this case the size
+of the section is followed by the size of the section after decompressing it.
+After the tag byte follows the section number. In the short format file,
+section numbers must be strictly increasing and consecutive. This is redundant but helps
+with checking. Then follows the size---or the sizes---of the section. After the size
+follows the file name terminated by a zero byte. The file name might be an empty
+string in which case there is just the zero byte. After the zero byte follows
+a copy of the tag byte.
+
+Here is the macro and function to read a directory\index{directory entry} entry:
+\gdef\subcodetitle{Directory Entries}%
+\getcode
+
+@<shared get macros@>=
+#define @[HGET_SIZE(I)@] \
+ if ((I)&b100) { \
+ if (((I)&b011)==0) s=HGET8,xs=HGET8; \
+ else if (((I)&b011)==1) HGET16(s),HGET16(xs); \
+ else if (((I)&b011)==2) HGET24(s),HGET24(xs); \
+ else if (((I)&b011)==3) HGET32(s),HGET32(xs); \
+ } \
+ else { \
+ if (((I)&b011)==0) s=HGET8; \
+ else if (((I)&b011)==1) HGET16(s); \
+ else if (((I)&b011)==2) HGET24(s); \
+ else if (((I)&b011)==3) HGET32(s); \
+ }
+
+#define @[HGET_ENTRY(I,E)@] \
+{ uint16_t i; \
+ uint32_t s=0,xs=0; \
+ char *file_name; \
+ HGET16(i); @+HGET_SIZE(I); @+HGET_STRING(file_name); @/\
+ hset_entry(&(E),i,s,xs,file_name); \
+}
+@
+
+@<get file functions@>=
+void hget_entry(entry_t *e)
+{ @<read the start byte |a|@>@;
+ DBG(DBGDIR,"Reading directory entry\n");
+ switch(a)
+ { case TAG(0,b000+0): HGET_ENTRY(b000+0,*e);@+ break;
+ case TAG(0,b000+1): HGET_ENTRY(b000+1,*e);@+ break;
+ case TAG(0,b000+2): HGET_ENTRY(b000+2,*e);@+ break;
+ case TAG(0,b000+3): HGET_ENTRY(b000+3,*e);@+ break;
+ case TAG(0,b100+0): HGET_ENTRY(b100+0,*e);@+ break;
+ case TAG(0,b100+1): HGET_ENTRY(b100+1,*e);@+ break;
+ case TAG(0,b100+2): HGET_ENTRY(b100+2,*e);@+ break;
+ case TAG(0,b100+3): HGET_ENTRY(b100+3,*e);@+ break;
+ default: TAGERR(a); @+ break;
+ }
+ @<read and check the end byte |z|@>@;
+}
+@
+
+Because the first entry in the directory section describes the
+directory section itself, we can not check its info bits in advance to determine
+whether it is compressed or not. Therefore the directory section
+starts with a root entry, which is always uncompressed. It describes
+the remainder of the directory which follows.
+There are two differences between the root entry and a normal entry:
+it starts with the maximum section number instead of the section number zero,
+and we set its position to the position of the
+entry for section 1 (which might already be compressed).
+The name of the directory section must be the empty string.
+\gdef\subcodetitle{Directory Section}%
+\getcode
+@<get file functions@>=
+static void hget_root(entry_t *root)
+{ DBG(DBGDIR,"Root entry at " SIZE_F "\n",hpos-hstart);
+ hget_entry(root);
+ root->pos=hpos-hstart;
+ max_section_no=root->section_no;
+ root->section_no=0;
+ if (max_section_no<2) QUIT("Sections 0, 1, and 2 are mandatory");
+}
+
+void hget_directory(void)
+{ int i;
+ entry_t root={0};
+ hget_root(&root);
+ DBG(DBGDIR,"Directory\n");
+ new_directory(max_section_no+1);
+ dir[0]=root;
+ DBG(DBGDIR,"Directory entry 1 at 0x%"PRIx64"\n",dir[0].pos);
+ hget_section(0);
+ for (i=1;i<=max_section_no;i++)@/
+ { hget_entry(&(dir[i]));@+
+ dir[i].pos=dir[i-1].pos +dir[i-1].size;@+
+ DBG(DBGDIR,"Section %d at 0x%"PRIx64"\n",i,dir[i].pos);
+ }
+}
+
+void hclear_dir(void)
+{ int i;
+ if (dir==NULL) return;
+ for (i=0;i<3;i++) /* currently the only compressed sections */
+ if (dir[i].xsize>0 && dir[i].buffer!=NULL) free(dir[i].buffer);
+ free(dir); dir=NULL;
+}
+
+@
+
+When the \.{shrink} program writes the directory section in the short format,
+it needs to know the sizes of all the sections---including the optional sections.
+These sizes are not provided in the long format because it is safer and more
+convenient to let the machine figure out the file sizes\index{file size}.
+
+@<set the file sizes for optional sections@>=
+{ int i;
+ for (i=3;i<=max_section_no;i++)
+ { struct stat s;
+ char *file_name=dir[i].file_name;
+ int file_name_length=0;
+ @<without {\tt -g} compute a local |file_name|@>@;
+ if (stat(file_name,&s)!=0)
+ QUIT("Unable to obtain file size for '%s'",dir[i].file_name);
+ dir[i].size=s.st_size;
+ dir[i].xsize=0;
+ }
+}
+@
+
+The computation of the sizes of the mandatory sections will be
+explained later.
+Armed with these preparations, we can put the directory into the \HINT\ file.
+
+\gdef\subcodetitle{Directory Section}%
+\putcode
+@<put functions@>=
+static void hput_entry(entry_t *e)
+{ uint8_t b;
+ if (e->size<0x100 && e->xsize<0x100) b=0;
+ else if (e->size<0x10000 &&e->xsize<0x10000) b=1;
+ else if (e->size<0x1000000 &&e->xsize<0x1000000) b=2;
+ else b=3;
+ if (e->xsize!=0) b =b|b100;
+ DBG(DBGTAGS,"Directory entry no=%d size=0x%x xsize=0x%x\n",e->section_no, e->size, e->xsize);
+ HPUTTAG(0,b);@/
+ HPUT16(e->section_no);
+ switch (b) {
+ case 0: HPUT8(e->size);@+break;
+ case 1: HPUT16(e->size);@+break;
+ case 2: HPUT24(e->size);@+break;
+ case 3: HPUT32(e->size);@+break;
+ case b100|0: HPUT8(e->size);@+HPUT8(e->xsize);@+break;
+ case b100|1: HPUT16(e->size);@+HPUT16(e->xsize);@+break;
+ case b100|2: HPUT24(e->size);@+HPUT24(e->xsize);@+break;
+ case b100|3: HPUT32(e->size);@+HPUT32(e->xsize);@+break;
+ default: QUIT("Can't happen");@+ break;
+ }
+ hput_string(e->file_name);@/
+ DBGTAG(TAG(0,b),hpos);@+HPUT8(TAG(0,b));
+}
+
+static void hput_directory_start(void)
+{ DBG(DBGDIR,"Directory Section\n");
+ section_no=0;
+ hpos=hstart=dir[0].buffer;
+ hend=hstart+dir[0].bsize;
+}
+static void hput_directory_end(void)
+{ dir[0].size=hpos-hstart;
+ DBG(DBGDIR,"End Directory Section size=0x%x\n",dir[0].size);
+}
+
+static size_t hput_root(void)
+{ uint8_t buffer[MAX_TAG_DISTANCE];
+ size_t s;
+ hpos=hstart=buffer;
+ hend=hstart+MAX_TAG_DISTANCE;
+ dir[0].section_no=max_section_no;
+ hput_entry(&dir[0]);
+ s=hput_data(0, hstart,hpos-hstart);
+ DBG(DBGDIR,@["Writing root size=" SIZE_F "\n"@],s);
+ return s;
+}
+
+extern int option_compress;
+void hput_directory(void)
+{ int i;
+ @<set the file sizes for optional sections@>@;
+ if (option_compress) { hcompress(1); @+hcompress(2); @+}
+ hput_directory_start();
+ for (i=1; i<=max_section_no; i++)
+ { dir[i].pos=dir[i-1].pos+dir[i-1].size;
+ DBG(DBGDIR,"writing entry %u at 0x%" PRIx64 "\n",i, dir[i].pos);
+ hput_entry(&dir[i]);
+ }
+ hput_directory_end();
+ if (option_compress) hcompress(0);
+}
+
+@
+
+
+To conclude this section, here is the function that adds the files that
+are described in the directory entries 3 and above to a \HINT\ file in short format.
+Where these files are found depends on the {\tt -g} option.
+With that option given, the file names of the directory entries are used unchanged.
+Without that option, the files are found in the {|hin_name|\tt .abs} and {|hin_name|\tt .rel}
+directories, as described in section~\secref{absrel}.
+
+\gdef\subcodetitle{Optional Sections}%
+\putcode
+@<put functions@>=
+static void hput_optional_sections(void)
+{ int i;
+ DBG(DBGDIR,"Optional Sections\n");
+ for (i=3; i<=max_section_no; i++)@/
+ { FILE *f;
+ size_t fsize;
+ char *file_name=dir[i].file_name;
+ int file_name_length=0;
+ DBG(DBGDIR,"file %d: %s\n",dir[i].section_no,file_name);
+ if (dir[i].xsize!=0) @/
+ DBG(DBGDIR,"Compressing of auxiliary files currently not supported");
+ @<without {\tt -g} compute a local |file_name|@>@;
+ f=fopen(file_name,"rb");
+ if (f==NULL) QUIT("Unable to read section %d, file %s",
+ dir[i].section_no,file_name);
+ fsize=0;
+ while (!feof(f))@/
+ { size_t s,t;
+ char buffer[1<<13]; /* 8kByte */
+ s=fread(buffer,1,1<<13,f);@/
+ t=fwrite(buffer,1,s,hout);
+ if (s!=t) QUIT("writing file %s",file_name);
+ fsize=fsize+t;
+ }
+ fclose(f);
+ if (fsize!=dir[i].size)
+ QUIT(@["File size " SIZE_F " does not match directory size %u"@],@|fsize,dir[i].size);
+ }
+}
+@
+
+
+
+\section{Definition Section}\index{definition section}
+\label{defsection}
+In a typical \HINT\ file, there are many things that are used over and over again.
+For example the interword glue of a specific font or the indentation of
+the first line of a paragraph. The definition section contains this information so that
+it can be referenced in the content section by a simple reference number.
+In addition there are a few parameters that guide the routines of \TeX.
+An example is the ``above display skip'', which controls the amount of white space
+inserted above a displayed equation, or the ``hyphen penalty'' that tells \TeX\
+the ``\ae sthetic cost'' of ending a line with a hyphenated word. These parameters
+also get their values in the definition section as explained in section~\secref{defaults}.
+
+
+The most simple way to store these definitions is to store them in an array indexed by the
+reference numbers.
+To simplify the dynamic allocation of these arrays, the list of definitions
+will always start with the list of maximum\index{maximum values} values: a list that contains
+for each node type the maximum reference number used.
+
+In the long format, the definition section starts with the keyword \.{definitions},
+followed by the list of maximum values,
+followed by the definitions proper.
+
+When writing the short format, we start by positioning the output stream at the beginning of
+the definition buffer and we end with recording the size of the definition section
+in the directory.
+
+\readcode
+@s definition_section symbol
+@s definition_list symbol
+@s definition symbol
+@s DEFINITIONS symbol
+@<symbols@>=
+%token DEFINITIONS "definitions"
+@
+
+@<scanning rules@>=
+::@=definitions@> :< return DEFINITIONS; >:
+@
+
+@<parsing rules@>=
+definition_section: START DEFINITIONS { hput_definitions_start();}@/
+ max_definitions definition_list @/
+ END {hput_definitions_end();};
+definition_list: @+ | definition_list def_node;
+@
+
+\writecode
+@<write functions@>=
+void hwrite_definitions_start(void)
+{ section_no=1; @+hwritef("<definitions");
+}
+
+void hwrite_definitions_end(void)
+{ hwritef("\n>\n");
+}
+@
+
+
+
+@<get functions@>=
+void hget_definition_section(void)
+{ DBG(DBGBASIC|DBGDEF,"Definitions\n");
+ hget_section(1);
+ hwrite_definitions_start();
+ DBG(DBGDEF,"List of maximum values\n");
+ hget_max_definitions();
+ @<initialize definitions@>@;
+ hwrite_max_definitions();
+ DBG(DBGDEF,"List of definitions\n");
+ while (hpos<hend)
+ hget_def_node();
+ hwrite_definitions_end();
+}
+@
+
+\putcode
+@<put functions@>=
+void hput_definitions_start(void)
+{ DBG(DBGDEF,"Definition Section\n");
+ section_no=1;
+ hpos=hstart=dir[1].buffer;
+ hend=hstart+dir[1].bsize;
+}
+void hput_definitions_end(void)
+{ dir[1].size=hpos-hstart;
+ DBG(DBGDEF,"End Definition Section size=0x%x\n",dir[1].size);
+}
+@
+\gdef\codetitle{Definitions}
+\hascode
+\subsection{Maximum Values}\index{maximum values}
+To help implementations allocating the right amount of memory for the
+definitions, the definition section starts with a list of maximum
+values. For each kind of node, we store the maximum valid reference
+number in the array |max_ref| which is indexed by the kind-values.
+For a reference number |n| and kind-value $k$ we have
+$0\le n\le |max_ref[k]|$.
+To make sure that a hint file without any definitions
+will work, some definitions have default values.
+The initialization of default and maximum values is described
+in section~\secref{defaults}. The maximum
+reference number that has a default value is stored in the array
+|max_default|.
+We have $-1 \le |max_default[k]| \le |max_ref[k]| < 2^{16}$,
+and for most $k$ even $|max_ref[k]| < 2^{8}$.
+Specifying maximum values that are lower than the
+default\index{default value} values is not allowed in the short
+format; in the long format, lower values are silently ignored. Some
+default values are permanently fixed; for example the zero glue with
+reference number |zero_skip_no| must never change. The array
+|max_fixed| stores the maximum reference number that has a fixed value for a
+given kind. Definitions with reference numbers less or equal than the
+corresponding |max_fixed[k]| number are disallowed. Usually we have
+$-1 \le |max_fixed[k]| \le |max_default[k]| $, but if for a kind-value
+$k$ no definitions, and hence no maximum values are allowed, we set
+$|max_fixed[k]|=|0x10000|>|max_default[k]| $.
+
+
+We use the |max_ref| array whenever we find a
+reference number in the input to check if it is within the proper range.
+
+@<debug macros@>=
+#define @[REF_RNG(K,N)@] if ((int)(N)>max_ref[K]) QUIT("Reference %d to %s out of range [0 - %d]",\
+ (N),definition_name[K],max_ref[K])
+@
+
+In the long format file, the list of maximum values starts with
+ ``\.{<max }'', then follow pairs of keywords and numbers like
+ ``\.{<glue 57>}'', and it ends with ``\.{>}''. In the short format,
+we start the list of maximums with a |list_kind| tag and end it with
+a |list_kind| tag. Each maximum value is preceded and followed by a
+tag byte with the appropriate kind-value. The info value has its |b001| bit
+cleared if the maximum value is in the range 0 to |0xFF| and fits into a
+single byte; the info value hast its |b001| bit set if it fits into two byte.
+Currently only the |label_kind| may need to use two byte.
+@<debug macros@>=
+#define MAX_REF(K) ((K)==label_kind?0xFFFF:0xFF)
+@
+
+Other info values are reserved for future extensions.
+After reading the maximum values, we initialize the data structures for
+the definitions.
+
+
+\readcode
+@s max_list symbol
+@s max_value symbol
+@s max_definitions symbol
+@s MAX symbol
+@<symbols@>=
+%token MAX "max"
+@
+
+@<scanning rules@>=
+::@=max@> :< return MAX; >:
+@
+@<parsing rules@>=
+max_definitions: START MAX max_list END @|
+ { @<initialize definitions@>@;@+ hput_max_definitions(); };
+
+max_list:@+ | max_list START max_value END;
+
+max_value: FONT UNSIGNED { hset_max(font_kind,$2); }
+ | INTEGER UNSIGNED { hset_max(int_kind,$2); }
+ | DIMEN UNSIGNED { hset_max(dimen_kind,$2); }
+ | LIGATURE UNSIGNED { hset_max(ligature_kind,$2); }
+ | DISC UNSIGNED { hset_max(disc_kind,$2); }
+ | GLUE UNSIGNED { hset_max(glue_kind,$2); }
+ | LANGUAGE UNSIGNED { hset_max(language_kind,$2); }
+ | RULE UNSIGNED { hset_max(rule_kind,$2); }
+ | IMAGE UNSIGNED { hset_max(image_kind,$2); }
+ | LEADERS UNSIGNED { hset_max(leaders_kind,$2); }
+ | BASELINE UNSIGNED { hset_max(baseline_kind,$2); }
+ | XDIMEN UNSIGNED { hset_max(xdimen_kind,$2); }
+ | PARAM UNSIGNED { hset_max(param_kind,$2); }
+ | STREAMDEF UNSIGNED { hset_max(stream_kind,$2); }
+ | PAGE UNSIGNED { hset_max(page_kind,$2); }
+ | RANGE UNSIGNED { hset_max(range_kind,$2); }
+ | LABEL UNSIGNED { hset_max(label_kind,$2); };
+
+@
+
+@<parsing functions@>=
+void hset_max(kind_t k, int n)
+{ DBG(DBGDEF,"Setting max %s to %d\n",definition_name[k],n);
+ RNG("Maximum",n,max_fixed[k]+1,MAX_REF(k));
+ if (n>max_ref[k])
+ max_ref[k]=n;
+}
+@
+
+\writecode
+@<write functions@>=
+void hwrite_max_definitions(void)
+{ kind_t k;
+ hwrite_start();@+
+ hwritef("max");
+ for (k=0; k<32;k++)
+ if (max_ref[k]>max_default[k])@/
+ {@+ switch (k)
+ { @<cases of writing special maximum values@>@;
+ default:
+ hwrite_start();
+ hwritef("%s %d",definition_name[k], max_ref[k]);
+ hwrite_end();
+ break;
+ }
+ }
+ hwrite_end();
+}
+@
+
+\getcode
+@<get file functions@>=
+void hget_max_definitions(void)
+{ kind_t k;
+ @<read the start byte |a|@>@;
+ if (a!=TAG(list_kind,0)) QUIT("Start of maximum list expected");
+ for(k= 0;k<32;k++)max_ref[k]= max_default[k]; max_outline=-1;
+ while (true) @/
+ { int n;
+ if (hpos>=hend) QUIT("Unexpected end of maximum list");
+ node_pos=hpos-hstart;
+ HGETTAG(a);@+
+ k=KIND(a);@+
+ if (k==list_kind) break;
+ if (INFO(a)&b001) HGET16(n); @+else n=HGET8;
+ switch (a)
+ { @<cases of getting special maximum values@>@;
+ default:
+ if (max_fixed[k]>max_default[k])
+ QUIT("Maximum value for kind %s not supported",definition_name[k]);
+ RNG("Maximum number",n,max_default[k],MAX_REF(k));
+ max_ref[k]=n;
+ DBG(DBGDEF,"max(%s) = %d\n",definition_name[k],max_ref[k]);
+ break;
+ }
+ @<read and check the end byte |z|@>@;
+ }
+ if (INFO(a)!=0) QUIT("End of maximum list with info %d", INFO(a));
+}
+@
+
+\putcode
+
+@<put functions@>=
+void hput_max_definitions(void)
+{ kind_t k;
+ DBG(DBGDEF,"Writing Max Definitions\n");
+ HPUTTAG(list_kind,0);
+ for (k=0; k<32; k++)
+ if (max_ref[k]>max_default[k])
+ { uint32_t pos=hpos++-hstart;
+ DBG(DBGDEF,"max(%s) = %d\n",definition_name[k],max_ref[k]);
+ hput_tags(pos,TAG(k,hput_n(max_ref[k])-1));
+ }
+ @<cases of putting special maximum values@>@;
+ HPUTTAG(list_kind,0);
+ DBG(DBGDEF,"Writing Max Definitions End\n");
+}
+@
+
+
+\subsection{Definitions}\label{definitions}
+A definition\index{definition section} associates a reference number
+with a content node. Here is an example: A glue definition associates
+a glue number, for example 71, with a glue specification. In the long
+format this might look like ``{\tt \.{<}glue *71 4pt plus 5pt minus
+0.5pt\.{>}}'' which makes glue number 71 refer to a 4pt glue with a
+stretchability of 5pt and a shrinkability of 0.5pt.
+Such a glue definition differs from a normal glue node just by an extra
+byte value immediately following the keyword respectively start byte.
+
+Whenever we need this glue in the content section, we can say
+``{\tt \.{<}glue *71\.{>}}''. Because we restrict the number of glue definitions
+to at most 256, a single byte is sufficient to
+store the reference number. The \.{shrink} and \.{stretch} programs
+will, however, not bother to store glue definitions. Instead they will
+write them in the new format immediately to the output.
+
+The parser will handle definitions in any order, but the order is relevant
+if a definition references another definition, and of course,
+it never does any harm to present definitions in a systematic way.
+
+As a rule, the definition of a reference must always precede the
+use of that reference. While this is always the case for
+references in the content section, it restricts the use of
+references inside the definition section.
+
+The definitions for integers, dimensions, extended dimensions,
+ languages, rules, ligatures, and images are ``simple''.
+They never contain references and so it is always possible to list them first.
+The definition of glues may contain extended dimensions,
+the definitions of baselines may reference glue nodes, and
+the definitions of parameter lists contain definitions of integers, dimensions,
+and glues. So these definitions should follow in this order.
+
+The definitions of leaders and discretionary breaks allow boxes.
+While these boxes are usually
+quite simple, they may contain arbitrary references---including again
+references to leaders and discretionary breaks. So, at least in principle,
+they might impose complex (or even unsatisfiable) restrictions
+on the order of those definitions.
+
+The definitions of fonts contain not only ``simple'' definitions
+but also the definitions of interword glues and hyphens
+introducing additional ordering restrictions.
+The definition of hyphens regularly contain glyphs which in turn reference
+a font---typically the font that just gets defined.
+Therefore we relax the define before use policy for glyphs:
+Glyphs may reference a font before the font is defined.
+
+The definitions of page templates contain lists of arbitrary content
+nodes, and while the boxes inside leaders or discretionary breaks tend to be simple,
+the content of page templates is often quite complex.
+Page templates are probably the source of most ordering restrictions.
+Placing page templates towards the end of the list of definitions
+might be a good idea.
+%
+A special case are stream definitions. These occur only as part of
+the corresponding page template definition and are listed at its end.
+So references to them will occur in the page template always before their
+definition.
+%
+Finally, the definitions of page ranges always reference a page template
+and they should come after the page template definitions.
+For technical reasons explained in section~\secref{labels},
+definitions of labels and outlines come last.
+
+To avoid complex dependencies, an application can always choose not to
+use references in the definition section. There are only three types of
+nodes where references can not be avoided: fonts are referenced in glyph nodes,
+labels are referenced in outlines,
+and languages are referenced in boxes or page templates.
+Possible ordering restrictions can be satisfied if languages are defined early.
+To check the define before use policy, we use an array of bitvectors,
+but we limit checking to the first 256 references.
+We have for every reference number $|N|<256$ and every kind |K| a single
+bit which is set if and only if the corresponding reference is defined.
+
+@<definition checks@>=
+uint32_t definition_bits[0x100/32][32]={{0}};
+
+#define @[SET_DBIT(N,K)@] ((N)>0xFF?1:(definition_bits[N/32][K]|=(1<<((N)&(32-1)))))
+#define @[GET_DBIT(N,K)@] ((N)>0xFF?1:((definition_bits[N/32][K]>>((N)&(32-1)))&1))
+#define @[DEF(D,K,N)@] (D).k=K;@+ (D).n=(N);@+SET_DBIT((D).n,(D).k);\
+ DBG(DBGDEF,"Defining %s %d\n",definition_name[(D).k],(D).n);\
+ RNG("Definition",(D).n,max_fixed[(D).k]+1,max_ref[(D).k]);
+#define @[REF(K,N)@] REF_RNG(K,N);if(!GET_DBIT(N,K)) \
+ QUIT("Reference %d to %s before definition",(N),definition_name[K])
+@
+
+@<initialize definitions@>=
+definition_bits[0][int_kind]=(1<<(MAX_INT_DEFAULT+1))-1;
+definition_bits[0][dimen_kind]=(1<<(MAX_DIMEN_DEFAULT+1))-1;
+definition_bits[0][xdimen_kind]=(1<<(MAX_XDIMEN_DEFAULT+1))-1;
+definition_bits[0][glue_kind]=(1<<(MAX_GLUE_DEFAULT+1))-1;
+definition_bits[0][baseline_kind]=(1<<(MAX_BASELINE_DEFAULT+1))-1;
+definition_bits[0][page_kind]=(1<<(MAX_PAGE_DEFAULT+1))-1;
+definition_bits[0][stream_kind]=(1<<(MAX_STREAM_DEFAULT+1))-1;
+definition_bits[0][range_kind]=(1<<(MAX_RANGE_DEFAULT+1))-1;
+@
+
+\goodbreak
+\vbox{\readcode\vskip -\baselineskip\putcode}
+
+
+@s font symbol
+@<symbols@>=
+
+%type <rf> def_node
+@
+
+@<parsing rules@>=
+def_node:
+ start FONT ref font END @| { DEF($$,font_kind,$3);@+ hput_tags($1,$4);@+}
+| start INTEGER ref integer END @| { DEF($$,int_kind,$3);@+ hput_tags($1,hput_int($4));@+}
+| start DIMEN ref dimension END @| { DEF($$,dimen_kind,$3);@+ hput_tags($1,hput_dimen($4));}
+| start LANGUAGE ref string END @| { DEF($$,language_kind,$3);@+hput_string($4); hput_tags($1,TAG(language_kind,0));}
+| start GLUE ref glue END @| { DEF($$,glue_kind,$3);@+ hput_tags($1,hput_glue(&($4)));}
+| start XDIMEN ref xdimen END @| { DEF($$,xdimen_kind,$3);@+ hput_tags($1,hput_xdimen(&($4)));}
+| start RULE ref rule END @| { DEF($$,rule_kind,$3);@+ hput_tags($1,hput_rule(&($4)));}
+| start LEADERS ref leaders END @| { DEF($$,leaders_kind,$3);@+ hput_tags($1,TAG(leaders_kind, $4));}
+| start BASELINE ref baseline END @| { DEF($$,baseline_kind,$3);@+hput_tags($1,TAG(baseline_kind, $4));@+}
+| start LIGATURE ref ligature END @| { DEF($$,ligature_kind,$3);@+hput_tags($1,hput_ligature(&($4)));}
+| start DISC ref disc END @| { DEF($$,disc_kind,$3);@+ hput_tags($1,hput_disc(&($4)));}
+| start IMAGE ref image END @| { DEF($$,image_kind,$3);@+ hput_tags($1,hput_image(&($4)));}
+| start PARAM ref parameters END @| { DEF($$,param_kind,$3);@+ hput_tags($1,hput_list($1+2,&($4)));}
+| start PAGE ref page END @| { DEF($$,page_kind,$3);@+ hput_tags($1,TAG(page_kind,0));};
+@
+
+There are a few cases where one wants to define a reference by a reference.
+For example, a \HINT\ file may want to set the {\tt parfillskip} glue to zero.
+While there are multiple ways to define the zero glue, the canonical way is a reference
+using the |zero_glue_no|. All these cases have in common that the reference to be defined
+is one of the default references and the defining reference is one of the fixed references.
+We add a few parsing rules and a testing macro for those cases where the number
+of default definitions is greater than the number of fixed definitions.
+
+@<definition checks@>=
+#define @[DEF_REF(D,K,M,N)@] DEF(D,K,M);\
+if ((M)>max_default[K]) QUIT("Defining non default reference %d for %s",M,definition_name[K]); \
+if ((N)>max_fixed[K]) QUIT("Defining reference %d for %s by non fixed reference %d",M,definition_name[K],N);
+@
+
+@<parsing rules@>=
+def_node:
+ start INTEGER ref ref END @/{DEF_REF($$,int_kind,$3,$4); hput_tags($1,TAG(int_kind,0)); }
+| start DIMEN ref ref END @/{DEF_REF($$,dimen_kind,$3,$4); hput_tags($1,TAG(dimen_kind,0)); }
+| start GLUE ref ref END @/{DEF_REF($$,glue_kind,$3,$4); hput_tags($1,TAG(glue_kind,0)); };
+@
+
+
+
+
+\goodbreak
+\vbox{\getcode\vskip -\baselineskip\writecode}
+
+@<get functions@>=
+void hget_definition(int n, uint8_t a, uint32_t node_pos)
+{@+ switch(KIND(a))
+ { case font_kind: hget_font_def(n);@+ break;
+ case param_kind:
+ {@+ list_t l; @+HGET_LIST(INFO(a),l); @+hwrite_parameters(&l); @+ break;@+}
+ case page_kind: hget_page(); @+break;
+ case dimen_kind: hget_dimen(a); @+break;
+ case xdimen_kind:
+ {@+ xdimen_t x; @+hget_xdimen(a,&x); @+hwrite_xdimen(&x); @+break;@+ }
+ case language_kind:
+ if (INFO(a)!=b000)
+ QUIT("Info value of language definition must be zero");
+ else
+ { char *n; HGET_STRING(n);@+ hwrite_string(n); }
+ break;
+ default:
+ hget_content(a); @+break;
+ }
+}
+
+
+void hget_def_node()
+{ kind_t k;
+
+ @<read the start byte |a|@>@;
+ k=KIND(a);
+ if (k==label_kind)
+ hget_outline_or_label_def(INFO(a),node_pos);
+ else
+ { int n;
+ n=HGET8;
+ if (k!=range_kind) REF_RNG(k,n);
+ SET_DBIT(n,k);
+ if (k==range_kind)
+ hget_range(INFO(a),n);
+ else
+ { hwrite_start(); @+hwritef("%s *%d",definition_name[k],n);
+ hget_definition(n,a,node_pos);
+ hwrite_end();
+ }
+ if(n>max_ref[k] || n <= max_fixed[k])
+ QUIT("Definition %d for %s out of range [%d - %d]",@|
+ n, definition_name[k],max_fixed[k]+1,max_ref[k]);
+ }
+ if (max_fixed[k]>max_default[k])
+ QUIT("Definitions for kind %s not supported", definition_name[k]);
+ @<read and check the end byte |z|@>@;
+}
+@
+
+
+
+\subsection{Parameter Lists}\label{paramlist}\index{parameter list}
+Because the content section is a ``stateless'' list of nodes, the
+definitions we see in the definition section can never change. It is
+however necessary to make occasionally local modifications of some of
+these definitions, because some definitions are parameters of the
+algorithms borrowed from \TeX. Nodes that need such modifications, for
+example the paragraph nodes that are passed to \TeX's line breaking
+algorithm, contain a list of local definitions called parameters.
+Typically sets of related parameters are needed. To facilitate a
+simple reference to such a set of parameters, we allow predefined
+parameter lists that can be referenced by a single number. The
+parameters of \TeX's routines are quite basic---integers\index{integer},
+dimensions\index{dimension}, and glues\index{glue}---and all
+of them have default values.
+Therefore we restrict the definitions in parameter lists to such
+basic definitions.
+
+@<parsing functions@>=
+void check_param_def(ref_t *df)
+{ if(df->k!=int_kind && df->k!=dimen_kind && @| df->k!=glue_kind)
+ QUIT("Kind %s not allowed in parameter list", definition_name[df->k]);
+ if(df->n<=max_fixed[df->k] || max_default[df->k]<df->n)
+ QUIT("Parameter %d for %s not allowed in parameter list", df->n, definition_name[df->k]);
+}
+@
+
+The definitions below repeat the definitions we have seen for lists in section~\secref{plainlists}
+with small modifications. For example we use the kind-value |param_kind|. An empty parameter list
+is omitted in the long format as well as in the short format.
+
+\goodbreak
+\vbox{\readcode\vskip -\baselineskip\putcode}
+
+@s PARAM symbol
+@s def_list symbol
+@s parameters_node symbol
+@s def_node symbol
+@s parameters symbol
+@s empty_param_list symbol
+@s non_empty_param_list symbol
+
+@<symbols@>=
+%token PARAM "param"
+%type <u> def_list
+%type <l> parameters
+@
+
+@<scanning rules@>=
+::@=param@> :< return PARAM; >:
+@
+@<parsing rules@>=
+def_list: position @+
+ | def_list def_node {check_param_def(&($2));};
+parameters: estimate def_list { $$.p=$2; $$.k=param_kind; $$.s=(hpos-hstart)-$2;};
+@
+
+Using a parsing rule like
+``\nts{param\_list}: \nts{start} \ts{PARAM} \nts{parameters} \ts{END}'',
+an empty parameter list will be written as ``\.{<param>}''.
+This looks ugly and seems like unnecessary syntax. It would be nicer
+if an empty parameter list could simply be omitted.
+Generating an empty parameter list for an omitted parameter list
+is however a bit tricky.
+Consider the sequence ``\.{<param\dots>} \.{<hbox\dots>}'' versus
+the sequence ``\.{<hbox\dots>}''. In the latter case,
+the parser will notice the missing parameter list
+when it encounters the \.{hbox} token.
+Of course it is not a good idea to augment the rules for the \.{hbox} with
+a special test for the missing empty parameter list.
+It is better to insert an empty parameter list before parsing the first ``\.{<}'' token
+and remove it again if a non-empty parameter list has been detected.
+This can be accomplished by the following two rules.
+
+@<parsing rules@>=
+empty_param_list: position { HPUTX(2); hpos++; hput_tags($1,TAG(param_kind,1));};
+non_empty_param_list: start PARAM {hpos=hpos-2;} parameters END @/
+ { @+ hput_tags($1-2,hput_list($1-1,&($4)));@+};
+@
+
+\writecode
+@<write functions@>=
+void hwrite_parameters(list_t *l)
+{ uint32_t h=hpos-hstart, e=hend-hstart; /* save |hpos| and |hend| */
+ hpos=l->p+hstart;@+ hend=hpos+l->s;
+ if (l->s>0xFF) hwritef(" %d",l->s);
+ while(hpos<hend) hget_def_node();
+ hpos=hstart+h;@+ hend=hstart+e; /* restore |hpos| and |hend| */
+}
+void hwrite_param_list(list_t *l)
+{ @+if (l->s!=0) @/
+ { hwrite_start();@+
+ hwritef("param");
+ hwrite_parameters(l);
+ hwrite_end();
+ }
+}
+@
+
+\getcode
+@<get functions@>=
+void hget_param_list(list_t *l)
+{ @+if (KIND(*hpos)!=param_kind) @/
+ QUIT("Parameter list expected at 0x%x", (uint32_t)(hpos-hstart));
+ else hget_list(l);
+}
+@
+
+
+
+\subsection{Fonts}\label{fonts}
+Another definition that has no corresponding content node is the
+font\index{font} definition. Fonts by themselves do not constitute
+content, instead they are used in glyph\index{glyph} nodes.
+Further, fonts are never directly embedded in a content node; in a content node, a
+font is always specified by its font number. This limits the number of
+fonts that can be used in a \HINT\ file to at most 256.
+
+A long format font definition starts with the keyword ``\.{font}'' and
+is followed by the font number, as usual prefixed by an asterisk. Then
+comes the font specification with the font size, the font
+name, the section number of the \TeX\ font metric file, and the
+section number of the file containing the glyphs for the font.
+The \HINT\ format supports \.{.pk} files, the traditional font format
+for \TeX, and the more modern PostScript Type 1 fonts,
+TrueType fonts, and OpenType fonts.
+
+The format of font definitions will probably change in future
+versions of the \HINT\ file format.
+For example, \.{.pk} files might be replaced entirely by PostScript Type 1 fonts.
+Also \HINT\ needs the \TeX\ font metric files only to obtain the sizes
+of characters when running \TeX's line breaking algorithm.
+But for many TrueType fonts there are no \TeX\ font metric files,
+while the necessary information about character sizes should be easy
+to obtain.
+Another information, that is currently missing from font definitions,
+is the fonts character encoding.
+
+In a \HINT\ file, text is represented as a sequence of numbers called
+character codes. \HINT\ files use the UTF-8 character encoding
+scheme (CES) to map these numbers to their representation as byte
+sequences. For example the number ``|0xE4|'' is encoded as the byte
+sequence ``|0xC3| |0xA4|''. The same number |0xE4| now can represent
+different characters depending on the coded character set (CCS). For
+example in the common ISO-8859-1 (Latin 1) encoding the number |0xE4|
+is the umlaut ``\"a'' where as in the ISO-8859-7 (Latin/Greek) it is
+the Greek letter ``$\delta$'' and in the EBCDIC encoding, used on IBM
+mainframes, it is the upper case letter ``U''.
+
+The character encoding is
+irrelevant for rendering a \HINT\ file as long as the character codes
+in the glyph nodes are consistent with the character codes used in the font
+file, but the character encoding is necessary for all programs that
+need to ``understand'' the content of the \HINT\ file. For example
+programs that want to translate a \HINT\ document to a different language,
+or for text-to-speech conversion.
+
+The Internet Engineering Task Force IETF has established a character set
+registry\cite{ietf:charset-mib} that defines an enumeration of all
+registered coded character sets\cite{iana:charset-mib}. The coded
+character set numbers are in the range 1--2999.
+This encoding number, as given in~\cite{iana:charset},
+might be one possibility for specifying the font encoding as
+part of a font definition.
+
+Currently, it is only required that a font specifies
+an interword glue and a default discretionary break. After that comes
+a list of up to 12 font specific parameters.
+
+The font size specifies the desired ``at size''\index{font at size}
+which might be different from the ``design size''\index{font design size}
+of the font as stored in the \.{.tfm} file.
+
+In the short format, the font specification is given in the same order
+as in the long format.
+
+Our internal representation of a font just stores the font name
+because in the long format we add the font name as a comment to glyph
+nodes.
+
+
+@<common variables@>=
+char **hfont_name; /* dynamically allocated array of font names */
+@
+
+@<hint basic types@>=
+#define MAX_FONT_PARAMS 11
+@
+
+@<initialize definitions@>=
+ALLOCATE(hfont_name,max_ref[font_kind]+1,char *);
+@
+
+
+
+\readcode
+@s FONT symbol
+@s fref symbol
+@s font_param_list symbol
+@s font_param symbol
+@s font_head symbol
+
+@<symbols@>=
+%token FONT "font"
+%type <info> font font_head
+@
+
+@<scanning rules@>=
+::@=font@> :< return FONT; >:
+@
+
+Note that we set the definition bit early because the definition of font |f|
+might involve glyphs that reference font |f| (or other fonts).
+
+@<parsing rules@>=@/
+
+font: font_head font_param_list;
+
+font_head: string dimension UNSIGNED UNSIGNED @/
+ {uint8_t f=$<u>@&0; SET_DBIT(f,font_kind); @+hfont_name[f]=strdup($1); $$=hput_font_head(f,hfont_name[f],$2,$3,$4);};
+
+font_param_list: glue_node disc_node @+ | font_param_list font_param ;
+
+font_param: @/
+ start PENALTY fref penalty END { hput_tags($1,hput_int($4));}
+| start KERN fref kern END { hput_tags($1,hput_kern(&($4)));}
+| start LIGATURE fref ligature END { hput_tags($1,hput_ligature(&($4)));}
+| start DISC fref disc END { hput_tags($1,hput_disc(&($4)));}
+| start GLUE fref glue END { hput_tags($1,hput_glue(&($4)));}
+| start LANGUAGE fref string END { hput_string($4);hput_tags($1,TAG(language_kind,0));}
+| start RULE fref rule END { hput_tags($1,hput_rule(&($4)));}
+| start IMAGE fref image END { hput_tags($1,hput_image(&($4)));};
+
+fref: ref @/{ RNG("Font parameter",$1,0,MAX_FONT_PARAMS); };
+@
+
+\goodbreak
+\vbox{\getcode\vskip -\baselineskip\writecode}
+
+@<get functions@>=
+static void hget_font_params(void)
+{ disc_t h;
+ hget_glue_node();
+ hget_disc_node(&(h));@+ hwrite_disc_node(&h);
+ DBG(DBGDEF,"Start font parameters\n");
+ while (KIND(*hpos)!=font_kind)@/
+ { ref_t df;
+ @<read the start byte |a|@>@;
+ df.k=KIND(a);
+ df.n=HGET8;
+ DBG(DBGDEF,"Reading font parameter %d: %s\n",df.n, definition_name[df.k]);
+ if (df.k!=penalty_kind && df.k!=kern_kind && df.k!=ligature_kind && @|
+ df.k!=disc_kind && df.k!=glue_kind && df.k!=language_kind && @| df.k!=rule_kind && df.k!=image_kind)
+ QUIT("Font parameter %d has invalid type %s",df.n, content_name[df.n]);
+ RNG("Font parameter",df.n,0,MAX_FONT_PARAMS);
+ hwrite_start(); @+ hwritef("%s *%d",content_name[KIND(a)],df.n);
+ hget_definition(df.n,a,node_pos);
+ hwrite_end();
+ @<read and check the end byte |z|@>@;
+ }
+ DBG(DBGDEF,"End font parameters\n");
+}
+
+
+void hget_font_def(uint8_t f)
+{ char *n; @+dimen_t s=0;@+uint16_t m,y;
+ HGET_STRING(n);@+ hwrite_string(n);@+ hfont_name[f]=strdup(n);
+ HGET32(s); @+ hwrite_dimension(s);
+ DBG(DBGDEF,"Font %s size 0x%x\n", n, s);
+ HGET16(m); @+RNG("Font metrics",m,3,max_section_no);
+ HGET16(y); @+RNG("Font glyphs",y,3,max_section_no);
+ hwritef(" %d %d",m,y);
+ hget_font_params();
+ DBG(DBGDEF,"End font definition\n");
+}
+@
+
+\putcode
+@<put functions@>=
+uint8_t hput_font_head(uint8_t f, char *n, dimen_t s, @| uint16_t m, uint16_t y)
+{ info_t i=b000;
+ DBG(DBGDEF,"Defining font %d (%s) size 0x%x\n", f, n, s);
+ hput_string(n);
+ HPUT32(s);@+
+ HPUT16(m); @+HPUT16(y);
+ return TAG(font_kind,i);
+}
+@
+
+
+
+\subsection{References}\label{reference}
+We have seen how to make definitions, now let's see how to
+reference\index{reference} them. In the long form, we can simply
+write the reference number, after the keyword like this:
+``{\tt \.{<}glue *17\.{>}}''.
+The asterisk\index{asterisk} is necessary to keep apart,
+for example, a penalty with value 50,
+written ``{\tt \.{<}penalty 50\.{>}}'',
+from a penalty referencing the integer
+definition number 50, written ``{\tt \.{<}penalty *50\.{>}}''.
+
+\goodbreak
+\vbox{\readcode\vskip -\baselineskip\putcode}
+
+@<parsing rules@>=
+xdimen_ref: ref { REF(xdimen_kind,$1);};
+param_ref: ref { REF(param_kind,$1); };
+stream_ref: ref { REF_RNG(stream_kind,$1); };
+
+
+content_node:
+ start PENALTY ref END @/{ REF(penalty_kind,$3); @+ hput_tags($1,TAG(penalty_kind,0)); }
+|start KERN explicit ref END @/
+ { REF(dimen_kind,$4); @+ hput_tags($1,TAG(kern_kind,($3)?b100:b000)); }
+|start KERN explicit XDIMEN ref END @/
+ { REF(xdimen_kind,$5); @+hput_tags($1,TAG(kern_kind,($3)?b101:b001)); }
+|start GLUE ref END @/{ REF(glue_kind,$3); @+ hput_tags($1,TAG(glue_kind,0)); }
+|start LIGATURE ref END @/{ REF(ligature_kind,$3); @+ hput_tags($1,TAG(ligature_kind,0)); }
+|start DISC ref END @/{ REF(disc_kind,$3); @+ hput_tags($1,TAG(disc_kind,0)); }
+|start RULE ref END @/{ REF(rule_kind,$3); @+ hput_tags($1,TAG(rule_kind,0)); }
+|start IMAGE ref END @/{ REF(image_kind,$3);@+ hput_tags($1,TAG(image_kind,0)); }
+|start LEADERS ref END @/{ REF(leaders_kind,$3); @+ hput_tags($1,TAG(leaders_kind,0)); }
+|start BASELINE ref END @/{ REF(baseline_kind,$3);@+ hput_tags($1,TAG(baseline_kind,0)); }
+|start LANGUAGE REFERENCE END @/{ REF(language_kind,$3);@+ hput_tags($1,hput_language($3)); };
+
+glue_node: start GLUE ref END @/{ REF(glue_kind,$3);
+if ($3==zero_skip_no) { hpos=hpos-2; $$=false;@+ }
+else {hput_tags($1,TAG(glue_kind,0)); $$=true;@t\2@>@+}};
+
+@
+
+\getcode
+@<cases to get content@>=
+@t\1\kern1em@>
+case TAG(penalty_kind,0): HGET_REF(penalty_kind); @+break;
+case TAG(kern_kind,b000): HGET_REF(dimen_kind); @+break;
+case TAG(kern_kind,b100): hwritef(" !"); @+HGET_REF(dimen_kind); @+break;
+case TAG(kern_kind,b001): @| hwritef(" xdimen");@+ HGET_REF(xdimen_kind); @+break;
+case TAG(kern_kind,b101): @| hwritef(" ! xdimen");@+ HGET_REF(xdimen_kind); @+break;
+case TAG(ligature_kind,0): HGET_REF(ligature_kind); @+break;
+case TAG(disc_kind,0): HGET_REF(disc_kind); @+break;
+case TAG(glue_kind,0): HGET_REF(glue_kind); @+break;
+case TAG(language_kind,b000): HGET_REF(language_kind); @+break;
+case TAG(rule_kind,0): HGET_REF(rule_kind); @+break;
+case TAG(image_kind,0): HGET_REF(image_kind); @+break;
+case TAG(leaders_kind,0): HGET_REF(leaders_kind); @+break;
+case TAG(baseline_kind,0): HGET_REF(baseline_kind); @+break;
+@
+
+@<get macros@>=
+#define @[HGET_REF(K)@] {uint8_t n=HGET8;@+ REF(K,n); @+hwrite_ref(n);@+}
+@
+\writecode
+@<write functions@>=
+void hwrite_ref(int n)
+{hwritef(" *%d",n);@+}
+
+void hwrite_ref_node(kind_t k, uint8_t n)
+{ hwrite_start(); @+hwritef("%s",content_name[k]);@+ hwrite_ref(n); @+hwrite_end();}
+@
+
+
+
+\section{Defaults}\label{defaults}\index{default value}
+Several of the predefined values found in the definition section are used
+as parameters for the routines borrowed from \TeX\ to display the content
+of a \HINT\ file. These values must be defined, but it is inconvenient if
+the same standard definitions need to be placed in each and every \HINT\ file.
+Therefore we specify in this chapter reasonable default values.
+As a consequence, even a \HINT\ file without any definitions should
+produce sensible results when displayed.
+
+The definitions that have default values are integers, dimensions,
+extended dimensions, glues, baselines, labels, page templates,
+streams, and page ranges.
+Each of these defaults has its own subsection below.
+Actually the defaults for extended dimensions, baselines, and labels
+ are not needed by \TeX's routines, but it is nice to have default
+values for the extended dimensions that represent
+\.{hsize}, \.{vsize}, a zero baseline skip, and a label for the table
+of content.
+
+The array |max_default| contains for each kind-value the maximum number of
+the default values. The function |hset_max| is used to initialize them.
+
+The programs \.{shrink} and \.{stretch} actually do not use the defaults,
+but it would be possible to suppress definitions if the defined value
+is the same as the default value.
+%
+We start by setting |max_default[k]==-1|, meaning no defaults,
+and |max_fixed[k]==0x10000|, meaning no definitions.
+The following subsections will then overwrite these values for
+all kinds of definitions that have defaults.
+It remains to reset |max_fixed| to $-1$ for all those kinds
+that have no defaults but allow definitions.
+
+@<take care of variables without defaults@>=
+ for (k=0; k<32; k++) max_default[k]=-1,max_fixed[k]=0x10000;
+ @/@t}$\hangindent=1em${@>max_fixed[font_kind]= max_fixed[ligature_kind]= max_fixed[disc_kind]
+ @|=max_fixed[language_kind]=max_fixed[rule_kind]= max_fixed[image_kind]
+ @|= max_fixed[leaders_kind]= max_fixed[param_kind]=max_fixed[label_kind]@|= -1;
+@
+
+
+\subsection{Integers}
+Integers\index{integer} are very simple objects, and it might be tempting not to
+use predefined integers at all. But the \TeX\ typesetting engine,
+which is used by \HINT, uses many integer parameters to fine tune
+its operations. As we will see, all these integer parameters have a predefined
+integer number that refers to an integer definition.
+
+Integers and penalties\index{penalty} share the same kind-value. So a penalty node that references
+one of the predefined penalties, simply contains the integer number as a reference
+number.
+
+The following integer numbers are predefined.
+The zero integer is fixed with integer number zero. %It is never redefined.
+The default values are taken from {\tt plain.tex}.
+
+@<default names@>=
+typedef enum {@t}$\hangindent=2em${@>
+ zero_int_no=0,
+ pretolerance_no=1,
+ tolerance_no=2,
+ line_penalty_no=3,
+ hyphen_penalty_no=4,
+ ex_hyphen_penalty_no=5,
+ club_penalty_no=6,
+ widow_penalty_no=7,
+ display_widow_penalty_no=8,
+ broken_penalty_no=9,
+ pre_display_penalty_no=10,
+ post_display_penalty_no=11,
+ inter_line_penalty_no=12,
+ double_hyphen_demerits_no=13,
+ final_hyphen_demerits_no=14,
+ adj_demerits_no=15,
+ looseness_no=16,
+ time_no=17,
+ day_no=18,
+ month_no=19,
+ year_no=20,
+ hang_after_no=21,
+ floating_penalty_no=22
+} int_no_t;
+#define MAX_INT_DEFAULT floating_penalty_no
+@
+
+@<define |int_defaults|@>=
+max_default[int_kind]=MAX_INT_DEFAULT;
+max_fixed[int_kind]=zero_int_no;
+int_defaults[zero_int_no]=0;
+int_defaults[pretolerance_no]=100;
+int_defaults[tolerance_no]=200;
+int_defaults[line_penalty_no]=10;
+int_defaults[hyphen_penalty_no]=50;
+int_defaults[ex_hyphen_penalty_no]=50;
+int_defaults[club_penalty_no]=150;
+int_defaults[widow_penalty_no]=150;
+int_defaults[display_widow_penalty_no]=50;
+int_defaults[broken_penalty_no]=100;
+int_defaults[pre_display_penalty_no]=10000;
+int_defaults[post_display_penalty_no]=0;
+int_defaults[inter_line_penalty_no]=0;
+int_defaults[double_hyphen_demerits_no]=10000;
+int_defaults[final_hyphen_demerits_no]=5000;
+int_defaults[adj_demerits_no]=10000;
+int_defaults[looseness_no]=0;
+int_defaults[time_no]=720;
+int_defaults[day_no]=4;
+int_defaults[month_no]=7;
+int_defaults[year_no]=1776;
+int_defaults[hang_after_no]=1;
+int_defaults[floating_penalty_no]=20000;
+@#
+
+printf("int32_t int_defaults[MAX_INT_DEFAULT+1]={");
+for (i=0; i<= max_default[int_kind];i++)@/
+{ printf("%d",int_defaults[i]);@+
+ if (i<max_default[int_kind]) printf(", ");@+
+}
+printf("};\n\n");
+@
+
+\subsection{Dimensions}
+
+Notice that there are default values for the two dimensions \.{hsize} and \.{vsize}.
+These are the ``design sizes'' for the hint file. While it might not be possible
+to display the \HINT\ file using these values of \.{hsize} and \.{vsize},
+these are the author's recommendation for the best ``viewing experience''.
+
+\noindent
+@<default names@>=
+typedef enum {@t}$\hangindent=2em${@>
+zero_dimen_no=0,
+hsize_dimen_no=1,
+vsize_dimen_no=2,
+line_skip_limit_no=3,
+max_depth_no=4,
+split_max_depth_no=5,
+hang_indent_no=6,
+emergency_stretch_no=7,
+quad_no=8,
+math_quad_no=9
+} dimen_no_t;
+#define MAX_DIMEN_DEFAULT math_quad_no
+@
+
+@<define |dimen_defaults|@>=
+max_default[dimen_kind]=MAX_DIMEN_DEFAULT;
+max_fixed[dimen_kind]=zero_dimen_no;@#
+dimen_defaults[zero_dimen_no]=0;
+dimen_defaults[hsize_dimen_no]=(dimen_t)(6.5*72.27*ONE);
+dimen_defaults[vsize_dimen_no]=(dimen_t)(8.9*72.27*ONE);
+dimen_defaults[line_skip_limit_no]=0;
+dimen_defaults[split_max_depth_no]=(dimen_t)(3.5*ONE);
+dimen_defaults[hang_indent_no]=0;
+dimen_defaults[emergency_stretch_no]=0;
+dimen_defaults[quad_no]=10*ONE;
+dimen_defaults[math_quad_no]=10*ONE;@#
+
+printf("dimen_t dimen_defaults[MAX_DIMEN_DEFAULT+1]={");
+for (i=0; i<= max_default[dimen_kind];i++)
+{ printf("0x%x",dimen_defaults[i]);
+ if (i<max_default[dimen_kind]) printf(", ");
+}
+printf("};\n\n");
+@
+
+\subsection{Extended Dimensions}
+Extended dimensions\index{extended dimension} can be used in a variety of nodes for example
+kern\index{kern} and box\index{box} nodes.
+We define three fixed extended dimensions: zero, hsize, and vsize.
+In contrast to the \.{hsize} and \.{vsize} dimensions defined in the previous
+section, the extended dimensions defined here are linear functions that always evaluate
+to the current horizontal and vertical size in the viewer.
+
+@<default names@>=
+typedef enum {
+zero_xdimen_no=0,
+hsize_xdimen_no=1,
+vsize_xdimen_no=2
+} xdimen_no_t;
+#define MAX_XDIMEN_DEFAULT vsize_xdimen_no
+@
+
+@<define |xdimen_defaults|@>=
+max_default[xdimen_kind]=MAX_XDIMEN_DEFAULT;
+max_fixed[xdimen_kind]=vsize_xdimen_no;@#
+
+printf("xdimen_t xdimen_defaults[MAX_XDIMEN_DEFAULT+1]={"@/
+"{0x0, 0.0, 0.0}, {0x0, 1.0, 0.0}, {0x0, 0.0, 1.0}"@/
+"};\n\n");
+@
+
+
+\subsection{Glue}
+
+There are predefined glue\index{glue} numbers that correspond to the skip parameters of \TeX.
+The default values are taken from {\tt plain.tex}.
+
+@<default names@>=
+typedef enum {@t}$\hangindent=2em${@>
+zero_skip_no=0,
+fil_skip_no=1,
+fill_skip_no=2,
+line_skip_no=3,
+baseline_skip_no=4,
+above_display_skip_no=5,
+below_display_skip_no=6,
+above_display_short_skip_no=7,
+below_display_short_skip_no=8,
+left_skip_no=9,
+right_skip_no=10,
+top_skip_no=11,
+split_top_skip_no=12,
+tab_skip_no=13,
+par_fill_skip_no=14
+} glue_no_t;
+#define MAX_GLUE_DEFAULT par_fill_skip_no
+@
+
+@<define |glue_defaults|@>=
+max_default[glue_kind]=MAX_GLUE_DEFAULT;
+max_fixed[glue_kind]=fill_skip_no;
+
+glue_defaults[fil_skip_no].p.f=1.0;
+glue_defaults[fil_skip_no].p.o=fil_o;
+
+glue_defaults[fill_skip_no].p.f=1.0;
+glue_defaults[fill_skip_no].p.o=fill_o;@#
+
+glue_defaults[line_skip_no].w.w=1*ONE;
+glue_defaults[baseline_skip_no].w.w=12*ONE;
+
+glue_defaults[above_display_skip_no].w.w=12*ONE;
+glue_defaults[above_display_skip_no].p.f=3.0;
+glue_defaults[above_display_skip_no].p.o=normal_o;
+glue_defaults[above_display_skip_no].m.f=9.0;
+glue_defaults[above_display_skip_no].m.o=normal_o;
+
+glue_defaults[below_display_skip_no].w.w=12*ONE;
+glue_defaults[below_display_skip_no].p.f=3.0;
+glue_defaults[below_display_skip_no].p.o=normal_o;
+glue_defaults[below_display_skip_no].m.f=9.0;
+glue_defaults[below_display_skip_no].m.o=normal_o;
+
+glue_defaults[above_display_short_skip_no].p.f=3.0;
+glue_defaults[above_display_short_skip_no].p.o=normal_o;
+
+glue_defaults[below_display_short_skip_no].w.w=7*ONE;
+glue_defaults[below_display_short_skip_no].p.f=3.0;
+glue_defaults[below_display_short_skip_no].p.o=normal_o;
+glue_defaults[below_display_short_skip_no].m.f=4.0;
+glue_defaults[below_display_short_skip_no].m.o=normal_o;
+
+glue_defaults[top_skip_no].w.w=10*ONE;
+glue_defaults[split_top_skip_no].w.w=(dimen_t)8.5*ONE;
+
+glue_defaults[par_fill_skip_no].p.f=1.0;
+glue_defaults[par_fill_skip_no].p.o=fil_o;
+
+#define @[PRINT_GLUE(G)@] \
+ @[printf("{{0x%x, %f, %f},{%f, %d},{%f, %d}}",\
+ G.w.w, G.w.h, G.w.v, G.p.f, G.p.o, G.m.f,G.m.o)@]@#
+
+printf("glue_t glue_defaults[MAX_GLUE_DEFAULT+1]={\n");
+for (i=0; i<= max_default[glue_kind];i++)@/
+{ PRINT_GLUE(glue_defaults[i]); @+
+ if (i<max_default[int_kind]) printf(",\n");
+}
+printf("};\n\n");
+@
+
+We fix the glue definition with number zero to be the ``zero glue'': a
+glue with width zero and zero stretchability and shrinkability. Here
+is the reason: In the short format, the info bits of a glue node
+indicate which components of a glue are nonzero. Therefore the zero
+glue should have an info value of zero---which on the other hand is
+reserved for a reference to a glue definition. Hence, the best way to
+represent a zero glue is as a predefined glue.
+
+
+\subsection{Baseline Skips}
+
+The zero baseline\index{baseline skip} which inserts no baseline skip is predefined.
+
+@<default names@>=
+typedef enum {@+
+zero_baseline_no=0@+
+} baseline_no_t;
+#define MAX_BASELINE_DEFAULT zero_baseline_no
+@
+@<define |baseline_defaults|@>=
+max_default[baseline_kind]=MAX_BASELINE_DEFAULT;
+max_fixed[baseline_kind]=zero_baseline_no;@#
+{ baseline_t z={{{0}}};
+ printf("baseline_t baseline_defaults[MAX_BASELINE_DEFAULT+1]={{");
+ PRINT_GLUE(z.bs); @+printf(", "); @+PRINT_GLUE(z.ls); printf(", 0x%x}};\n\n",z.lsl);
+}
+@
+
+\subsection{Labels}
+The zero label\index{label} is predefined. It should point to the
+``home'' position of the document which should be the position
+where a user can start reading or navigating the document.
+For a short document this is usually the start of the document,
+and hence, the default is the first position of the content section.
+For a larger document, the home position could point to the
+table of content where a reader will find links to other parts
+of the document.
+
+@<default names@>=
+typedef enum {@+
+zero_label_no=0@+
+} label_no_t;
+#define MAX_LABEL_DEFAULT zero_label_no
+@
+@<define |label_defaults|@>=
+max_default[label_kind]=MAX_LABEL_DEFAULT;
+printf("label_t label_defaults[MAX_LABEL_DEFAULT+1]="@|"{{0,LABEL_TOP,true,0,0,0}};\n\n");
+@
+
+
+\subsection{Streams}
+The zero stream\index{stream} is predefined for the main content.
+@<default names@>=
+typedef enum {@+
+zero_stream_no=0@+
+} stream_no_t;
+#define MAX_STREAM_DEFAULT zero_stream_no
+@
+
+@<define stream defaults@>=
+max_default[stream_kind]=MAX_STREAM_DEFAULT;
+max_fixed[stream_kind]=zero_stream_no;
+@
+
+
+\subsection{Page Templates}
+
+The zero page template\index{template} is a predefined, built-in page template.
+@<default names@>=
+typedef enum {@+
+zero_page_no=0@+
+} page_no_t;
+#define MAX_PAGE_DEFAULT zero_page_no
+@
+
+@<define page defaults@>=
+max_default[page_kind]=MAX_PAGE_DEFAULT;
+max_fixed[page_kind]=zero_page_no;
+@
+
+\subsection{Page Ranges}
+
+The page\index{page range} range for the zero page template is
+the entire content section.
+
+@<default names@>=
+typedef enum {@+
+zero_range_no=0@+
+} range_no_t;
+#define MAX_RANGE_DEFAULT zero_range_no
+@
+
+@<define range defaults@>=
+max_default[range_kind]=MAX_RANGE_DEFAULT;
+max_fixed[range_kind]=zero_range_no;
+@
+
+
+\section{Content Section}
+The content section\index{content section} is just a list of nodes.
+Within the \.{shrink} program,
+reading a node in long format will trigger writing the node in short format.
+Similarly within the \.{stretch} program, reading a node
+in short form will cause writing it in long format. As a consequence,
+the main task of writing the content section in long format is accomplished
+by calling |get_content| and writing it in the short format
+is accomplished by parsing the |content_list|.
+
+%\readcode
+\codesection{\redsymbol}{Reading the Long Format}\redindex{1}{6}{Content Section}
+\label{content}%
+@s CONTENT symbol
+@<symbols@>=
+%token CONTENT "content"
+@
+
+@<scanning rules@>=
+::@=content@> :< return CONTENT; >:
+@
+
+
+@<parsing rules@>=
+content_section: START CONTENT { hput_content_start(); } @| content_list END @|
+ { hput_content_end(); hput_range_defs(); hput_label_defs(); };
+@
+
+%\writecode
+\codesection{\wrtsymbol}{Writing the Long Format}\wrtindex{1}{6}{Content Section}
+
+@<write functions@>=
+void hwrite_content_section(void)
+{ section_no=2;
+ hwritef("<content");
+ hsort_ranges();
+ hsort_labels();
+ hget_content_section();
+ hwritef("\n>\n");
+}
+@
+
+%\getcode
+\codesection{\getsymbol}{Reading the Short Format}\getindex{1}{6}{Content Section}
+@<get functions@>=
+void hget_content_section()
+{ DBG(DBGBASIC|DBGDIR,"Content\n");
+ hget_section(2);
+ hwrite_range();
+ hwrite_label();
+ while(hpos<hend)
+ hget_content_node();
+}
+@
+
+%\putcode
+\codesection{\putsymbol}{Writing the Short Format}\putindex{1}{6}{Content Section}
+@<put functions@>=
+void hput_content_start(void)
+{ DBG(DBGDIR,"Content Section\n");
+ section_no=2;
+ hpos0=hpos=hstart=dir[2].buffer;
+ hend=hstart+dir[2].bsize;
+
+}
+void hput_content_end(void)
+{
+ dir[2].size=hpos-hstart; /* Updating the directory entry */
+ DBG(DBGDIR,"End Content Section, size=0x%x\n", dir[2].size);
+}
+@
+
+
+\section{Processing the Command Line}
+The following code explains the command line\index{command line}
+parameters and options\index{option}\index{debugging}.
+It tells us what to expect in the rest of this section.
+{\def\SP{\hskip .5em}
+@<explain usage@>=
+ fprintf(stderr,
+ "Usage: %s [options] filename%s\n",prog_name, in_ext);@/
+ fprintf(stderr,
+ "Options:\n"@/
+ "\t -o file\t specify an output file name\n"@/
+ "\t -g \t assume global names for auxiliary files\n"@/
+ "\t -l \t redirect stderr to a log file\n"@/
+ "\t -u \t enable writing utf8 character codes\n"@/
+ "\t -x \t enable writing hexadecimal character codes\n"@/
+ "\t -c \t enable compression of section 1 and 2\n");@/
+#ifdef DEBUG
+fprintf(stderr,"\t -d XXX \t hexadecimal value. OR together these values:\n");@/
+fprintf(stderr,"\t\t\t XX=%03X basic debugging\n", DBGBASIC);@/
+fprintf(stderr,"\t\t\t XX=%03X tag debugging\n", DBGTAGS);@/
+fprintf(stderr,"\t\t\t XX=%03X node debugging\n",DBGNODE);@/
+fprintf(stderr,"\t\t\t XX=%03X definition debugging\n", DBGDEF);@/
+fprintf(stderr,"\t\t\t XX=%03X directory debugging\n", DBGDIR);@/
+fprintf(stderr,"\t\t\t XX=%03X range debugging\n",DBGRANGE);@/
+fprintf(stderr,"\t\t\t XX=%03X float debugging\n", DBGFLOAT);@/
+fprintf(stderr,"\t\t\t XX=%03X compression debugging\n", DBGCOMPRESS);@/
+fprintf(stderr,"\t\t\t XX=%03X buffer debugging\n", DBGBUFFER);@/
+fprintf(stderr,"\t\t\t XX=%03X flex debugging\n", DBGFLEX);@/
+fprintf(stderr,"\t\t\t XX=%03X bison debugging\n", DBGBISON);@/
+fprintf(stderr,"\t\t\t XX=%03X TeX debugging\n", DBGTEX);@/
+fprintf(stderr,"\t\t\t XX=%03X Page debugging\n", DBGPAGE);@/
+fprintf(stderr,"\t\t\t XX=%03X Font debugging\n", DBGFONT);@/
+fprintf(stderr,"\t\t\t XX=%03X Render debugging\n", DBGRENDER);@/
+fprintf(stderr,"\t\t\t XX=%03X Label debugging\n", DBGLABEL);@/
+#endif
+@
+}
+We define constants for different debug flags.
+@<debug constants@>=
+#define DBGNONE 0x0
+#define DBGBASIC 0x1
+#define DBGTAGS 0x2
+#define DBGNODE 0x4
+#define DBGDEF 0x8
+#define DBGDIR 0x10
+#define DBGRANGE 0x20
+#define DBGFLOAT 0x40
+#define DBGCOMPRESS 0x80
+#define DBGBUFFER 0X100
+#define DBGFLEX 0x200
+#define DBGBISON 0x400
+#define DBGTEX 0x800
+#define DBGPAGE 0x1000
+#define DBGFONT 0x2000
+#define DBGRENDER 0x4000
+#define DBGLABEL 0x8000
+@
+
+Next we define common variables that are
+needed in all three programs defined here.
+
+@<common variables@>=
+unsigned int debugflags=DBGNONE;
+int option_utf8=false;
+int option_hex=false;
+int option_force=false;
+int option_global=false;
+int option_compress=false;
+char *stem_name=NULL;
+int stem_length=0;
+@
+The variable |stem_name| contains the name of the input file
+not including the extension. The space allocated for it
+is large enough to append an extension with up to five characters.
+It can be used with the extension {\tt .log} for the log file,
+with {\tt .HINT} or {\tt .hnt} for the output file,
+and with {\tt .abs} or {\tt .rel} when writing or reading the auxiliary sections.
+The {\tt stretch} program will overwrite the |stem_name|
+using the name of the output file if it is set with the {\tt -o}
+option.
+
+
+Next are the variables that are local in the |main| program.
+@<local variables in |main|@>=
+char *prog_name;
+char *in_ext;
+char *out_ext;
+char *file_name=NULL;
+int file_name_length=0;
+int option_log=false;
+@
+
+Processing the command line looks for options and then sets the
+input file name\index{file name}.
+
+@<process the command line@>=
+ debugflags=DBGBASIC;
+ prog_name=argv[0];
+ if (argc < 2) goto explain_usage;
+ argv++; /* skip the program name */
+ while (*argv!=NULL)
+ { if ((*argv)[0]=='-')
+ { char option=(*argv)[1];
+ switch(option)
+ { default: goto explain_usage;
+ case 'o': argv++;
+ file_name_length=(int)strlen(*argv);
+ ALLOCATE(file_name,file_name_length+6,char); /*plus extension*/
+ strcpy(file_name,*argv);@+ break;
+ case 'l': option_log=true; @+break;
+ case 'u': option_utf8=true;@+break;
+ case 'x': option_hex=true;@+break;
+ case 'f': option_force=true; @+break;
+ case 'g': option_global=true; @+break;
+ case 'c': option_compress=true; @+break;
+ case 'd': @/
+ argv++; if (*argv==NULL) goto explain_usage;
+ debugflags=strtol(*argv,NULL,16);
+ break;
+ }
+ }
+ else /* the input file name */
+ { int path_length=(int)strlen(*argv);
+ int ext_length=(int)strlen(in_ext);
+ ALLOCATE(hin_name,path_length+ext_length+1,char);
+ strcpy(hin_name,*argv);
+ if (path_length<ext_length
+ || strncmp(hin_name+path_length-ext_length,in_ext,ext_length)!=0)
+ { strcat(hin_name,in_ext);
+ path_length+=ext_length;
+ }
+ stem_length=path_length-ext_length;
+ ALLOCATE(stem_name,stem_length+6,char);
+ strncpy(stem_name,hin_name,stem_length);
+ stem_name[stem_length]=0;
+ if (*(argv+1)!=NULL) goto explain_usage;
+ }
+ argv++;
+ }
+ if (hin_name==NULL) goto explain_usage;
+@
+
+After the command line has been processed, three file streams need to be opened:
+The input file |hin|\index{input file} and the output file |hout|\index{output file}.
+Further we need a log file |hlog|\index{log file} if debugging is enabled.
+For technical reasons, the scanner\index{scanning} generated by \.{flex} needs
+an input file |yyin|\index{input file} which is set to |hin|
+and an output file |yyout| (which is not used).
+
+@<common variables@>=
+FILE *hin=NULL, *hout=NULL, *hlog=NULL;
+@
+
+
+The log file is opened first because
+this is the place where error messages\index{error message}
+should go while the other files are opened.
+It inherits its name from the input file name.
+
+@<open the log file@> =
+#ifdef DEBUG
+ if (option_log)
+ {
+ strcat(stem_name,".log");
+ hlog=freopen(stem_name,"w",stderr);
+ if (hlog==NULL)
+ { fprintf(stderr,"Unable to open logfile %s",stem_name);
+ hlog=stderr;
+ }
+ stem_name[stem_length]=0;
+ }
+ else
+ hlog=stderr;
+#else
+ hlog=stderr;
+#endif
+@
+
+Once we have established logging, we can try to open the other files.
+@<open the input file@>=
+ hin=fopen(hin_name,"rb");
+ if (hin==NULL) QUIT("Unable to open input file %s",hin_name);
+@
+
+@<open the output file@>=
+ if (file_name!=NULL)
+ { int ext_length=(int)strlen(out_ext);
+ if (file_name_length<=ext_length
+ || strncmp(file_name+file_name_length-ext_length,out_ext,ext_length)!=0)
+ { strcat(file_name,out_ext); file_name_length+=ext_length; }
+ }
+ else
+ { file_name_length=stem_length+(int)strlen(out_ext);
+ ALLOCATE(file_name,file_name_length+1,char);
+ strcpy(file_name,stem_name);@+
+ strcpy(file_name+stem_length,out_ext);
+ }
+ @<make sure the path in |file_name| exists@>@;
+ hout=fopen(file_name,"wb");
+ if (hout==NULL) QUIT("Unable to open output file %s",file_name);
+@
+
+The {\tt stretch} program will replace the |stem_name| using the stem of the
+output file.
+@<determine the |stem_name| from the output |file_name|@>=
+stem_length=file_name_length-(int)strlen(out_ext);
+ALLOCATE(stem_name,stem_length+6,char);
+strncpy(stem_name,file_name,stem_length);
+stem_name[stem_length]=0;
+@
+
+At the very end, we will close the files again.
+@<close the input file@>=
+if (hin_name!=NULL) free(hin_name);
+if (hin!=NULL) fclose(hin);
+@
+@<close the output file@>=
+if (file_name!=NULL) free(file_name);
+if (hout!=NULL) fclose(hout);
+@
+@<close the log file@>=
+if (hlog!=NULL) fclose(hlog);
+if (stem_name!=NULL) free(stem_name);
+@
+
+
+
+\section{Error Handling and Debugging}\label{error_section}
+There is no good program without good error handling\index{error message}\index{debugging}.
+To print messages\index{message} or indicate errors, I define the following macros:
+\index{MESSAGE+\.{MESSAGE}}\index{QUIT+\.{QUIT}}
+
+@(error.h@>=
+#ifndef _ERROR_H
+#define _ERROR_H
+#include <stdlib.h>
+#include <stdio.h>
+extern FILE *hlog;
+extern uint8_t *hpos, *hstart;
+#define @[LOG(...)@] @[(fprintf(hlog,__VA_ARGS__),fflush(hlog))@]
+#define @[MESSAGE(...)@] @[(fprintf(hlog,__VA_ARGS__),fflush(hlog))@]
+#define @[QUIT(...)@] (MESSAGE("ERROR: " __VA_ARGS__),fprintf(hlog,"\n"),exit(1))
+
+#endif
+@
+
+
+The amount of debugging\index{debugging} depends on the debugging flags.
+For portability, we first define the output specifier for expressions of type |size_t|.
+\index{DBG+\.{DBG}}\index{SIZE F+\.{SIZE\_F}}\index{DBGTAG+\.{DBGTAG}}
+\index{RNG+\.{RNG}}\index{TAGERR+\.{TAGERR}}
+@<debug macros@>=
+#ifdef WIN32
+#define SIZE_F "0x%x"
+#else
+#define SIZE_F "0x%zx"
+#endif
+#ifdef DEBUG
+#define @[DBG(FLAGS,...)@] ((debugflags & (FLAGS))?LOG(__VA_ARGS__):0)
+#else
+#define @[DBG(FLAGS,...)@] 0
+#endif
+#define @[DBGTAG(A,P)@] @[DBG(DBGTAGS,@["tag [%s,%d] at " SIZE_F "\n"@],@|NAME(A),INFO(A),(P)-hstart)@]
+
+#define @[RNG(S,N,A,Z)@] @/\
+ if ((int)(N)<(int)(A)||(int)(N)>(int)(Z)) QUIT(S@, " %d out of range [%d - %d]",N,A,Z)
+
+#define @[TAGERR(A)@] @[QUIT(@["Unknown tag [%s,%d] at " SIZE_F "\n"@],NAME(A),INFO(A),hpos-hstart)@]
+@
+
+The \.{bison} generated parser will need a function |yyerror| for
+error reporting. We can define it now:
+
+@<parsing functions@>=
+extern int yylineno;
+int yyerror(const char *msg)
+{ QUIT(" in line %d %s",yylineno,msg);
+ return 0;
+}
+@
+
+To enable the generation of debugging code \.{bison} needs also the following:
+@<enable bison debugging@>=
+#ifdef DEBUG
+#define YYDEBUG 1
+extern int yydebug;
+#else
+#define YYDEBUG 0
+#endif
+@
+
+
+\appendix
+
+\section{Traversing Short Format Files}\label{fastforward}
+For applications like searching or repositioning a file after reloading
+a possibly changed version of a file, it is useful to have a fast way
+of getting from one content node to the next.
+For quite some nodes, it is possible to know the size of the
+node from the tag. So the fastest way to get to the next node
+is looking up the node size in a table.
+
+Other important nodes, for example hbox, vbox, or par nodes, end with a
+list node and it is possible to know the size of the node up to the final
+list. With that knowledge it is possible to skip the initial
+part of the node, then skip the list, and finally skip the tag byte.
+The size of the initial part can be stored in the same node size table
+using negated values. What works for lists,
+of course, will work for other kinds of nodes as well.
+So we use the lowest two bits of the values in the size table
+to store the number of embedded nodes that follow after the initial part.
+
+For list nodes neither of these methods works and these nodes can be marked
+with a zero entry in the node size table.
+
+This leads to the following code for a ``fast forward'' function
+for |hpos|:
+
+@<shared skip functions@>=
+uint32_t hff_list_pos=0, hff_list_size=0;
+uint8_t hff_tag;
+void hff_hpos(void)
+{ signed char i,k;
+ hff_tag=*hpos;@+
+ DBGTAG(hff_tag,hpos);
+ i= hnode_size[hff_tag];
+ if (i>0) {hpos=hpos+i; @+return;@+ }
+ else if (i<0)
+ { k=1+(i&0x3);@+ i=i>>2;
+ hpos=hpos-i; /* skip initial part */
+ while (k>0)
+ { hff_hpos(); @+k--; @+} /* skip trailing nodes */
+ hpos++;/* skip end byte */
+ return;
+ }
+ else if (hff_tag <=TAG(param_kind,5))
+ @<advance |hpos| over a list@>@;
+ TAGERR(hff_tag);
+}
+@
+
+
+We will put the |hnode_size| variable into the {\tt hformat.c} file
+using the following function. We add some comments and
+split negative values into their components, to make the result more
+readable.
+
+@<print the |hnode_size| variable@>=
+ printf("signed char hnode_size[0x100]= {\n");
+ for (i=0; i<=0xff; i++)@/
+ { signed char s = hnode_size[i];
+ if (s>=0) printf("%d",s); else printf("-4*%d+%d",-(s>>2),s&3);
+ if (i<0xff) printf(","); else printf("};");
+ if ((i&0x7)==0x7) printf(" /* %s */\n", content_name[KIND(i)]);
+ }
+ printf("\n\n");
+@
+
+\subsection{Lists}\index{list}\index{text}\index{parameters}
+List don't follow the usual schema of nodes. They have a variable size
+that is stored in the node. We keep position and size in global variables
+so that the list that ends a node can be conveniently located.
+
+@<advance |hpos| over a list@>=
+switch (INFO(hff_tag)){
+case 1: hff_list_pos=hpos-hstart+1;hff_list_size=0; hpos=hpos+2;@+ return;
+case 2: hpos++;@+ hff_list_size=HGET8;@+ hff_list_pos=hpos-hstart+1; hpos=hpos+1+hff_list_size+1+1+1;@+ return;
+case 3: hpos++;@+ HGET16(hff_list_size);@+hff_list_pos=hpos-hstart+1; hpos=hpos+1+hff_list_size+1+2+1;@+ return;
+case 4: hpos++;@+ HGET24(hff_list_size);@+hff_list_pos=hpos-hstart+1; hpos=hpos+1+hff_list_size+1+3+1;@+ return;
+case 5: hpos++;@+ HGET32(hff_list_size);@+hff_list_pos=hpos-hstart+1; hpos=hpos+1+hff_list_size+1+4+1;@+ return;
+}
+@
+
+Now let's consider the different kinds of nodes.
+
+\subsection{Glyphs}\index{glyph}
+We start with the glyph nodes. All glyph nodes
+have a start and an end tag, one byte for the font,
+and depending on the info from 1 to 4 bytes for the character code.
+
+@<initialize the |hnode_size| array@>=
+hnode_size[TAG(glyph_kind,1)] = 1+1+1+1;
+hnode_size[TAG(glyph_kind,2)] = 1+1+2+1;
+hnode_size[TAG(glyph_kind,3)] = 1+1+3+1;
+hnode_size[TAG(glyph_kind,4)] = 1+1+4+1;
+@
+
+\subsection{Penalties}\index{penalty}
+Penalty nodes either contain a one byte reference, a one byte number, or a two byte number.
+
+@<initialize the |hnode_size| array@>=
+hnode_size[TAG(penalty_kind,0)] = 1+1+1;
+hnode_size[TAG(penalty_kind,1)] = 1+1+1;
+hnode_size[TAG(penalty_kind,2)] = 1+2+1;
+@
+
+\subsection{Kerns}\index{kern}
+Kern nodes can contain a reference (either to a dimension or an extended dimension)
+a dimension, or an extended dimension node.
+
+@<initialize the |hnode_size| array@>=
+hnode_size[TAG(kern_kind,b000)] = 1+1+1;
+hnode_size[TAG(kern_kind,b001)] = 1+1+1;
+hnode_size[TAG(kern_kind,b010)] = 1+4+1;
+hnode_size[TAG(kern_kind,b011)] = I_T(1,1);
+hnode_size[TAG(kern_kind,b100)] = 1+1+1;
+hnode_size[TAG(kern_kind,b101)] = 1+1+1;
+hnode_size[TAG(kern_kind,b110)] = 1+4+1;
+hnode_size[TAG(kern_kind,b111)] = I_T(1,1);
+@
+
+For the two cases where a kern node contains an extended dimension,
+we use the following macro to combine the size of the initial part
+with the number of trailing nodes:
+@<skip macros@>=
+#define @[I_T(I,T)@] (((-(I))<<2)|((T)-1))
+@
+
+\subsection{Extended Dimensions}\index{extended dimension}
+Extended dimensions contain either one two or three 4 byte values depending
+on the info bits.
+@<initialize the |hnode_size| array@>=
+hnode_size[TAG(xdimen_kind,b100)] = 1+4+1;
+hnode_size[TAG(xdimen_kind,b010)] = 1+4+1;
+hnode_size[TAG(xdimen_kind,b001)] = 1+4+1;
+hnode_size[TAG(xdimen_kind,b110)] = 1+4+4+1;
+hnode_size[TAG(xdimen_kind,b101)] = 1+4+4+1;
+hnode_size[TAG(xdimen_kind,b011)] = 1+4+4+1;
+hnode_size[TAG(xdimen_kind,b111)] = 1+4+4+4+1;
+@
+
+\subsection{Language}\index{language}
+Language nodes either code the language in the info value or they contain
+a reference byte.
+@<initialize the |hnode_size| array@>=
+hnode_size[TAG(language_kind,b000)] = 1+1+1;
+hnode_size[TAG(language_kind,1)] = 1+1;
+hnode_size[TAG(language_kind,2)] = 1+1;
+hnode_size[TAG(language_kind,3)] = 1+1;
+hnode_size[TAG(language_kind,4)] = 1+1;
+hnode_size[TAG(language_kind,5)] = 1+1;
+hnode_size[TAG(language_kind,6)] = 1+1;
+hnode_size[TAG(language_kind,7)] = 1+1;
+@
+
+\subsection{Rules}\index{rule}
+Rules usually contain a reference, otherwise
+they contain either one, two, or three 4 byte values depending
+on the info bits.
+@<initialize the |hnode_size| array@>=
+hnode_size[TAG(rule_kind,b000)] = 1+1+1;
+hnode_size[TAG(rule_kind,b100)] = 1+4+1;
+hnode_size[TAG(rule_kind,b010)] = 1+4+1;
+hnode_size[TAG(rule_kind,b001)] = 1+4+1;
+hnode_size[TAG(rule_kind,b110)] = 1+4+4+1;
+hnode_size[TAG(rule_kind,b101)] = 1+4+4+1;
+hnode_size[TAG(rule_kind,b011)] = 1+4+4+1;
+hnode_size[TAG(rule_kind,b111)] = 1+4+4+4+1;
+@
+
+\subsection{Glue}\index{glue}
+Glues usually contain a reference or
+they contain either one two or three 4 byte values depending
+on the info bits, and possibly even an extended dimension node followed
+by two 4 byte values.
+@<initialize the |hnode_size| array@>=
+hnode_size[TAG(glue_kind,b000)] = 1+1+1;
+hnode_size[TAG(glue_kind,b100)] = 1+4+1;
+hnode_size[TAG(glue_kind,b010)] = 1+4+1;
+hnode_size[TAG(glue_kind,b001)] = 1+4+1;
+hnode_size[TAG(glue_kind,b110)] = 1+4+4+1;
+hnode_size[TAG(glue_kind,b101)] = 1+4+4+1;
+hnode_size[TAG(glue_kind,b011)] = 1+4+4+1;
+hnode_size[TAG(glue_kind,b111)] = I_T(1+4+4,1);
+@
+
+
+\subsection{Boxes}\index{box}
+The layout of boxes is quite complex and explained in section~\secref{boxnodes}.
+All boxes contain height and width, some contain a depth, some a shift amount,
+and some a glue setting together with glue sign and glue order.
+The last item in a box is a node list.
+
+@<initialize the |hnode_size| array@>=
+hnode_size[TAG(hbox_kind,b000)] = I_T(1+4+4,1); /* tag, height, width*/
+hnode_size[TAG(hbox_kind,b001)] = I_T(1+4+4+4,1); /* and depth */
+hnode_size[TAG(hbox_kind,b010)] = I_T(1+4+4+4,1); /* or shift */
+hnode_size[TAG(hbox_kind,b011)] = I_T(1+4+4+4+4,1); /* or both */
+hnode_size[TAG(hbox_kind,b100)] = I_T(1+4+4+5,1); /* and glue setting*/
+hnode_size[TAG(hbox_kind,b101)] = I_T(1+4+4+4+5,1); /* and depth */
+hnode_size[TAG(hbox_kind,b110)] = I_T(1+4+4+4+5,1); /* or shift */
+hnode_size[TAG(hbox_kind,b111)] = I_T(1+4+4+4+4+5,1); /*or both */
+hnode_size[TAG(vbox_kind,b000)] = I_T(1+4+4,1); /* same for vbox*/
+hnode_size[TAG(vbox_kind,b001)] = I_T(1+4+4+4,1);
+hnode_size[TAG(vbox_kind,b010)] = I_T(1+4+4+4,1);
+hnode_size[TAG(vbox_kind,b011)] = I_T(1+4+4+4+4,1);
+hnode_size[TAG(vbox_kind,b100)] = I_T(1+4+4+5,1);
+hnode_size[TAG(vbox_kind,b101)] = I_T(1+4+4+4+5,1);
+hnode_size[TAG(vbox_kind,b110)] = I_T(1+4+4+4+5,1);
+hnode_size[TAG(vbox_kind,b111)] = I_T(1+4+4+4+4+5,1);
+@
+
+\subsection{Extended Boxes}\index{extended box}
+Extended boxes start with height, width, depth, stretch, or shrink components.
+Then follows an extended dimension either as a reference or a node.
+The node ends with a list.
+@<initialize the |hnode_size| array@>=
+hnode_size[TAG(hset_kind,b000)] = I_T(1+4+4+4+4+1,1);
+hnode_size[TAG(hset_kind,b001)] = I_T(1+4+4+4+4+4+1,1);
+hnode_size[TAG(hset_kind,b010)] = I_T(1+4+4+4+4+4+1,1);
+hnode_size[TAG(hset_kind,b011)] = I_T(1+4+4+4+4+4+4+1,1);
+hnode_size[TAG(vset_kind,b000)] = I_T(1+4+4+4+4+1,1);
+hnode_size[TAG(vset_kind,b001)] = I_T(1+4+4+4+4+4+1,1);
+hnode_size[TAG(vset_kind,b010)] = I_T(1+4+4+4+4+4+1,1);
+hnode_size[TAG(vset_kind,b011)] = I_T(1+4+4+4+4+4+4+1,1);
+
+hnode_size[TAG(hset_kind,b100)] = I_T(1+4+4+4+4,2);
+hnode_size[TAG(hset_kind,b101)] = I_T(1+4+4+4+4+4,2);
+hnode_size[TAG(hset_kind,b110)] = I_T(1+4+4+4+4+4,2);
+hnode_size[TAG(hset_kind,b111)] = I_T(1+4+4+4+4+4+4,2);
+hnode_size[TAG(vset_kind,b100)] = I_T(1+4+4+4+4,2);
+hnode_size[TAG(vset_kind,b101)] = I_T(1+4+4+4+4+4,2);
+hnode_size[TAG(vset_kind,b110)] = I_T(1+4+4+4+4+4,2);
+hnode_size[TAG(vset_kind,b111)] = I_T(1+4+4+4+4+4+4,2);
+@
+
+The hpack and vpack nodes start with a shift amount and in case of vpack a depth.
+Then again an extended dimension and a list.
+@<initialize the |hnode_size| array@>=
+hnode_size[TAG(hpack_kind,b000)] = I_T(1+1,1);
+hnode_size[TAG(hpack_kind,b001)] = I_T(1+1,1);
+hnode_size[TAG(hpack_kind,b010)] = I_T(1+4+1,1);
+hnode_size[TAG(hpack_kind,b011)] = I_T(1+4+1,1);
+hnode_size[TAG(vpack_kind,b000)] = I_T(1+4+1,1);
+hnode_size[TAG(vpack_kind,b001)] = I_T(1+4+1,1);
+hnode_size[TAG(vpack_kind,b010)] = I_T(1+4+4+1,1);
+hnode_size[TAG(vpack_kind,b011)] = I_T(1+4+4+1,1);
+
+hnode_size[TAG(hpack_kind,b100)] = I_T(1,2);
+hnode_size[TAG(hpack_kind,b101)] = I_T(1,2);
+hnode_size[TAG(hpack_kind,b110)] = I_T(1+4,2);
+hnode_size[TAG(hpack_kind,b111)] = I_T(1+4,2);
+hnode_size[TAG(vpack_kind,b100)] = I_T(1+4,2);
+hnode_size[TAG(vpack_kind,b101)] = I_T(1+4,2);
+hnode_size[TAG(vpack_kind,b110)] = I_T(1+4+4,2);
+hnode_size[TAG(vpack_kind,b111)] = I_T(1+4+4,2);
+@
+
+\subsection{Leaders}\index{leaders}
+Most leader nodes will use a reference.
+Otherwise they contain a glue node followed by a box or rule node.
+@<initialize the |hnode_size| array@>=
+hnode_size[TAG(leaders_kind,b000)] = 1+1+1;
+hnode_size[TAG(leaders_kind,1)] = I_T(1,1);
+hnode_size[TAG(leaders_kind,2)] = I_T(1,1);
+hnode_size[TAG(leaders_kind,3)] = I_T(1,1);
+hnode_size[TAG(leaders_kind,b100|1)] = I_T(1,2);
+hnode_size[TAG(leaders_kind,b100|2)] = I_T(1,2);
+hnode_size[TAG(leaders_kind,b100|3)] = I_T(1,2);
+@
+
+\subsection{Baseline Skips}\index{baseline skip}
+Here we expect either a reference or two optional glue nodes followed by an optional dimension.
+@<initialize the |hnode_size| array@>=
+hnode_size[TAG(baseline_kind,b000)] = 1+1+1;
+hnode_size[TAG(baseline_kind,b001)] = 1+4+1;
+hnode_size[TAG(baseline_kind,b010)] = I_T(1,1);
+hnode_size[TAG(baseline_kind,b100)] = I_T(1,1);
+hnode_size[TAG(baseline_kind,b110)] = I_T(1,2);
+
+hnode_size[TAG(baseline_kind,b011)] = I_T(1+4,1);
+hnode_size[TAG(baseline_kind,b101)] = I_T(1+4,1);
+hnode_size[TAG(baseline_kind,b111)] = I_T(1+4,2);
+@
+
+
+\subsection{Ligatures}\index{ligature}
+As usual a reference is possible, otherwise the font is followed by character bytes
+as given by the info. Only if the info value is 7, the number of character bytes
+is stored separately.
+@<initialize the |hnode_size| array@>=
+hnode_size[TAG(ligature_kind,b000)] = 1+1+1;
+hnode_size[TAG(ligature_kind,1)] = 1+1+1+1;
+hnode_size[TAG(ligature_kind,2)] = 1+1+2+1;
+hnode_size[TAG(ligature_kind,3)] = 1+1+3+1;
+hnode_size[TAG(ligature_kind,4)] = 1+1+4+1;
+hnode_size[TAG(ligature_kind,5)] = 1+1+5+1;
+hnode_size[TAG(ligature_kind,6)] = 1+1+6+1;
+hnode_size[TAG(ligature_kind,7)] = I_T(1+1,1);
+@
+
+\subsection{Discretionary breaks}\index{discretionary break}
+The simple cases here are references, discretionary breaks
+with empty pre- and post-list, or with a zero line skip limit
+Otherwise one or two lists are followed by an optional replace count.
+
+@<initialize the |hnode_size| array@>=
+hnode_size[TAG(disc_kind,b000)] = 1+1+1;
+hnode_size[TAG(disc_kind,b010)] = I_T(1,1);
+hnode_size[TAG(disc_kind,b011)] = I_T(1,2);
+hnode_size[TAG(disc_kind,b100)] = 1+1+1;
+hnode_size[TAG(disc_kind,b110)] = I_T(1+1,1);
+hnode_size[TAG(disc_kind,b111)] = I_T(1+1,2);
+@
+
+\subsection{Paragraphs}\index{paragraph}
+Paragraph nodes contain an extended dimension, an parameter list and a list.
+The first two can be given as a reference.
+@<initialize the |hnode_size| array@>=
+hnode_size[TAG(par_kind,b000)] = I_T(1+1+1,1);
+hnode_size[TAG(par_kind,b010)] = I_T(1+1,2);
+hnode_size[TAG(par_kind,b110)] = I_T(1,3);
+hnode_size[TAG(par_kind,b100)] = I_T(1+1,2);
+@
+
+\subsection{Mathematics}\index{mathematics}\index{displayed formula}
+Displayed math needs a parameter list, either as list or as reference
+followed by an optional left or right equation number and a list.
+Text math is simpler: the only information is in the info value.
+@<initialize the |hnode_size| array@>=
+hnode_size[TAG(math_kind,b000)] = I_T(1+1,1);
+hnode_size[TAG(math_kind,b001)] = I_T(1+1,2);
+hnode_size[TAG(math_kind,b010)] = I_T(1+1,2);
+hnode_size[TAG(math_kind,b100)] = I_T(1,2);
+hnode_size[TAG(math_kind,b101)] = I_T(1,3);
+hnode_size[TAG(math_kind,b110)] = I_T(1,3);
+hnode_size[TAG(math_kind,b111)] = 1+1;
+hnode_size[TAG(math_kind,b011)] = 1+1;
+@
+
+\subsection{Adjustments}\index{adjustment}
+@<initialize the |hnode_size| array@>=
+hnode_size[TAG(adjust_kind,1)] = I_T(1,1);
+@
+
+\subsection{Tables}\index{alignment}
+Tables have an extended dimension either as a node or as a reference followed
+by two lists.
+@<initialize the |hnode_size| array@>=
+hnode_size[TAG(table_kind,b000)] = I_T(1+1,2);
+hnode_size[TAG(table_kind,b001)] = I_T(1+1,2);
+hnode_size[TAG(table_kind,b010)] = I_T(1+1,2);
+hnode_size[TAG(table_kind,b011)] = I_T(1+1,2);
+hnode_size[TAG(table_kind,b100)] = I_T(1,3);
+hnode_size[TAG(table_kind,b101)] = I_T(1,3);
+hnode_size[TAG(table_kind,b110)] = I_T(1,3);
+hnode_size[TAG(table_kind,b111)] = I_T(1,3);
+@
+Outer item nodes are lists of inner item nodes, inner item nodes are box nodes
+followed by an optional span count.
+@<initialize the |hnode_size| array@>=
+hnode_size[TAG(item_kind,b000)] = I_T(1,1); /* outer */
+hnode_size[TAG(item_kind,1)] = I_T(1,1); /* inner */
+hnode_size[TAG(item_kind,2)] = I_T(1,1);
+hnode_size[TAG(item_kind,3)] = I_T(1,1);
+hnode_size[TAG(item_kind,4)] = I_T(1,1);
+hnode_size[TAG(item_kind,5)] = I_T(1,1);
+hnode_size[TAG(item_kind,6)] = I_T(1,1);
+hnode_size[TAG(item_kind,6)] = I_T(2,1);
+@
+
+\subsection{Images}\index{image}
+If not given by a reference, images contain a section reference and optional dimensions, stretch, and shrink.
+@<initialize the |hnode_size| array@>=
+hnode_size[TAG(image_kind,b000)] = 1+1+1;
+hnode_size[TAG(image_kind,b100)] = 1+2+1;
+hnode_size[TAG(image_kind,b101)] = 1+2+4+4+1;
+hnode_size[TAG(image_kind,b110)] = 1+2+4+4+1;
+hnode_size[TAG(image_kind,b111)] = 1+2+4+4+4+4+1;
+@
+
+\subsection{Links}\index{link}
+Links contain either a 2 byte or a 1 byte reference.
+@<initialize the |hnode_size| array@>=
+hnode_size[TAG(link_kind,b000)] = 1+1+1;
+hnode_size[TAG(link_kind,b001)] = 1+2+1;
+hnode_size[TAG(link_kind,b010)] = 1+1+1;
+hnode_size[TAG(link_kind,b011)] = 1+2+1;
+@
+
+\subsection{Stream Nodes}\index{stream}
+After the stream reference follows a parameter list, either as reference
+or as a list, and then a content list.
+@<initialize the |hnode_size| array@>=
+hnode_size[TAG(stream_kind,b000)] = I_T(1+1+1,1);
+hnode_size[TAG(stream_kind,b010)] = I_T(1+1,2);
+@
+
+
+\section{Reading Short Format Files Backwards}
+This section is not really part of the file format definition, but it
+illustrates an important property of the content section in short
+format files: it can be read in both directions. This is important
+because we want to be able to start at an arbitrary point in the
+content and from there move pagewise backward.
+
+The program {\tt skip}\index{skip+{\tt skip}} described in this
+section does just that. As wee see in appendix~\secref{skip}, its
+|main| program is almost the same as the |main| program of the program
+{\tt stretch} in appendix~\secref{stretchmain}.
+The major difference is the removal of an output file
+and the replacement of the call to |hwrite_content_section| by
+a call to |hteg_content_section|.
+
+@<skip functions@>=
+static void hteg_content_section(void)
+{ hget_section(2);
+ hpos=hend;
+ while(hpos>hstart)
+ hteg_content_node();
+}
+@
+
+The functions |hteg_content_section| and |hteg_content_node| above are
+reverse versions of the functions |hget_content_section| and
+|hget_content_node|. Many such ``reverse functions'' will follow now
+and we will consistently use the same naming scheme: replacing
+``{\it get\/}`` by ``{\it teg\/}'' or ``{\tt GET}'' by ``{\tt TEG}''.
+The {\tt skip} program does not do much input
+checking; it will just extract enough information from a content node
+to skip a node and ``advance'' or better ``retreat'' to the previous
+node.
+
+@<skip functions@>=
+static void hteg_content_node(void)
+{ @<skip the end byte |z|@>@;
+ hteg_content(z);
+ @<skip and check the start byte |a|@>@;
+}
+
+static void hteg_content(uint8_t z)
+{@+ switch (z)@/
+ {
+ @<cases to skip content@>@;@t\1@>@/
+ default:
+ TAGERR(z);
+ break;@t\2@>@/
+ }
+}
+@
+
+The code to skip the end\index{end byte} byte |z| and to check the start\index{start byte} byte |a| is used repeatedly.
+
+@<skip the end byte |z|@>=
+ uint8_t a,z; /* the start and the end byte*/
+ uint32_t node_pos=hpos-hstart;
+ if (hpos<=hstart) return;
+ HTEGTAG(z);
+@
+
+@<skip and check the start byte |a|@>=
+ HTEGTAG(a);
+ if (a!=z) QUIT(@["Tag mismatch [%s,%d]!=[%s,%d] at " SIZE_F " to 0x%x\n"@],@|NAME(a),INFO(a),NAME(z),INFO(z),@|
+ hpos-hstart,node_pos-1);
+@
+
+We replace the ``{\tt GET}'' macros by the following ``{\tt TEG}'' macros:
+
+@<shared get macros@>=
+#define @[HBACK(X)@] @[((hpos-(X)<hstart)?(QUIT("HTEG underflow\n"),NULL):(hpos-=(X)))@]
+
+#define @[HTEG8@] (HBACK(1),hpos[0])
+#define @[HTEG16(X)@] (HBACK(2),(X)=(hpos[0]<<8)+hpos[1])
+#define @[HTEG24(X)@] (HBACK(3),(X)=(hpos[0]<<16)+(hpos[1]<<8)+hpos[2])
+#define @[HTEG32(X)@] (HBACK(4),(X)=(hpos[0]<<24)+(hpos[1]<<16)+(hpos[2]<<8)+hpos[3])
+#define @[HTEGTAG(X)@] @[X=HTEG8,DBGTAG(X,hpos)@]
+@
+
+Now we review step by step the different kinds of nodes.
+\subsection{Floating Point Numbers}\index{floating point number}
+\noindent
+@<shared skip functions@>=
+float32_t hteg_float32(void)
+{ union {@+float32_t d; @+ uint32_t bits; @+} u;
+ HTEG32(u.bits);
+ return u.d;
+}
+@
+
+
+\subsection{Extended Dimensions}\index{extended dimension}
+\noindent
+@<skip macros@>=
+#define @[HTEG_XDIMEN(I,X)@] \
+ if((I)&b001) HTEG32((X).v); \
+ if((I)&b010) HTEG32((X).h);\
+ if((I)&b100) HTEG32((X).w);
+@
+
+@<skip functions@>=
+static void hteg_xdimen_node(xdimen_t *x)
+{ @<skip the end byte |z|@>@;
+ switch(z)
+ {
+#if 0
+/* currently the info value 0 is not supported */
+case TAG(xdimen_kind,b000): /* see section~\secref{reference} */
+ {uint8_t n;@+ n=HTEG8;} @+ break;
+#endif
+ case TAG(xdimen_kind,b001): HTEG_XDIMEN(b001,*x);@+break;
+ case TAG(xdimen_kind,b010): HTEG_XDIMEN(b010,*x);@+break;
+ case TAG(xdimen_kind,b011): HTEG_XDIMEN(b011,*x);@+break;
+ case TAG(xdimen_kind,b100): HTEG_XDIMEN(b100,*x);@+break;
+ case TAG(xdimen_kind,b101): HTEG_XDIMEN(b101,*x);@+break;
+ case TAG(xdimen_kind,b110): HTEG_XDIMEN(b110,*x);@+break;
+ case TAG(xdimen_kind,b111): HTEG_XDIMEN(b111,*x);@+break;
+ default:
+ QUIT("Extent expected at 0x%x got %s",node_pos,NAME(z)); @+ break;
+ }
+@<skip and check the start byte |a|@>@;
+}
+@
+
+
+\subsection{Stretch and Shrink}\index{stretchability}\index{shrinkability}
+\noindent
+@<skip macros@>=
+#define @[HTEG_STRETCH(S)@] { stch_t st; @+ HTEG32(st.u);@+ S.o=st.u&3;@+ st.u&=~3;@+ S.f=st.f; @+}
+@
+
+\subsection{Glyphs}\index{glyph}
+\noindent
+@<skip macros@>=
+#define HTEG_GLYPH(I,G) \
+ (G).f=HTEG8; \
+ if (I==1) (G).c=HTEG8;\
+ else if (I==2) HTEG16((G).c);\
+ else if (I==3) HTEG24((G).c);\
+ else if (I==4) HTEG32((G).c);
+@
+
+@<cases to skip content@>=
+@t\1\kern1em@>case TAG(glyph_kind,1): @+{@+glyph_t g;@+ HTEG_GLYPH(1,g);@+}@+break;
+case TAG(glyph_kind,2): @+{@+glyph_t g;@+ HTEG_GLYPH(2,g);@+}@+break;
+case TAG(glyph_kind,3): @+{@+glyph_t g;@+ HTEG_GLYPH(3,g);@+}@+break;
+case TAG(glyph_kind,4): @+{@+glyph_t g;@+ HTEG_GLYPH(4,g);@+}@+break;
+@
+
+
+\subsection{Penalties}\index{penalty}
+\noindent
+@<skip macros@>=
+#define @[HTEG_PENALTY(I,P)@] \
+if (I==1) {int8_t n; @+n=HTEG8; @+P=n;@+ } \
+else {int16_t n;@+ HTEG16(n); @+ P=n; @+}\
+@
+
+@<cases to skip content@>=
+@t\1\kern1em@>case TAG(penalty_kind,1): @+{int32_t p;@+ HTEG_PENALTY(1,p);@+} @+break;
+case TAG(penalty_kind,2): @+{int32_t p;@+ HTEG_PENALTY(2,p);@+} @+break;
+@
+
+
+\subsection{Kerns}\index{kern}
+\noindent
+@<skip macros@>=
+#define @[HTEG_KERN(I,X)@] @[if (((I)&b011)==2) HTEG32(X.w); else if (((I)&b011)==3) hteg_xdimen_node(&(X))@]
+@
+
+@<cases to skip content@>=
+@t\1\kern1em@>case TAG(kern_kind,b010): @+ {@+xdimen_t x; @+HTEG_KERN(b010,x);@+ } @+break;
+case TAG(kern_kind,b011): @+ {@+xdimen_t x; @+HTEG_KERN(b011,x);@+ } @+break;
+case TAG(kern_kind,b110): @+ {@+xdimen_t x; @+HTEG_KERN(b110,x);@+ } @+break;
+case TAG(kern_kind,b111): @+ {@+xdimen_t x; @+HTEG_KERN(b111,x);@+ } @+break;
+@
+
+\subsection{Language}\index{language}
+\noindent
+@<cases to skip content@>=
+@t\kern1em@>case TAG(language_kind,1):
+case TAG(language_kind,2):
+case TAG(language_kind,3):
+case TAG(language_kind,4):
+case TAG(language_kind,5):
+case TAG(language_kind,6):
+case TAG(language_kind,7):@+break;
+@
+
+\subsection{Rules}\index{rule}
+\noindent
+@<skip macros@>=
+#define @[HTEG_RULE(I,R)@]@/\
+if ((I)&b001) HTEG32((R).w); @+else (R).w=RUNNING_DIMEN;\
+if ((I)&b010) HTEG32((R).d); @+else (R).d=RUNNING_DIMEN;\
+if ((I)&b100) HTEG32((R).h); @+else (R).h=RUNNING_DIMEN;
+@
+
+@<cases to skip content@>=
+@t\1\kern1em@>case TAG(rule_kind,b011): @+ {rule_t r;@+ HTEG_RULE(b011,r);@+ }@+ break;
+case TAG(rule_kind,b101): @+ {rule_t r;@+ HTEG_RULE(b101,r);@+ }@+ break;
+case TAG(rule_kind,b001): @+ {rule_t r;@+ HTEG_RULE(b001,r);@+ }@+ break;
+case TAG(rule_kind,b110): @+ {rule_t r;@+ HTEG_RULE(b110,r);@+ }@+ break;
+case TAG(rule_kind,b111): @+ {rule_t r;@+ HTEG_RULE(b111,r);@+ }@+ break;
+@
+
+@<skip functions@>=
+static void hteg_rule_node(void)
+{ @<skip the end byte |z|@>@;
+ if (KIND(z)==rule_kind) { @+rule_t r; @+HTEG_RULE(INFO(z),r); @+}
+ else
+ QUIT("Rule expected at 0x%x got %s",node_pos,NAME(z));
+ @<skip and check the start byte |a|@>@;
+}
+@
+\subsection{Glue}\index{glue}
+\noindent
+@<skip macros@>=
+#define @[HTEG_GLUE(I,G)@] @/\
+ if(I==b111) hteg_xdimen_node(&((G).w)); else (G).w.h=(G).w.v=0.0;\
+ if((I)&b001) HTEG_STRETCH((G).m) @+else (G).m.f=0.0, (G).m.o=0; \
+ if((I)&b010) HTEG_STRETCH((G).p) @+else (G).p.f=0.0, (G).p.o=0;\
+ if((I)!=b111) { @+if ((I)&b100) HTEG32((G).w.w);@+ else (G).w.w=0;@+ }
+@
+
+@<cases to skip content@>=
+@t\1\kern1em@>case TAG(glue_kind,b001): @+{ glue_t g;@+ HTEG_GLUE(b001,g);@+}@+break;
+case TAG(glue_kind,b010): @+{ glue_t g;@+ HTEG_GLUE(b010,g);@+}@+break;
+case TAG(glue_kind,b011): @+{ glue_t g;@+ HTEG_GLUE(b011,g);@+}@+break;
+case TAG(glue_kind,b100): @+{ glue_t g;@+ HTEG_GLUE(b100,g);@+}@+break;
+case TAG(glue_kind,b101): @+{ glue_t g;@+ HTEG_GLUE(b101,g);@+}@+break;
+case TAG(glue_kind,b110): @+{ glue_t g;@+ HTEG_GLUE(b110,g);@+}@+break;
+case TAG(glue_kind,b111): @+{ glue_t g;@+ HTEG_GLUE(b111,g);@+}@+break;
+@
+
+@<skip functions@>=
+static void hteg_glue_node(void)
+{ @<skip the end byte |z|@>@;
+ if (INFO(z)==b000) HTEG_REF(glue_kind);
+ else
+ { @+glue_t g; @+HTEG_GLUE(INFO(z),g);@+}
+ @<skip and check the start byte |a|@>@;
+}
+@
+
+\subsection{Boxes}\index{box}
+\noindent
+@<skip macros@>=
+#define @[HTEG_BOX(I,B)@] \
+hteg_list(&(B.l));\
+if ((I)&b100) @/{ B.s=HTEG8; @+ B.r=hteg_float32();@+ B.o=B.s&0xF; @+B.s=B.s>>4;@+ }\
+else { B.r=0.0;@+ B.o=B.s=0;@+ }\
+if ((I)&b010) HTEG32(B.a); @+else B.a=0;\
+HTEG32(B.w);\
+if ((I)&b001) HTEG32(B.d); @+ else B.d=0;\
+HTEG32(B.h);\
+@
+
+@<cases to skip content@>=
+@t\1\kern1em@> case TAG(hbox_kind,b000): @+{box_t b; @+HTEG_BOX(b000,b);@+} @+ break;
+case TAG(hbox_kind,b001): @+{box_t b; @+HTEG_BOX(b001,b);@+} @+ break;
+case TAG(hbox_kind,b010): @+{box_t b; @+HTEG_BOX(b010,b);@+} @+ break;
+case TAG(hbox_kind,b011): @+{box_t b; @+HTEG_BOX(b011,b);@+} @+ break;
+case TAG(hbox_kind,b100): @+{box_t b; @+HTEG_BOX(b100,b);@+} @+ break;
+case TAG(hbox_kind,b101): @+{box_t b; @+HTEG_BOX(b101,b);@+} @+ break;
+case TAG(hbox_kind,b110): @+{box_t b; @+HTEG_BOX(b110,b);@+} @+ break;
+case TAG(hbox_kind,b111): @+{box_t b; @+HTEG_BOX(b111,b);@+} @+ break;
+case TAG(vbox_kind,b000): @+{box_t b; @+HTEG_BOX(b000,b);@+} @+ break;
+case TAG(vbox_kind,b001): @+{box_t b; @+HTEG_BOX(b001,b);@+} @+ break;
+case TAG(vbox_kind,b010): @+{box_t b; @+HTEG_BOX(b010,b);@+} @+ break;
+case TAG(vbox_kind,b011): @+{box_t b; @+HTEG_BOX(b011,b);@+} @+ break;
+case TAG(vbox_kind,b100): @+{box_t b; @+HTEG_BOX(b100,b);@+} @+ break;
+case TAG(vbox_kind,b101): @+{box_t b; @+HTEG_BOX(b101,b);@+} @+ break;
+case TAG(vbox_kind,b110): @+{box_t b; @+HTEG_BOX(b110,b);@+} @+ break;
+case TAG(vbox_kind,b111): @+{box_t b; @+HTEG_BOX(b111,b);@+} @+ break;
+@
+
+@<skip functions@>=
+static void hteg_hbox_node(void)
+{ box_t b;
+ @<skip the end byte |z|@>@;
+ if (KIND(z)!=hbox_kind) QUIT("Hbox expected at 0x%x got %s",node_pos,NAME(z));
+ HTEG_BOX(INFO(z),b);@/
+ @<skip and check the start byte |a|@>@;
+}
+
+static void hteg_vbox_node(void)
+{ box_t b;
+ @<skip the end byte |z|@>@;
+ if (KIND(z)!=vbox_kind) QUIT("Vbox expected at 0x%x got %s",node_pos,NAME(z));
+ HTEG_BOX(INFO(z),b);@/
+ @<skip and check the start byte |a|@>@;
+}
+@
+
+
+\subsection{Extended Boxes}\index{extended box}
+\noindent
+@<skip macros@>=
+#define @[HTEG_SET(I)@] @/\
+{ list_t l; @+hteg_list(&l); @+} \
+ if ((I)&b100) {xdimen_t x;@+ hteg_xdimen_node(&x); @+} \
+ else HTEG_REF(xdimen_kind);\
+{ stretch_t m; @+HTEG_STRETCH(m);@+}\
+{ stretch_t p; @+HTEG_STRETCH(p);@+}\
+if ((I)&b010) { dimen_t a; @+HTEG32(a);@+} \
+ { dimen_t w; @+HTEG32(w);@+} \
+ { dimen_t d; @+if ((I)&b001) HTEG32(d); @+ else d=0;@+}\
+ { dimen_t h; @+HTEG32(h);@+}
+@#
+
+#define @[HTEG_PACK(K,I)@] @/\
+ { list_t l; @+hteg_list(&l); @+} \
+ if ((I)&b100) {xdimen_t x; hteg_xdimen_node(&x);@+} @+ else HTEG_REF(xdimen_kind);\
+ if (K==vpack_kind) { dimen_t d; @+HTEG32(d); @+ }\
+ if ((I)&b010) { dimen_t d; @+HTEG32(d); @+ }
+@
+
+@<cases to skip content@>=
+@t\1\kern1em@>case TAG(hset_kind,b000): HTEG_SET(b000); @+ break;
+case TAG(hset_kind,b001): HTEG_SET(b001); @+ break;
+case TAG(hset_kind,b010): HTEG_SET(b010); @+ break;
+case TAG(hset_kind,b011): HTEG_SET(b011); @+ break;
+case TAG(hset_kind,b100): HTEG_SET(b100); @+ break;
+case TAG(hset_kind,b101): HTEG_SET(b101); @+ break;
+case TAG(hset_kind,b110): HTEG_SET(b110); @+ break;
+case TAG(hset_kind,b111): HTEG_SET(b111); @+ break;@#
+
+case TAG(vset_kind,b000): HTEG_SET(b000); @+ break;
+case TAG(vset_kind,b001): HTEG_SET(b001); @+ break;
+case TAG(vset_kind,b010): HTEG_SET(b010); @+ break;
+case TAG(vset_kind,b011): HTEG_SET(b011); @+ break;
+case TAG(vset_kind,b100): HTEG_SET(b100); @+ break;
+case TAG(vset_kind,b101): HTEG_SET(b101); @+ break;
+case TAG(vset_kind,b110): HTEG_SET(b110); @+ break;
+case TAG(vset_kind,b111): HTEG_SET(b111); @+ break;@#
+
+case TAG(hpack_kind,b000): HTEG_PACK(hpack_kind,b000); @+ break;
+case TAG(hpack_kind,b001): HTEG_PACK(hpack_kind,b001); @+ break;
+case TAG(hpack_kind,b010): HTEG_PACK(hpack_kind,b010); @+ break;
+case TAG(hpack_kind,b011): HTEG_PACK(hpack_kind,b011); @+ break;
+case TAG(hpack_kind,b100): HTEG_PACK(hpack_kind,b100); @+ break;
+case TAG(hpack_kind,b101): HTEG_PACK(hpack_kind,b101); @+ break;
+case TAG(hpack_kind,b110): HTEG_PACK(hpack_kind,b110); @+ break;
+case TAG(hpack_kind,b111): HTEG_PACK(hpack_kind,b111); @+ break;@#
+
+case TAG(vpack_kind,b000): HTEG_PACK(vpack_kind,b000); @+ break;
+case TAG(vpack_kind,b001): HTEG_PACK(vpack_kind,b001); @+ break;
+case TAG(vpack_kind,b010): HTEG_PACK(vpack_kind,b010); @+ break;
+case TAG(vpack_kind,b011): HTEG_PACK(vpack_kind,b011); @+ break;
+case TAG(vpack_kind,b100): HTEG_PACK(vpack_kind,b100); @+ break;
+case TAG(vpack_kind,b101): HTEG_PACK(vpack_kind,b101); @+ break;
+case TAG(vpack_kind,b110): HTEG_PACK(vpack_kind,b110); @+ break;
+case TAG(vpack_kind,b111): HTEG_PACK(vpack_kind,b111); @+ break;
+@
+
+
+\subsection{Leaders}\index{leaders}
+\noindent
+@<skip macros@>=
+#define @[HTEG_LEADERS(I)@]@/ \
+if (KIND(hpos[-1])==rule_kind) hteg_rule_node(); \
+else if (KIND(hpos[-1])==hbox_kind) hteg_hbox_node();\
+else hteg_vbox_node();\
+if ((I)&b100) hteg_glue_node();
+@
+
+@<cases to skip content@>=
+@t\1\kern1em@>case TAG(leaders_kind,1): @+ HTEG_LEADERS(1); @+break;
+case TAG(leaders_kind,2): @+ HTEG_LEADERS(2); @+break;
+case TAG(leaders_kind,3): @+ HTEG_LEADERS(3); @+break;
+case TAG(leaders_kind,b100|1): @+ HTEG_LEADERS(b100|1); @+break;
+case TAG(leaders_kind,b100|2): @+ HTEG_LEADERS(b100|2); @+break;
+case TAG(leaders_kind,b100|3): @+ HTEG_LEADERS(b100|3); @+break;
+@
+
+\subsection{Baseline Skips}\index{baseline skip}
+\noindent
+@<skip macros@>=
+#define @[HTEG_BASELINE(I,B)@] \
+ if((I)&b010) hteg_glue_node(); \
+ else {B.ls.p.o=B.ls.m.o=B.ls.w.w=0; @+B.ls.w.h=B.ls.w.v=B.ls.p.f=B.ls.m.f=0.0;@+}\
+ if((I)&b100) hteg_glue_node(); \
+ else {B.bs.p.o=B.bs.m.o=B.bs.w.w=0; @+B.bs.w.h=B.bs.w.v=B.bs.p.f=B.bs.m.f=0.0;@+}\
+ if((I)&b001) HTEG32((B).lsl); @+else B.lsl=0;
+@
+
+@<cases to skip content@>=
+@t\1\kern1em@>case TAG(baseline_kind,b001): @+{ baseline_t b;@+ HTEG_BASELINE(b001,b);@+ }@+break;
+case TAG(baseline_kind,b010): @+{ baseline_t b;@+ HTEG_BASELINE(b010,b);@+ }@+break;
+case TAG(baseline_kind,b011): @+{ baseline_t b;@+ HTEG_BASELINE(b011,b);@+ }@+break;
+case TAG(baseline_kind,b100): @+{ baseline_t b;@+ HTEG_BASELINE(b100,b);@+ }@+break;
+case TAG(baseline_kind,b101): @+{ baseline_t b;@+ HTEG_BASELINE(b101,b);@+ }@+break;
+case TAG(baseline_kind,b110): @+{ baseline_t b;@+ HTEG_BASELINE(b110,b);@+ }@+break;
+case TAG(baseline_kind,b111): @+{ baseline_t b;@+ HTEG_BASELINE(b111,b);@+ }@+break;
+@
+\subsection{Ligatures}\index{ligature}
+\noindent
+@<skip macros@>=
+#define @[HTEG_LIG(I,L)@] @/\
+if ((I)==7) hteg_list(&((L).l)); \
+else {(L).l.s=(I); @+hpos-=(L).l.s; @+ (L).l.p=hpos-hstart;@+} \
+(L).f=HTEG8;
+@
+
+@<cases to skip content@>=
+@t\1\kern1em@>case TAG(ligature_kind,1):@+ {lig_t l; @+HTEG_LIG(1,l);@+} @+break;
+case TAG(ligature_kind,2):@+ {lig_t l; @+HTEG_LIG(2,l);@+} @+break;
+case TAG(ligature_kind,3):@+ {lig_t l; @+HTEG_LIG(3,l);@+} @+break;
+case TAG(ligature_kind,4):@+ {lig_t l; @+HTEG_LIG(4,l);@+} @+break;
+case TAG(ligature_kind,5):@+ {lig_t l; @+HTEG_LIG(5,l);@+} @+break;
+case TAG(ligature_kind,6):@+ {lig_t l; @+HTEG_LIG(6,l);@+} @+break;
+case TAG(ligature_kind,7):@+ {lig_t l; @+HTEG_LIG(7,l);@+} @+break;
+@
+
+
+\subsection{Discretionary breaks}\index{discretionary breaks}
+\noindent
+@<skip macros@>=
+#define @[HTEG_DISC(I,H)@]\
+if ((I)&b001) hteg_list(&((H).q)); else { (H).q.p=hpos-hstart; @+(H).q.s=0; @+(H).q.k=list_kind; @+}\
+if ((I)&b010) hteg_list(&((H).p)); else { (H).p.p=hpos-hstart; @+(H).p.s=0; @+(H).p.k=list_kind; @+} \
+if ((I)&b100) (H).r=HTEG8; @+else (H).r=0;
+@
+@<cases to skip content@>=
+@t\1\kern1em@>case TAG(disc_kind,b001): @+{disc_t h; @+HTEG_DISC(b001,h); @+} @+break;
+case TAG(disc_kind,b010): @+{disc_t h; @+HTEG_DISC(b010,h); @+} @+break;
+case TAG(disc_kind,b011): @+{disc_t h; @+HTEG_DISC(b011,h); @+} @+break;
+case TAG(disc_kind,b100): @+{disc_t h; @+HTEG_DISC(b100,h); @+} @+break;
+case TAG(disc_kind,b101): @+{disc_t h; @+HTEG_DISC(b101,h); @+} @+break;
+case TAG(disc_kind,b110): @+{disc_t h; @+HTEG_DISC(b110,h); @+} @+break;
+case TAG(disc_kind,b111): @+{disc_t h; @+HTEG_DISC(b111,h); @+} @+break;
+@
+
+
+\subsection{Paragraphs}\index{paragraph}
+\noindent
+@<skip macros@>=
+#define @[HTEG_PAR(I)@] @/\
+ { list_t l; @+hteg_list(&l); @+} \
+ if ((I)&b010) { list_t l; @+hteg_param_list(&l); @+} else if ((I)!=b100) HTEG_REF(param_kind);\
+ if ((I)&b100) {xdimen_t x; @+ hteg_xdimen_node(&x); @+} else HTEG_REF(xdimen_kind);\
+ if ((I)==b100) HTEG_REF(param_kind);
+@
+
+@<cases to skip content@>=
+@t\1\kern1em@>case TAG(par_kind,b000): @+HTEG_PAR(b000);@+break;
+case TAG(par_kind,b010): @+HTEG_PAR(b010);@+break;
+case TAG(par_kind,b100): @+HTEG_PAR(b100);@+break;
+case TAG(par_kind,b110): @+HTEG_PAR(b110);@+break;
+@
+
+
+\subsection{Mathematics}\index{mathematics}\index{displayed formula}%
+\noindent
+@<skip macros@>=
+#define @[HTEG_MATH(I)@] \
+if ((I)&b001) hteg_hbox_node();\
+{ list_t l; @+hteg_list(&l); @+} \
+if ((I)&b010) hteg_hbox_node(); \
+if ((I)&b100) { list_t l; @+hteg_param_list(&l); @+} @+ else HTEG_REF(param_kind);
+@
+
+@<cases to skip content@>=
+@t\1\kern1em@>case TAG(math_kind,b000): HTEG_MATH(b000); @+ break;
+case TAG(math_kind,b001): HTEG_MATH(b001); @+ break;
+case TAG(math_kind,b010): HTEG_MATH(b010); @+ break;
+case TAG(math_kind,b100): HTEG_MATH(b100); @+ break;
+case TAG(math_kind,b101): HTEG_MATH(b101); @+ break;
+case TAG(math_kind,b110): HTEG_MATH(b110); @+ break;
+case TAG(math_kind,b011):
+case TAG(math_kind,b111): @+ break;
+@
+
+\subsection{Images}\index{image}
+\noindent
+@<skip macros@>=
+#define @[HTEG_IMAGE(I,X)@] @/\
+if (I&b001) {HTEG_STRETCH((X).m);HTEG_STRETCH((X).p);@+}\
+else { (X).p.f=(X).m.f=0.0; (X).p.o=(X).m.o=normal_o;@+}\
+if (I&b010) {HTEG32((X).h);HTEG32((X).w);@+} \
+else (X).w=(X).h=0;\
+HTEG16((X).n);
+@
+
+@<cases to skip content@>=
+@t\1\kern1em@>case TAG(image_kind,b100): @+ { image_t x;@+HTEG_IMAGE(b100,x);@+}@+break;
+case TAG(image_kind,b101): @+ { image_t x;@+HTEG_IMAGE(b101,x);@+}@+break;
+case TAG(image_kind,b110): @+ { image_t x;@+HTEG_IMAGE(b110,x);@+}@+break;
+case TAG(image_kind,b111): @+ { image_t x;@+HTEG_IMAGE(b111,x);@+}@+break;
+@
+
+\subsection{Links and Labels}
+\noindent
+@<skip macros@>=
+#define @[HTEG_LINK(I)@] @/\
+{ uint16_t n; if (I&b001) HTEG16(n);@+ else n=HTEG8; @+}
+@
+
+@<cases to skip content@>=
+@t\1\kern1em@>case TAG(link_kind,b000): @+ HTEG_LINK(b000); @+break;
+case TAG(link_kind,b001): @+ HTEG_LINK(b001); @+break;
+case TAG(link_kind,b010): @+ HTEG_LINK(b010); @+break;
+case TAG(link_kind,b011): @+ HTEG_LINK(b011); @+break;
+@
+
+
+\subsection{Plain Lists, Texts, and Parameter Lists}\index{list}
+
+\noindent
+@<shared skip functions@>=
+void hteg_size_boundary(info_t info)
+{ uint32_t n;
+ if (info<2) return;
+ n=HTEG8;
+ if (n-1!=0x100-info) QUIT(@["List size boundary byte 0x%x does not match info value %d at " SIZE_F@],
+ n, info,hpos-hstart);
+}
+
+uint32_t hteg_list_size(info_t info)
+{ uint32_t n;
+ if (info==1) return 0;
+ else if (info==2) n=HTEG8;
+ else if (info==3) HTEG16(n);
+ else if (info==4) HTEG24(n);
+ else if (info==5) HTEG32(n);
+ else QUIT("List info %d must be 1, 2, 3, 4, or 5",info);
+ return n;
+}
+
+void hteg_list(list_t *l)
+{ @<skip the end byte |z|@>@,
+ @+if (KIND(z)!=list_kind && KIND(z)!=text_kind &&@| KIND(z)!=param_kind) @/
+ QUIT("List expected at 0x%x", (uint32_t)(hpos-hstart));
+ else
+ { uint32_t s;
+ l->k=KIND(z);
+ l->s=hteg_list_size(INFO(z));
+ hteg_size_boundary(INFO(z));
+ hpos=hpos-l->s;
+ l->p=hpos-hstart;
+ hteg_size_boundary(INFO(z));
+ s=hteg_list_size(INFO(z));
+ if (s!=l->s) QUIT(@["List sizes at " SIZE_F " and 0x%x do not match 0x%x != 0x%x"@],
+ hpos-hstart,node_pos-1,s,l->s);
+ @<skip and check the start byte |a|@>@;
+ }
+}
+
+void hteg_param_list(list_t *l)
+{ @+if (KIND(*(hpos-1))!=param_kind) return;
+ hteg_list(l);
+}
+
+
+@
+
+\subsection{Adjustments}\index{adjustment}
+\noindent
+@<cases to skip content@>=
+@t\1\kern1em@>case TAG(adjust_kind,b001): @+ { list_t l; @+hteg_list(&l);@+ } @+ break;
+@
+
+\subsection{Tables}\index{table}
+\noindent
+@<skip macros@>=
+#define @[HTEG_TABLE(I)@] \
+{@+ list_t l; @+ hteg_list(&l);@+}\
+{@+ list_t l; @+ hteg_list(&l);@+}\
+if ((I)&b100) {xdimen_t x;@+ hteg_xdimen_node(&x);@+} else HTEG_REF(xdimen_kind)@;
+@
+
+@<cases to skip content@>=
+@t\1\kern1em@>case TAG(table_kind,b000): @+ HTEG_TABLE(b000); @+ break;
+case TAG(table_kind,b001): @+ HTEG_TABLE(b001); @+ break;
+case TAG(table_kind,b010): @+ HTEG_TABLE(b010); @+ break;
+case TAG(table_kind,b011): @+ HTEG_TABLE(b011); @+ break;
+case TAG(table_kind,b100): @+ HTEG_TABLE(b100); @+ break;
+case TAG(table_kind,b101): @+ HTEG_TABLE(b101); @+ break;
+case TAG(table_kind,b110): @+ HTEG_TABLE(b110); @+ break;
+case TAG(table_kind,b111): @+ HTEG_TABLE(b111); @+ break;@#
+
+case TAG(item_kind,b000): @+{@+ list_t l; @+hteg_list(&l);@+ } @+ break;
+case TAG(item_kind,b001): hteg_content_node(); @+ break;
+case TAG(item_kind,b010): hteg_content_node(); @+ break;
+case TAG(item_kind,b011): hteg_content_node(); @+ break;
+case TAG(item_kind,b100): hteg_content_node(); @+ break;
+case TAG(item_kind,b101): hteg_content_node(); @+ break;
+case TAG(item_kind,b110): hteg_content_node(); @+ break;
+case TAG(item_kind,b111): hteg_content_node(); @+{uint8_t n;@+ n=HTEG8;@+}@+ break;
+@
+
+
+
+\subsection{Stream Nodes}\index{stream}
+@<skip macros@>=
+#define @[HTEG_STREAM(I)@] @/\
+{ list_t l; @+hteg_list(&l); @+}\
+if ((I)&b010) { list_t l; @+hteg_param_list(&l); @+} @+ else HTEG_REF(param_kind);\
+HTEG_REF(stream_kind);
+@
+
+@<cases to skip content@>=
+@t\1\kern1em@>case TAG(stream_kind,b000): HTEG_STREAM(b000); @+ break;
+case TAG(stream_kind,b010): HTEG_STREAM(b010); @+ break;
+@
+
+
+
+\subsection{References}\index{reference}
+\noindent
+@<skip macros@>=
+#define @[HTEG_REF(K)@] do@+{uint8_t n; @+ n=HTEG8;@+} @+ while (false)
+@
+
+@<cases to skip content@>=
+@t\1\kern1em@>case TAG(penalty_kind,0): HTEG_REF(penalty_kind); @+break;
+case TAG(kern_kind,b000): HTEG_REF(dimen_kind); @+break;
+case TAG(kern_kind,b100): HTEG_REF(dimen_kind); @+break;
+case TAG(kern_kind,b001): HTEG_REF(xdimen_kind); @+break;
+case TAG(kern_kind,b101): HTEG_REF(xdimen_kind); @+break;
+case TAG(ligature_kind,0): HTEG_REF(ligature_kind); @+break;
+case TAG(disc_kind,0): HTEG_REF(disc_kind); @+break;
+case TAG(glue_kind,0): HTEG_REF(glue_kind); @+break;
+case TAG(language_kind,0): HTEG_REF(language_kind); @+break;
+case TAG(rule_kind,0): HTEG_REF(rule_kind); @+break;
+case TAG(image_kind,0): HTEG_REF(image_kind); @+break;
+case TAG(leaders_kind,0): HTEG_REF(leaders_kind); @+break;
+case TAG(baseline_kind,0): HTEG_REF(baseline_kind); @+break;
+@
+
+
+\section{Code and Header Files}\index{code file}\index{header file}
+
+\subsection{{\tt basetypes.h}}
+To define basic types in a portable way, we create an include file.
+The macro |_MSC_VER| (Microsoft Visual C Version)\index{Microsoft Visual C}
+is defined only if using the respective compiler.
+\index{false+\\{false}}\index{true+\\{true}}\index{bool+\&{bool}}
+@(basetypes.h@>=
+#ifndef __BASETYPES_H__
+#define __BASETYPES_H__
+#include <stdlib.h>
+#include <stdio.h>
+#ifndef _STDLIB_H
+#define _STDLIB_H
+#endif
+#ifdef _MSC_VER
+#include <windows.h>
+#define uint8_t UINT8
+#define uint16_t UINT16
+#define uint32_t UINT32
+#define uint64_t UINT64
+#define int8_t INT8
+#define int16_t INT16
+#define int32_t INT32
+#define bool BOOL
+#define true (0==0)
+#define false (!true)
+#define __SIZEOF_FLOAT__ 4
+#define __SIZEOF_DOUBLE__ 8
+#define INT32_MAX (2147483647)
+#define PRIx64 "I64x"
+#pragma @[warning( disable : @[4244@]@t @> @[4996@]@t @> @[4127@])@]
+#else
+#include <stdint.h>
+#include <stdbool.h>
+#include <inttypes.h>
+#include <unistd.h>
+#ifdef WIN32
+#include <io.h>
+#endif
+#endif
+typedef float float32_t;
+typedef double float64_t;
+#if __SIZEOF_FLOAT__!=4
+#error @=float32 type must have size 4@>
+#endif
+#if __SIZEOF_DOUBLE__!=8
+#error @=float64 type must have size 8@>
+#endif
+#endif
+@
+
+
+
+\subsection{{\tt hformat.h}}\index{hformat.h+{\tt hformat.h}}
+The \.{hformat.h} file contains definitions of types, macros, variables and functions
+that are needed in other compilation units.
+
+@(hformat.h@>=
+#ifndef _HFORMAT_H_
+#define _HFORMAT_H_
+@<debug macros@>@;
+@<debug constants@>@;
+@<hint macros@>@;
+@<hint basic types@>@;
+@<default names@>@;
+
+extern const char *content_name[32];
+extern const char *definition_name[32];
+extern unsigned int debugflags;
+extern FILE *hlog;
+extern int max_fixed[32], max_default[32], max_ref[32], max_outline;
+extern int32_t int_defaults[MAX_INT_DEFAULT+1];
+extern dimen_t dimen_defaults[MAX_DIMEN_DEFAULT+1];
+extern xdimen_t xdimen_defaults[MAX_XDIMEN_DEFAULT+1];
+extern glue_t glue_defaults[MAX_GLUE_DEFAULT+1];
+extern baseline_t baseline_defaults[MAX_BASELINE_DEFAULT+1];
+extern label_t label_defaults[MAX_LABEL_DEFAULT+1];
+extern signed char hnode_size[0x100];
+
+#endif
+@
+\subsection{{\tt hformat.c}}\index{hformat.c+{\tt hformat.c}}\index{mkhformat.c+{\tt mkhformat.c}}
+For maximum flexibility and efficiency, the file {\tt hformat.c}
+is generated by a \CEE\ program.
+Here is the |main| program of {\tt mkhformat}:
+
+@(mkhformat.c@>=
+#include <stdio.h>
+#include "basetypes.h"
+#include "hformat.h"
+@<skip macros@>@;
+
+int max_fixed[32], max_default[32];
+
+int32_t int_defaults[MAX_INT_DEFAULT+1]={0};
+dimen_t dimen_defaults[MAX_DIMEN_DEFAULT+1]={0};
+xdimen_t xdimen_defaults[MAX_XDIMEN_DEFAULT+1]={{0}};
+glue_t glue_defaults[MAX_GLUE_DEFAULT+1]={{{0}}};
+baseline_t baseline_defaults[MAX_BASELINE_DEFAULT+1]={{{{0}}}};
+
+signed char hnode_size[0x100]={0};
+@<define |content_name| and |definition_name|@>@;
+int main(void)
+{ kind_t k;
+ int i;
+
+
+ printf("#include \"basetypes.h\"\n"@/
+ "#include \"hformat.h\"\n\n");@/
+
+ @<print |content_name| and |definition_name|@>@;
+
+ printf("int max_outline=-1;\n\n");
+
+ @<take care of variables without defaults@>@;
+ @<define |int_defaults|@>@;
+ @<define |dimen_defaults|@>@;
+ @<define |glue_defaults|@>@;
+ @<define |xdimen_defaults|@>@;
+ @<define |baseline_defaults|@>@;
+ @<define page defaults@>@;
+ @<define stream defaults@>@;
+ @<define range defaults@>@;
+ @<define |label_defaults|@>@;
+ @<print defaults@>@;
+
+ @<initialize the |hnode_size| array@>@;
+ @<print the |hnode_size| variable@>@;
+ return 0;
+}
+@
+
+The following code prints the arrays containing the default values.
+
+@<print defaults@>=
+ printf("int max_fixed[32]= {");
+ for (k=0; k<32; k++)@/
+ { printf("%d",max_fixed[k]);@+
+ if (k<31) printf(", ");@+
+ }
+ printf("};\n\n");@#
+ printf("int max_default[32]= {");
+ for (k=0; k<32; k++)@/
+ { printf("%d",max_default[k]);@+
+ if (k<31) printf(", ");@+
+ }
+ printf("};\n\n");
+ printf("int max_ref[32]= {");
+ for (k=0; k<32; k++)@/
+ { printf("%d",max_default[k]);@+
+ if (k<31) printf(", ");@+
+ }
+ printf("};\n\n");
+@
+
+
+\subsection{{\tt hget.h}}\index{hget.h+{\tt hget.h}}
+The \.{hget.h} file contains function prototypes for all the functions
+that read the short format.
+
+@(hget.h@>=
+@<hint types@>@;
+@<directory entry type@>@;
+@<shared get macros@>@;
+
+extern entry_t *dir;
+extern uint16_t section_no, max_section_no;
+extern uint8_t *hpos, *hstart, *hend, *hpos0;
+extern uint64_t hin_size, hin_time;
+extern uint8_t *hin_addr;
+
+extern label_t *labels;
+extern char *hin_name;
+extern bool hget_map(void);
+extern void hget_unmap(void);
+
+extern void new_directory(uint32_t entries);
+extern void hset_entry(entry_t *e, uint16_t i, @|uint32_t size, uint32_t xsize, char *file_name);
+
+extern void hget_banner(void);
+extern void hget_section(uint16_t n);
+extern void hget_entry(entry_t *e);
+extern void hget_directory(void);
+extern void hclear_dir(void);
+extern bool hcheck_banner(char *magic);
+
+extern void hget_max_definitions(void);
+extern uint32_t hget_utf8(void);
+extern void hget_size_boundary(info_t info);
+extern uint32_t hget_list_size(info_t info);
+extern void hget_list(list_t *l);
+extern uint32_t hget_utf8(void);
+extern float32_t hget_float32(void);
+extern float32_t hteg_float32(void);
+extern void hteg_size_boundary(info_t info);
+extern uint32_t hteg_list_size(info_t info);
+extern void hteg_list(list_t *l);
+extern void hff_hpos(void);
+extern uint32_t hff_list_pos, hff_list_size;
+extern uint8_t hff_tag;
+@
+
+
+
+\subsection{{\tt hget.c}}\index{hget.c+{\tt hget.c}}
+@(hget.c@>=
+#include "basetypes.h"
+#include <string.h>
+#include <math.h>
+#include <zlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#include "error.h"
+#include "hformat.h"
+#include "hget.h"
+
+@<common variables@>@;
+
+@<map functions@>@;
+@<function to check the banner@>@;
+@<directory functions@>@;
+
+@<get file functions@>@;
+@<shared get functions@>@;
+@<shared skip functions@>@;
+@
+
+\subsection{{\tt hput.h}}\index{hput.h+{\tt hput.h}}
+The \.{hput.h} file contains function prototypes for all the functions
+that write the short format.
+
+
+@(hput.h@>=
+@<put macros@>@;
+@<hint macros@>@;
+@<hint types@>@;
+@<directory entry type@>@;
+extern entry_t *dir;
+extern uint16_t section_no, max_section_no;
+extern uint8_t *hpos, *hstart, *hend, *hpos0;
+extern int next_range;
+extern range_pos_t *range_pos;
+extern int *page_on;
+extern label_t *labels;
+extern int first_label;
+extern int max_outline;
+extern outline_t *outlines;
+
+
+extern FILE *hout;
+extern void new_directory(uint32_t entries);
+extern void new_output_buffers(void);
+
+/* declarations for the parser */
+extern void hput_definitions_start(void);
+extern void hput_definitions_end(void);
+extern void hput_content_start(void);
+extern void hput_content_end(void);
+
+extern void hset_label(int n,int w);
+extern uint8_t hput_link(int n, int on);
+extern void hset_outline(int m, int r, int d, uint32_t p);
+extern void hput_label_defs(void);
+
+extern void hput_tags(uint32_t pos, uint8_t tag);
+extern uint8_t hput_glyph(glyph_t *g);
+extern uint8_t hput_xdimen(xdimen_t *x);
+extern uint8_t hput_int(int32_t p);
+extern uint8_t hput_language(uint8_t n);
+extern uint8_t hput_rule(rule_t *r);
+extern uint8_t hput_glue(glue_t *g);
+extern uint8_t hput_list(uint32_t size_pos, list_t *y);
+extern uint8_t hsize_bytes(uint32_t n);
+extern void hput_txt_cc(uint32_t c);
+extern void hput_txt_font(uint8_t f);
+extern void hput_txt_global(ref_t *d);
+extern void hput_txt_local(uint8_t n);
+extern info_t hput_box_dimen(dimen_t h, dimen_t d, dimen_t w);
+extern info_t hput_box_shift(dimen_t a);
+extern info_t hput_box_glue_set(int8_t s, float32_t r, order_t o);
+extern void hput_stretch(stretch_t *s);
+extern uint8_t hput_kern(kern_t *k);
+extern void hput_utf8(uint32_t c);
+extern uint8_t hput_ligature(lig_t *l);
+extern uint8_t hput_disc(disc_t *h);
+extern info_t hput_span_count(uint32_t n);
+extern uint8_t hput_image(image_t *x);
+extern void hput_string(char *str);
+extern void hput_range(uint8_t pg, bool on);
+extern void hput_max_definitions(void);
+extern uint8_t hput_dimen(dimen_t d);
+extern uint8_t hput_font_head(uint8_t f, char *n, dimen_t s,@| uint16_t m, uint16_t y);
+extern void hput_range_defs(void);
+extern void hput_xdimen_node(xdimen_t *x);
+extern void hput_directory(void);
+extern void hput_hint(char * str);
+extern void hput_list_size(uint32_t n, int i);
+extern int hcompress_depth(int n, int c);
+@
+
+
+\subsection{{\tt hput.c}}\label{writeshort}\index{hput.c+{\tt hput.c}}
+\noindent
+@(hput.c@>=
+#include "basetypes.h"
+#include <string.h>
+#include <ctype.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <zlib.h>
+#include "error.h"
+#include "hformat.h"
+#include "hput.h"
+
+@<common variables@>@;
+@<shared put variables@>@;
+@<directory functions@>@;
+@<function to write the banner@>@;
+@<put functions@>@;
+@
+
+\subsection{{\tt shrink.l}}\index{shrink.l+{\tt shrink.l}}\index{scanning}
+The definitions for lex are collected in the file {\tt shrink.l}
+
+@(shrink.lex-in@>=
+%{
+#include "basetypes.h"
+#include "error.h"
+#include "hformat.h"
+#include "hput.h"
+
+@<enable bison debugging@>@;
+#include "shrink-parser.h"
+
+@<scanning macros@>@;@+
+@<scanning functions@>@;
+int yywrap (void )@+{ return 1;@+}
+#ifdef _MSC_VER
+#pragma warning( disable : 4267)
+#endif
+%}
+
+%option yylineno batch stack
+%option debug
+%option nounistd nounput noinput noyy_top_state
+
+@<scanning definitions@>@/
+
+%%
+
+@<scanning rules@>@/
+::@=[a-z]+@> :< QUIT("Unexpected keyword '%s' in line %d",@|yytext,yylineno); >:
+::@=.@> :< QUIT("Unexpected character '%c' (0x%02X) in line %d",@|yytext[0]>' '?yytext[0]:' ',yytext[0],yylineno); >:
+
+%%
+@
+
+
+
+\subsection{{\tt shrink.y}}\index{shrink.y+{\tt shrink.y}}\index{parsing}
+
+The grammar rules for bison are collected in the file {\tt shrink.y}.
+% for the option %token-table use the command line parameter -k
+
+
+@(shrink.yacc-in@>=
+%{
+#include "basetypes.h"
+#include <string.h>
+#include <math.h>
+#include "error.h"
+#include "hformat.h"
+#include "hput.h"
+extern char **hfont_name; /* in common variables */
+
+@<definition checks@>@;
+
+extern void hset_entry(entry_t *e, uint16_t i, @|uint32_t size,
+ uint32_t xsize, char *file_name);
+
+@<enable bison debugging@>@;
+extern int yylex(void);
+
+@<parsing functions@>@;
+
+%}
+
+
+@t{\label{union}\index{union}\index{parsing}}@>
+
+
+%union {uint32_t u; @+ int32_t i; @+ char *s; @+ float64_t f; @+ glyph_t c;
+ @+ dimen_t @+d; stretch_t st; @+ xdimen_t xd; @+ kern_t kt;
+ @+ rule_t r; @+ glue_t g; @+ @+ image_t x;
+ @+ list_t l; @+ box_t h; @+ disc_t dc; @+ lig_t lg;
+ @+ ref_t rf; @+ info_t info; @+ order_t o; bool@+ b;
+ }
+
+@t{}@>
+
+%error_verbose
+%start hint
+@t@>
+@<symbols@>@/
+%%
+@<parsing rules@>@;
+%%
+@
+
+\subsection{{\tt hishrink.c}}\index{hishrink.c+{\tt hishrink.c}}
+
+\.{shrink} is a \CEE\ program translating a \HINT\ file in long format into a \HINT\ file in short format.
+
+@(hishrink.c@>=
+#include "basetypes.h"
+#include <string.h>
+#include <ctype.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#ifdef WIN32
+#include <direct.h>
+#endif
+#include <zlib.h>
+
+#include "error.h"
+#include "hformat.h"
+#include "hput.h"
+#include "shrink-parser.h"
+
+extern void yyset_debug(int lex_debug);
+extern int yylineno;
+extern FILE *yyin, *yyout;
+extern int yyparse(void);
+
+@<put macros@>@;
+
+@<common variables@>@;
+@<shared put variables@>@;
+@<function to check the banner@>@;
+@<directory functions@>@;
+@<function to write the banner@>@;
+@<put functions@>@;
+
+int main(int argc, char *argv[])
+{ @<local variables in |main|@>@;
+ in_ext=".HINT";
+ out_ext=".hnt";
+ @<process the command line@>@;
+
+ if (debugflags&DBGFLEX) yyset_debug(1); else yyset_debug(0);
+#if YYDEBUG
+ if (debugflags&DBGBISON) yydebug=1;
+ else yydebug=0;
+#endif
+ @<open the log file@>@;
+ @<open the input file@>@;
+ @<open the output file@>@;
+
+ yyin=hin;
+ yyout=hlog;
+ @<read the banner@>@;
+ if (!hcheck_banner("HINT")) QUIT("Invalid banner");
+ yylineno++;
+ DBG(DBGBISON|DBGFLEX,"Parsing Input\n");
+ yyparse();
+
+ hput_directory();
+
+ hput_hint("shrink");
+
+ @<close the output file@>@;
+ @<close the input file@>@;
+ @<close the log file@>@;
+ return 0;
+explain_usage:
+ @<explain usage@>@;
+ return 1;
+}
+@
+
+
+
+\subsection{{\tt histretch.c}}\label{stretchmain}\index{histretch.c+{\tt histretch.c}}
+\.{histretch} is a \CEE\ program translating a \HINT\ file in short
+format into a \HINT\ file in long format.
+
+@(histretch.c@>=
+#include "basetypes.h"
+#include <math.h>
+#include <string.h>
+#include <ctype.h>
+#include <zlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#ifdef WIN32
+#include <direct.h>
+#endif
+#include <fcntl.h>
+#include "error.h"
+#include "hformat.h"
+#include "hget.h"
+
+@<get macros@>@;
+@<write macros@>@;
+@<common variables@>@;
+@<shared put variables@>@;
+@<map functions@>@;
+@<function to check the banner@>@;
+@<function to write the banner@>@;
+@<directory functions@>@;
+
+@<definition checks@>@;
+@<get function declarations@>@;
+@<write functions@>@;
+@<get file functions@>@;
+@<shared get functions@>@;
+@<get functions@>@;
+
+int main(int argc, char *argv[])
+{ @<local variables in |main|@>@;
+
+ in_ext=".hnt";
+ out_ext=".HINT";
+ @<process the command line@>@;
+ @<open the log file@>@;
+ @<open the output file@>@;
+ @<determine the |stem_name| from the output |file_name|@>@;
+ if (!hget_map()) QUIT("Unable to map the input file");
+ hpos=hstart=hin_addr;
+ hend=hstart+hin_size;
+ hget_banner();
+ if (!hcheck_banner("hint")) QUIT("Invalid banner");
+ hput_banner("HINT","stretch");
+ hget_directory();
+ hwrite_directory();
+ hget_definition_section();
+ hwrite_content_section();
+ hwrite_aux_files();
+ hget_unmap();
+ @<close the output file@>@;
+ DBG(DBGBASIC,"End of Program\n");
+ @<close the log file@>@;
+ return 0;
+explain_usage:
+ @<explain usage@>@;
+ return 1;}
+@
+
+In the above program, the get functions call the write functions
+and the write functions call some get functions. This requires
+function declarations to satisfy the define before use requirement
+of \CEE. Some of the necessary function declarations are already
+contained in {\tt hget.h}. The remaining declarations are these:
+
+@<get function declarations@>=
+extern void hget_xdimen_node(xdimen_t *x);
+extern void hget_def_node(void);
+extern void hget_font_def(uint8_t f);
+extern void hget_content_section(void);
+extern uint8_t hget_content_node(void);
+extern void hget_glue_node(void);
+extern void hget_rule_node(void);
+extern void hget_hbox_node(void);
+extern void hget_vbox_node(void);
+extern void hget_param_list(list_t *l);
+extern int hget_txt(void);
+@
+
+
+\subsection{{\tt skip.c}}\label{skip}\index{skip.c+{\tt skip.c}}
+\.{skip} is a \CEE\ program reading the content section of a \HINT\ file in short format
+backwards.
+
+@(skip.c@>=
+#include "basetypes.h"
+#include <string.h>
+#include <zlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include "error.h"
+#include "hformat.h"
+@<hint types@>@;
+
+@<common variables@>@;
+
+@<map functions@>@;
+@<function to check the banner@>@;
+@<directory entry type@>@;
+@<directory functions@>@;
+@<shared get macros@>@;
+@<get file functions@>@;
+
+@<skip macros@>@;
+@<skip function declarations@>@;
+@<shared skip functions@>@;
+@<skip functions@>@;
+
+int main(int argc, char *argv[])
+{ @<local variables in |main|@>@;
+ in_ext=".hnt";
+ out_ext=".bak";
+
+ @<process the command line@>@;
+ @<open the log file@>@;
+ if (!hget_map()) QUIT("Unable to map the input file");
+ hpos=hstart=hin_addr;
+ hend=hstart+hin_size;
+ hget_banner();
+ if (!hcheck_banner("hint")) QUIT("Invalid banner");
+ hget_directory();
+ DBG(DBGBASIC,"Skipping Content Section\n");
+ hteg_content_section();
+ DBG(DBGBASIC,"Fast forward Content Section\n");
+ hpos=hstart;
+ while(hpos<hend)
+ { hff_hpos();
+ if (KIND(*(hpos-1))==par_kind && KIND(hff_tag)==list_kind && hff_list_size>0)
+ { uint8_t *p=hpos,*q;
+ DBG(DBGTAGS,"Fast forward list at 0x%x, size %d",hff_list_pos,hff_list_size);
+ hpos=hstart+hff_list_pos;
+ q=hpos+hff_list_size;
+ while (hpos<q)
+ hff_hpos();
+ hpos=p;
+ }
+ }
+ hget_unmap();
+ @<close the log file@>@;
+ return 0;
+explain_usage:
+ @<explain usage@>@;
+ return 1;
+}
+@
+
+As we have seen already in the {\tt stretch} program, a few
+function declarations are necessary to satisfy the define before
+use requirement of \CEE.
+
+@<skip function declarations@>=
+static void hteg_content_node(void);
+static void hteg_content(uint8_t z);
+static void hteg_xdimen_node(xdimen_t *x);
+static void hteg_list(list_t *l);
+static void hteg_param_list(list_t *l);
+static float32_t hteg_float32(void);
+static void hteg_rule_node(void);
+static void hteg_hbox_node(void);
+static void hteg_vbox_node(void);
+static void hteg_glue_node(void);
+@
+
+\thecodeindex
+
+\crosssections
+
+\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 format.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/hitexdir/hitex.w b/Build/source/texk/web2c/hitexdir/hitex.w
new file mode 100644
index 00000000000..b56946e7421
--- /dev/null
+++ b/Build/source/texk/web2c/hitexdir/hitex.w
@@ -0,0 +1,4753 @@
+% This file is part of HINT
+% Copyright 2017-2021 Martin Ruckert
+%
+% 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 ../hint.sty
+\input ../changefile.sty
+
+%% defining how to display certain C identifiers
+@s uint8_t int
+@s uint16_t int
+@s uint32_t int
+@s uint64_t int
+@s int8_t int
+@s int16_t int
+@s int32_t int
+@s float64_t int
+@s float32_t int
+@s scaled int
+@s pointer int
+@s memory_word int
+@s ASCII_code int
+@s small_number int
+@s info_t int
+@s lig_t int
+@s disc_t int
+@s math_t int
+@s glyph_t int
+@s glue_t int
+@s rule_t int
+@s list_t int
+@s kind_t int
+@s image_t int
+@s glue_ord int
+@s four_quarters int
+@s eight_bits int
+@s internal_font_number int
+@s explicit normal
+@s str_number int
+@s dimen_t int
+@s loop else
+@s kpse_file_format_type int
+@s font_t int
+
+
+\makeindex
+\maketoc
+
+%\makefigindex
+\titletrue
+
+\def\lastrevision{${}$Revision: 2485 ${}$}
+\def\lastdate{${}$Date: 2021-08-19 19:37:13 +0200 (Thu, 19 Aug 2021) ${}$}
+\eject
+\input titlepage.tex
+
+
+\frontmatter
+
+@
+
+
+\plainsection{Preface}
+To be written
+
+\vskip 1cm
+\noindent {\it M\"unchen\hfil\break
+August 20, 2018 \hfill Martin Ruckert}
+
+
+\tableofcontent
+%\thefigindex
+
+
+\mainmatter
+
+\section{Introduction}\label{intro}
+Hi\TeX\ is a modified version of \TeX\ that replaces the \.{DVI}
+(device independent) output format by the \HINT\ format. The \HINT\
+format, described in \cite{MR:format}, was designed with two
+objectives: being able to support reflowing pages using the \TeX\
+typesetting engine and making it simple to use it as output format of
+the \TeX\ programm. The present book tries to convince its readers
+that the latter claim is true. To this end, this book implements a
+version of \TeX\ that produces \HINT\ output---to be precise: a short
+format \HINT\ file.
+
+The book is written as a literate program\cite{Knuth:lp} using ``The
+\.{CWEB} System of Structured Documentation''\cite{Knuth:cweb}. The
+largest part of this program is, of course, \TeX\cite{Knuth:tex}
+itself. Therefore a significant part of Hi\TeX\ consists of
+modifications of the \TeX\ source code, which itself is written as a
+literate program using ``The \.{WEB} System of Structured
+Documentation''\cite{Knuth:WEB}. The tiny, but significant,
+difference between \.{WEB}\index{WEB+{\tt WEB}}
+and \.{CWEB}\index{CWEB+{\tt CWEB}} is the transition from
+\Pascal\ to \CEE/ as target language. To bridge the gap between
+\.{WEB} and \.{CWEB}, Hi\TeX\ is not based on the original \.{WEB}
+implementation of \TeX\ but on the \.{CWEB} implementation of \TeX\
+as described in~\cite{MR:webtocweb} and~\cite{MR:web2w}.
+Further, it does not use the plain {\tt ctex.w} translation of {\tt tex.web}
+but the {\tt ktex.w} file which extends \TeX\ with
+ the features of $\epsilon$-\TeX, the file searching
+mechanisms of the {\tt kpathsearch} library, and the command line
+conventions of \TeX\ Live.
+
+To accomplish modifications of a \.{CWEB} file, the \.{CWEB} tools
+support the use of ``change files''. These change files are lists of
+code changes optionally embellished with explanatory text. Each code
+change consists of two parts: a literal copy of the original source
+code followed by the replacement text. When a \.{CWEB} tool applies
+such a change file to a cweb file, it will read both files
+sequentially and applies the code changes in the order given: Whenever
+it finds a section in the \.{CWEB} file that matches the first part of
+the current code change, it will replace it by the second part of the
+code change, and advance to the next code change.
+
+The present book makes an attempt to present these code changes in
+``literate programming style'' and had to overcome two obstacles:
+First, in a literate program, the exposition determines the order of
+appearance of the code sections; and second, the tools that generate
+the documentation from a \.{CWEB} file are not able to generate
+documentation for a change file.
+
+The first problem can be solved by using the program {\tt
+tie}\cite{kg:tie}\cite{jg:ctie}. It allows splitting a single change
+file into multiple change files, and while within each change file,
+the order of changes is still determined by the original cweb file,
+changes that belong together can be grouped into separate files.
+
+The second problem is solved by a simple preprocessor, that converts
+change files into cweb files which then can be converted into
+\TeX. With the help of some modifications of the macros in {\tt
+cwebmac.tex} these \TeX\ files are used to present change files in
+this book.
+
+Let's look at an examples to explain and illustrate the presentation
+of code changes in this book: the {\tt display\_node.ch} change file.
+
+\subsection{The Function |display_node|}
+It is the purpose of the {\tt display\_node.ch} change file to make
+the \TeX\ code contained in the section ``\<Display node |p|>''
+available to other parts of Hi\TeX\ as a function.
+The function is then used to display debugging output.
+
+This requires the following changes:
+
+\changestyle{displaynode.tex}
+
+As you can see, the two parts of a code change are enclosed in square brackets
+where the opening top bracket is labeled {\it old\/} or {\it new\/}.
+While a full understanding of these code changes requires a look at the original \TeX\ code,
+at least they document what was done and why.
+
+There is still one thing missing before we can use the |display_node|
+function in the {\tt hitex.c} file we are about to generate:
+we need an |extern| declaration of |display_node|.
+In fact, the Hi\TeX\ program contains a whole bunch of functions
+that will need access to \TeX's internals and the most readable
+and convenient way to gain this access is a header file.
+
+Generating such a header file can be accomplished by using the {\tt -h}
+and {\tt -e} options of the {\tt web2w} program when converting the {\tt WEB}
+source of \TeX\ to its {\tt cweb} source. We just need to modify the
+process slightly for our purposes.
+
+{
+\changestyle{types.tex}
+}
+
+
+\section{Modifying \TeX}\label{modifyingTeX}
+In this section, we explain the different change files that modify \TeX.
+Larger changes are accomplished by replacing entire functions.
+
+
+\subsection{The |main| Program}
+We add two function calls to \TeX's |main| program:
+|hint_open|, and |hint_close|.
+
+{
+\changestyle{main.tex}
+}
+The functions to open and close the \HINT\ file follow in
+section~\secref{output}.
+
+
+\subsection{The Command Line}
+Hi\TeX\ has a few additional command line options that we define next.
+
+{
+\changestyle{command_line.tex}
+}
+
+Above we used the function |hint_debug_help|. It is defined as:
+
+@<Hi\TeX\ routines@>=
+#ifdef DEBUG
+void hint_debug_help(void)
+{
+fprintf(stderr,@/
+ "To generate HINT format debug output use the option\n"
+ " -debug=XX "@/
+@t\qquad@>"\t XX is a hexadecimal value. OR together these values:\n");@/
+fprintf(stderr,"\t\t\t XX=%04X \t basic debugging\n", DBGBASIC);@/
+fprintf(stderr,"\t\t\t XX=%04X \t tag debugging\n", DBGTAGS);@/
+fprintf(stderr,"\t\t\t XX=%04X \t node debugging\n",DBGNODE);@/
+fprintf(stderr,"\t\t\t XX=%04X \t definition debugging\n", DBGDEF);@/
+fprintf(stderr,"\t\t\t XX=%04X \t directory debugging\n", DBGDIR);@/
+fprintf(stderr,"\t\t\t XX=%04X \t range debugging\n",DBGRANGE);@/
+fprintf(stderr,"\t\t\t XX=%04X \t float debugging\n", DBGFLOAT);@/
+fprintf(stderr,"\t\t\t XX=%04X \t compression debugging\n", DBGCOMPRESS);@/
+fprintf(stderr,"\t\t\t XX=%04X \t buffer debugging\n", DBGBUFFER);@/
+fprintf(stderr,"\t\t\t XX=%04X \t TeX debugging\n", DBGTEX);@/
+fprintf(stderr,"\t\t\t XX=%04X \t page debugging\n", DBGPAGE);@/
+fprintf(stderr,"\t\t\t XX=%04X \t font debugging\n", DBGFONT);@/
+exit(0);
+}
+#endif
+@
+
+
+
+\subsection{The Page Builder}\label{pagebuilder}
+The point where Hi\TeX\ goes an entirely different path than \TeX\ is the page builder:
+Instead of building a page, Hi\TeX\ writes a \HINT\ file.
+
+{
+\changestyle{build.tex}
+}
+
+
+
+\subsection{Adding new {\tt whatsit} Nodes}
+\TeX\ has a mechanism to extend it: the {\tt whatsit} nodes.
+For new concepts like baseline specifications, paragraphs, and boxes with unknown dimensions,
+we define now special {\tt whatsit} nodes. The new node definitions are supplemented by
+procedures to print them, copy them, delete them, and handle them in various contexts.
+
+{
+\changestyle{whatsit.tex}
+}
+
+\subsubsection{Freeing}
+Because some of the new nodes occur at places where \TeX\ originaly only
+handles box nodes,
+there are many places in \TeX\ where we can no longer just say |free_node(b, box_node_size)|
+to free a (box) node.
+Instead, we have to use the more general routine |flush_node_list|.
+This leads to a long series of simple replacements:
+
+{
+\changestyle{free.tex}
+}
+
+\subsubsection{Unpacking}
+In the function |unpackage| the pointer |p| might now point to an
+hset, vset, hpack or vpack node instead of a vbox or hbox node.
+We have to adapte the following test to this new situation.
+
+{
+\changestyle{unpackage.tex}
+}
+
+We continue to define auxiliar functions to create the new nodes.
+
+\subsubsection{Creating}
+
+The following functions create nodes for paragraphs, displayed equations, baseline skips,
+hpack nodes, vpack nodes, hset nodes, vset nodes, and image nodes.
+
+@<Hi\TeX\ routines@>=
+pointer new_graf_node(void)
+{ @+ pointer p;
+ p=get_node(graf_node_size);
+ type(p)=whatsit_node;
+ subtype(p)=graf_node;
+ graf_params(p)=null;
+ graf_list(p)=null;
+ return p;
+}
+
+
+pointer new_disp_node(void)
+{ @+ pointer p;
+ p=get_node(disp_node_size);
+ type(p)=whatsit_node;
+ subtype(p)=disp_node;
+ display_params(p)=null;
+ display_formula(p)=null;
+ display_eqno(p)=null;
+ return p;
+}
+
+pointer new_baseline_node(pointer bs, pointer ls, scaled lsl)
+{ @+pointer p;
+ p=get_node(baseline_node_size);
+ type(p)=whatsit_node;
+ subtype(p)=baseline_node;
+ baseline_node_no(p)=hget_baseline_no(bs, ls, lsl);
+ return p;
+}
+
+pointer new_pack_node(void)
+{ @+pointer p;
+ p=get_node(pack_node_size);
+ type(p)=whatsit_node;
+ subtype(p)=hpack_node;
+ width(p)=depth(p)=height(p)=shift_amount(p)=0;
+ pack_limit(p)=max_dimen;
+ list_ptr(p)=null;
+ return p;
+}
+pointer new_set_node(void)
+{ @+pointer p;
+ p=get_node(set_node_size);
+ type(p)=whatsit_node;
+ subtype(p)=hset_node;
+ width(p)=depth(p)=height(p)=shift_amount(p)=set_stretch(p)=set_shrink(p)=set_extent(p)=0;
+ list_ptr(p)=null;
+ return p;
+}
+
+
+
+
+pointer new_image_node( str_number n, char *a, char *e)
+{ pointer p;
+ int i;
+ char *fn;
+ int l;
+
+ p=get_node(image_node_size);type(p)=whatsit_node;subtype(p)=image_node;
+ image_name(p)=n;
+ image_area(p)=s_no(a);
+ image_ext(p)=s_no(e);
+ fn=hfile_name(n,a,e);
+#if 0
+ fn=kpse_find_tex(fn);
+#endif
+ i=hnew_file_section(fn);
+#if 0
+ free(fn);
+#endif
+ image_no(p)=i;
+ image_width(p)=image_height(p)=image_stretch(p)=image_shrink(p)=0;
+ image_shrink_order(p)=image_stretch_order(p)=normal;@/
+ return p;
+}
+
+
+
+@
+
+\subsubsection{Parameter nodes}
+
+Parameter nodes are added to the current list using the |add_par_node| function.
+It should be possible to check the parameter values against those
+stored in the definition section and remove the ones that
+are unchanged. It would make the parameter lists shorter, saving
+some time when setting and restoring them later.
+There is probably not much savings in memory space, because
+most of the times a reference number is found for the parameter list.
+@<Create the parameter node@>=
+ p=get_node(par_node_size);
+ type(p)=whatsit_node;
+ subtype(p)=par_node;
+ par_type(p)=t;
+ par_number(p)=n;
+@
+
+@<Initialize the parameter node@>=
+ if (t==int_type) par_value(p).i=v;
+ else if (t==dimen_type) par_value(p).sc=v;
+ else if (t==glue_type)
+ {@+ par_value(p).i=v;add_glue_ref(par_value(p).i); @+}
+ else
+ { free_node(p, par_node_size);
+ QUIT("Undefined parameter type %d",t);
+ }
+@
+
+@<Hi\TeX\ routines@>=
+void add_par_node(uint8_t t, uint8_t n, int v)
+{ @+pointer p;
+ @<Create the parameter node@>@;
+ @<Initialize the parameter node@>@;
+ link(p)=link(temp_head);
+ link(temp_head)=p;
+}
+@
+
+
+\subsection{Extended Dimensions}
+An extended dimension is a linar function of {\tt hsize} and {\tt vsize}, and
+whenever \TeX\ works with a dimension, we want it to be able to deal with
+an extended dimension. This implies many changes throughout \TeX's sources.
+So let's get started.
+
+{
+\changestyle{xdimen.tex}
+}
+
+
+
+
+\subsection{Hyphenation}
+While the breaking of a paragraph into lines must be postponed because
+{\tt hsize} is not known, hyphenation should be done as part of Hi\TeX\
+because we want to keep hyphenation out of the viewer. Therefore
+Hi\TeX\ will do hyphenation for all words within a paragraph.
+
+There is a fine point to observe here: \TeX\ will consider a word as
+a candidate for automatic hyphenation only if the world ``follows'' after a
+glue. (For the exact rules, see the \TeX book\cite{DK:texbook}, Appendix H.)
+As a consequence, \TeX\ usually does not submit the first word of a
+paragraph to its hyphenation routine.
+Viewing paragraphs that start with a lengthy word on a narrow display
+therefore often look more unsightly than necessary: the long word sticks out
+into the right margin as much as it can. To remedy this situation,
+Hi\TeX\ has a ``{\tt -f}'' option. If set Hi\TeX\ will deviate from
+\TeX's rules and submit the first word of a paragraph to the hyphenation algorithm.
+
+The next problem arises from \TeX's multipass approach to line breaking
+and the attempt to have Hi\TeX\ choose exactly the same line breaks as
+\TeX\ does:
+\TeX\ distingishes between discretionary breaks inserted by the author of a
+text, and discretionary breaks discovered by the hyphenation routine.
+The latter, called here ``automatic'', are used only in pass two and three
+of the line breaking routine.
+
+The change file that follows below contains mostly three types of changes:
+\itemize
+\item Hi\TeX\ restricts the |replace_count| to seven bit to make room for an
+extra bit to mark automatic discretionary breaks.
+Assignments to the replace count are therefore replaced by a macro that
+protects the automatic bit.
+\item The \TeX\ code for ``\<Try to hyphenate the following word>''
+is packaged into a function |hyphenate_word|, so that it can be invoked as needed.
+\item And calls of the function |line_break| are replaced by calls to |hline_break|
+to have a hook for inserting Hi\TeX specific changes.
+\enditemize
+
+{
+\changestyle{disc.tex}
+}
+
+The function |hline_break| follows:
+
+
+@<Hi\TeX\ routines@>=
+void hline_break(int final_widow_penalty)
+{@+ bool auto_breaking; /*is node |cur_p| outside a formula?*/
+ pointer r, s ; /*miscellaneous nodes of temporary interest*/
+ pointer pp;
+ bool par_shape_fix=false;
+ bool first_word=true;
+ if (DBGTEX&debugflags)
+ { print_ln();print("Before hline_break:\n");
+ breadth_max=200;
+ depth_threshold=200;
+ show_node_list(link(head));print_ln();
+ }
+ if (dimen_par_hfactor(hsize_code)==0 && dimen_par_vfactor(hsize_code)==0)
+ { line_break(final_widow_penalty); /* the easy case */
+ return;
+ }
+ /* Get ready to start line breaking */
+ pp=new_graf_node();
+ graf_penalty(pp)=final_widow_penalty;
+ if(par_shape_ptr==null)
+ graf_extent(pp)=new_xdimen(dimen_par(hsize_code),
+ dimen_par_hfactor(hsize_code),dimen_par_vfactor(hsize_code));
+ else
+ @<fix the use of parshape = 1 indent length@>@;
+ link(temp_head)=link(head);
+ if (is_char_node(tail))
+ { tail_append(new_penalty(inf_penalty))@;
+ tail_append(new_param_glue(par_fill_skip_code));
+ }
+ else if (type(tail)!=whatsit_node || subtype(tail)!=disp_node)
+ { 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);
+ }
+ DBG(DBGTEX,"\nCalling line_break:\n"
+ "hang_indent=0x%08X hang_after=%d",hang_indent,hang_after);
+ if (line_skip_limit!=0)
+ DBG(DBGTEX," line_skip_limit=0x%08X",line_skip_limit);
+ DBG(DBGTEX," prev_graf=0x%08X",prev_graf);
+
+ init_cur_lang=prev_graf%0200000;
+ init_l_hyf=prev_graf/020000000;
+ init_r_hyf=(prev_graf/0200000)%0100;
+ pop_nest();
+ DBG(DBGTEX," prev_graf=0x%08X",prev_graf);
+
+ /* 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;
+ if (DBGTEX&debugflags)
+ { print_ln();print("Before hyphenation:\n");
+ breadth_max=200;
+ depth_threshold=200;
+ show_node_list(link(temp_head));print_ln();
+ }
+ auto_breaking=true;
+ cur_p=temp_head;
+ if (option_hyphen_first && is_char_node(link(cur_p)))
+ { hyphenate_word(); first_word=false; }
+ cur_p=link(cur_p);
+ while (cur_p!=null)
+ { /*Call |try_break| if |cur_p| is a legal breakpoint...*/
+ if (is_char_node(cur_p))
+ { /* Advance |cur_p| to the node following the present string...*/
+ do {
+ cur_p=link(cur_p);
+ } while (is_char_node(cur_p));
+ if (cur_p==null) goto done5; /* mr: no glue and penalty at the end */
+ }
+ switch (type(cur_p))
+ { case whatsit_node:
+ adv_past(cur_p);
+ break;
+ case glue_node:
+ if (auto_breaking) /* Try to hyphenate the following word*/
+ hyphenate_word();
+ break;
+ case ligature_node:
+ break;
+ case disc_node:
+ /* Try to break after a discretionary fragment...*/
+ r=replace_count(cur_p);s=link(cur_p);
+ while (r > 0)
+ { decr(r);s=link(s);
+ }
+ cur_p=s;
+ goto done5;
+ case math_node:
+ auto_breaking=(subtype(cur_p)==after);
+ break;
+ default:
+ break;
+ }
+ if (option_hyphen_first && first_word && is_char_node(link(cur_p)))
+ { hyphenate_word(); first_word=false; }
+ cur_p=link(cur_p);
+done5:;
+ }
+ if (DBGTEX&debugflags)
+ { print_ln();print("After hline_break:\n");
+ breadth_max=200;
+ depth_threshold=200;
+ show_node_list(link(temp_head));print_ln();
+ }
+ graf_list(pp)=link(temp_head);
+ /* adding parameter nodes */
+ link(temp_head)=null;
+
+ add_par_node(int_type,pretolerance_code,pretolerance);
+ add_par_node(int_type,tolerance_code,tolerance);
+ add_par_node(dimen_type,emergency_stretch_code,emergency_stretch);
+
+ add_par_node(int_type,line_penalty_code,line_penalty);
+ add_par_node(int_type,hyphen_penalty_code,hyphen_penalty);
+ add_par_node(int_type,ex_hyphen_penalty_code,ex_hyphen_penalty);
+ add_par_node(int_type,club_penalty_code,club_penalty);
+ add_par_node(int_type,widow_penalty_code,widow_penalty);
+ add_par_node(int_type,broken_penalty_code,broken_penalty);
+ add_par_node(int_type,inter_line_penalty_code,inter_line_penalty);
+ add_par_node(int_type,double_hyphen_demerits_code,double_hyphen_demerits);
+ add_par_node(int_type,final_hyphen_demerits_code,final_hyphen_demerits);
+ add_par_node(int_type,adj_demerits_code,adj_demerits);
+ add_par_node(int_type,looseness_code,looseness);
+
+ if (par_shape_fix)
+ { add_par_node(int_type,hang_after_code,0);
+ add_par_node(dimen_type,hang_indent_code,second_indent);
+ }
+ else
+ { add_par_node(int_type,hang_after_code,hang_after);
+ add_par_node(dimen_type,hang_indent_code, hang_indent);
+ }
+
+ add_par_node(dimen_type,line_skip_limit_code,line_skip_limit);
+ add_par_node(glue_type,line_skip_code,line_skip);
+ add_par_node(glue_type,baseline_skip_code,baseline_skip);
+
+ add_par_node(glue_type,left_skip_code,left_skip);
+ add_par_node(glue_type,right_skip_code,right_skip);
+ add_par_node(glue_type,par_fill_skip_code,par_fill_skip);
+
+
+ /* |par_shape| is not yet supported */
+ graf_params(pp)=link(temp_head);
+ link(temp_head)=null;
+ append_to_vlist(pp);
+}
+@
+
+Currently Hi\TeX\ dos not implement the parshape feature of \TeX.
+The implementation of {\tt \BS list} in \LaTeX\ does however depend
+on a simple us of parshape where all lines have the same length
+and indentation. We cover this special case be using a hanging
+indentation and adjusting the paragraph width by the difference
+of the normal {\tt \BS hsize} and the given length.
+
+@<fix the use of parshape = 1 indent length@>=
+{ last_special_line= info(par_shape_ptr)-1;
+ if (last_special_line!=0)
+ DBG(DBGTEX,"Warning parshape with n=%d not yet implemented",info(par_shape_ptr));
+ second_width= mem[par_shape_ptr+2*(last_special_line+1)].sc;
+ second_indent= mem[par_shape_ptr+2*last_special_line+1].sc;
+
+ graf_extent(pp)=new_xdimen(second_indent+second_width,
+ par_shape_hfactor,par_shape_vfactor);
+ second_width=second_width+ round((double)par_shape_hfactor*hhsize/unity
+ +(double)par_shape_vfactor*hvsize/unity);
+ par_shape_fix=true;
+}
+@
+
+\subsection{Baseline Skips}
+
+\TeX\ will automatically insert a baseline skip between two boxes in a
+vertical list. The baseline skip is computed to make the distance
+between the baselines of the two boxes exactly equal to the value of
+{\tt \BS baselineskip}. And if that is not possible, because it would
+make the distance between the descenders of the upper box and the
+ascenders of the lower box smaller than {\tt \BS lineskiplimit}, it
+will insert at least a small amount of glue of size {\tt \BS
+lineskip}. As a further complication, if the depth of the upper box
+has the special value |ignore_depth| the insertion of a baseline skip
+is suppressed. It is also common practice that authors manipulate the
+values of {\tt \BS baselineskip}, {\tt \BS lineskiplimit}, {\tt \BS
+lineskip}, and {\tt \BS prevdepth} to change the baseline calculations
+of \TeX.
+
+Of course a prerequisite of the whole computation of a baseline skip
+is the knowledge of the depth of the upper box and the height of the
+lower box. With the new types of boxes introduced by Hi\TeX\ neither
+of them might be known. In these cases, the baseline skip computation
+must be defered to the viewer by inserting a whatsit node of subtype
+|baseline_node|.
+
+
+{
+\changestyle{baseline.tex}
+}
+
+
+\subsection{Displayed Formulas}
+
+\TeX\ enters into math mode when it finds a math shift character ``\$''.
+Then it calls |init_math| which checks for an aditional math shift
+character to call either
+ \<Go into ordinary math mode> or \<Go into display math mode>.
+We remove most of the differences from the latter because we will
+treat both cases very similar here and will consider the necessary
+differences in the \HINT\ viewer.
+
+When the shift character that terminates math more is encountered,
+\TeX\ calls |after_math| which ends with
+either \<Finish math in text> or \<Finish displayed math> .
+We need to modify the latter, because positioning a displayed
+equation usually depends on {\tt hsize},
+and must be postponed until the viewer knows its value.
+So all Hi\TeX\ can do in this case is create a new whatsit node
+with subtype |disp_node| and
+insert it proceeding as if the formula had occured in ordinary math mode.
+
+
+{
+\changestyle{display.tex}
+}
+
+
+
+
+\subsection{Alignments}
+
+
+{
+\changestyle{align.tex}
+}
+
+
+\subsection{Inserts}
+\TeX\ uses |vpack| to determine the natural height of inserted
+material. In Hi\TeX, the function |vpack| will not always return
+a |vlist_node| with a fixed height because the computation of the
+height might fail. In the latter case, |vpack| will return a |vset_node|
+or |vpack_node|. Therfore Hi\TeX\ will not use the |height| field of
+a an |ins_node|.
+
+As a result we have the following changes:
+
+
+{
+\changestyle{insert.tex}
+}
+
+
+
+
+\subsection{Implementing Hi\TeX\ Specific Primitives}\label{primitive}
+Now we implement new \TeX\ primitives:
+one to include images, a few more to define page teamplates,
+and one to test whether the current instance of \TeX\ is
+the new Hi\TeX.
+To implement a primitive, we call the |primitive| function.
+Since it needs the name of the new primitive as a \TeX\ string,
+we need to add the strings to the \TeX\ string pool.
+
+Note that primitives are put into \TeX's memory when a \TeX\ format file is read.
+So adding or changing the code below implies regenerating the format files.
+To generate a format file run a command like
+
+{\tt\obeylines
+hinitex
+**plain \BS dump
+}
+
+\noindent
+Then find out where the \kpse\ library looks for format files
+(for example by running
+
+{\tt\obeylines
+ kpsewhich latex.fmt
+}
+
+\noindent
+and then place the new format file in the same directory.
+
+
+{
+\changestyle{primitive.tex}
+}
+
+
+
+
+\section{Hi\TeX}
+
+\subsection{Images}
+The handling of images is an integral part of Hi\TeX. In
+section~\secref{imageext} on page~\pageref{imageext} the {\tt image}
+primitive was defined. It requires a filename for the image and
+allows the specification, of width, height, stretch and shrink of an
+image. If either width or height is not given, Hi\TeX\ tries to
+extract this information from the image file itself calling the
+function |hget_image_information| with the pointer |p| to the image
+node as parameter.
+
+@<Hi\TeX\ routines@>=
+void hget_image_information(pointer p)
+{ char *fn;
+ FILE *f;
+ if (image_width(p)!=0 && image_height(p)!=0) return;
+ fn=dir[image_no(p)].file_name;
+ f=fopen(fn,"rb");
+ if (f==NULL) QUIT("Unable to open image file %s.", fn);
+ MESSAGE("(%s",fn);
+ img_buf_size=0;
+ if (!get_BMP_info(f,fn,p)&&!get_PNG_info(f,fn,p)&&!get_JPG_info(f,fn,p))
+ QUIT("Unable to obtain width and height information for image %s",fn);
+ fclose(f);
+ MESSAGE(" width= %fpt height= %fpt)",image_width(p)/(double)ONE,image_height(p)/(double)ONE);
+}
+@
+
+When we have found the width and height of the stored image, we can
+supplement the information given in the \TeX\ file. Occasionaly, the
+image file will not specify the absolute dimensions of the image. In
+this case, we can still compute the aspect ratio and supplement either
+the width based on the height or vice versa. This is accomplished by
+calling the |set_image_dimensions| function. It returns true on
+success and false otherwise.
+
+@<Hi\TeX\ auxiliar routines@>=
+static bool set_image_dimensions(pointer p, double w, double h, bool absolute)
+{ if (image_width(p)!=0)
+ { double aspect=h/w;
+ image_height(p)=round(image_width(p)*aspect);
+ }
+ else if (image_height(p)!=0)
+ { double aspect=w/h;
+ image_width(p)=round(image_height(p)*aspect);
+ }
+ else
+ { if (!absolute) return false;
+ image_width(p)=round(unity*w);
+ image_height(p)=round(unity*h);
+ }
+ return true;
+}
+@
+We call the following routines with the image buffer partly filled
+with the start of the image file.
+
+@<Hi\TeX\ auxiliar routines@>=
+#define IMG_BUF_MAX 54
+#define IMG_HEAD_MAX 2
+static unsigned char img_buf[IMG_BUF_MAX];
+static size_t img_buf_size;
+#define @[LittleEndian32(X)@] (img_buf[(X)]+(img_buf[(X)+1]<<8)+\
+ (img_buf[(X)+2]<<16)+(img_buf[(X)+3]<<24))
+
+#define @[BigEndian16(X)@] (img_buf[(X)+1]+(img_buf[(X)]<<8))
+
+#define @[BigEndian32(X)@] (img_buf[(X)+3]+(img_buf[(X)+2]<<8)+\
+ (img_buf[(X)+1]<<16)+(img_buf[(X)]<<24))
+
+#define Match2(X,A,B) ((img_buf[(X)]==(A)) && (img_buf[(X)+1]==(B)))
+#define Match4(X,A,B,C,D) (Match2(X,A,B)&&Match2((X)+2,C,D))
+
+#define @[GET_IMG_BUF(X)@] \
+if (img_buf_size<X) \
+ { size_t i=fread(img_buf+img_buf_size,1,(X)-img_buf_size,f); \
+ if (i<0) QUIT("Unable to read image %s",fn); \
+ else if (i==0) QUIT("Unable to read image header %s",fn); \
+ else img_buf_size+=i; \
+ }
+@
+
+\subsubsection{BMP}
+We start with Windows Bitmaps. A Windows bitmap file usually has the extension {\tt .bmp}
+but the better way to check for a Windows bitmap file ist to examine the first two byte
+of the file: the ASCII codes for `B' and `M'.
+Once we have verified the file type, we find the width and height of the bitmap in pixels
+at offsets |0x12| and |0x16| stored as little-endian 32 bit integers. At offsets |0x26| and |0x2A|,
+we find the horizontal and vertical resolution in pixel per meter stored in the same format.
+This is sufficient to compute the true width and height of the image in scaled points.
+If either the width or the height was given in the \TeX\ file, we just compute the aspect ratio
+and compute the missing value.
+
+The Windows Bitmap format is easy to process but not very efficient. So the support for this
+format in Hi\TeX\ is deprecated and will disappear. You should use one of the formats described next.
+
+@<Hi\TeX\ auxiliar routines@>=
+static bool get_BMP_info(FILE *f, char *fn, pointer p)
+{ double w,h;
+ double xppm,yppm;
+ GET_IMG_BUF(2);
+ if (!Match2(0,'B','M')) return false;
+ GET_IMG_BUF(0x2E);
+ w=(double)LittleEndian32(0x12); /*width in pixel*/
+ h=(double)LittleEndian32(0x16); /*height in pixel*/
+ xppm=(double)LittleEndian32(0x26); /* horizontal pixel per meter*/
+ yppm=(double)LittleEndian32(0x2A); /* vertical pixel per meter*/
+ return set_image_dimensions(p,(72.27*1000.0/25.4)*w/xppm,(72.27*1000.0/25.4)*h/yppm,true);
+}
+@
+
+\subsubsection{PNG}
+Now we repeat this process for image files in using the Portable Network Graphics\cite{png2nd} file
+format. This file format is well suited to simple graphics that do not use color gradients.
+These images usually have the extension {\tt .png} and start with an eight byte signature:
+|0x89| followed by the ASCII Codes `P', `N', `G', followd by a carriage return (|0x0D| and line feed (|0x0A|),
+an DOS end-of-file character (|0x1A|) and final line feed (|0x0A|).
+After the signature follows a list of chunks. The first chunk is the image header chunk.
+Each chunk starts with the size of the chunk stored as big-endian 32 bit integer, followed by the chunk name
+stored as four ASCII codes followed by the chunk data and a CRC. The size as stored in the chunk
+does not include the size itself, the name or the CRC.
+The first chunk is the IHDR chunk.
+The chunk data of the IHDR chunk starts with the width and the height of the image in pixels
+stored as 32 bit big-endian integers.
+
+Finding the image resolution takes some more effort. The image resolution is stored in an optional chunk
+named ``pHYs'' for the physical pixel dimensions.
+All we know is that this chunk, if it exists, will appear after the IHDR
+chunk and before the (required) IDAT chunk. The pHYs chunk contains two 32 bit big-endian integers,
+giving the horizontal and vertical pixels per unit, and a one byte unit specifier, which is either 0
+for an undefined unit or 1 for the meter as unit. With an undefined unit only the aspect ratio
+of the pixels anh hence the aspect ratio of the image can be determined.
+
+
+@<Hi\TeX\ auxiliar routines@>=
+
+static bool get_PNG_info(FILE *f, char *fn, pointer p)
+{ int pos, size;
+ double w,h;
+ double xppu,yppu;
+ int unit;
+ GET_IMG_BUF(24);
+ if (!Match4(0, 0x89, 'P', 'N', 'G') ||
+ !Match4(4, 0x0D, 0x0A, 0x1A, 0x0A)) return false;
+ size=BigEndian32(8);
+ if (!Match4(12,'I', 'H', 'D', 'R')) return false;
+ w=(double)BigEndian32(16);
+ h=(double)BigEndian32(20);
+ pos=20+size;
+ while (true)
+ { if (fseek(f,pos,SEEK_SET)!=0) return false;
+ img_buf_size=0;
+ GET_IMG_BUF(17);
+ size=BigEndian32(0);
+ if (Match4(4,'p', 'H', 'Y', 's'))
+ { xppu =(double)BigEndian32(8);
+ yppu =(double)BigEndian32(12);
+ unit=img_buf[16];
+ if (unit==0)
+ return set_image_dimensions(p,w/xppu,h/yppu,false);
+ else if (unit==1)
+ return set_image_dimensions(p,(72.27/0.0254)*w/xppu,(72.27/0.0254)*h/yppu,true);
+ else
+ return false;
+ }
+ else if (Match4(4,'I', 'D', 'A', 'T'))
+ return set_image_dimensions(p,w,h,false);
+ else
+ pos=pos+12+size;
+ }
+ return false;
+}
+@
+
+\subsubsection{JPG}
+For photographs, the JPEG File Interchange Format (JFIF)\cite{Ham92}\cite{JPEG93} is more appropriate.
+JPEG files come with all sorts of file extensions like {\tt .jpg}, {\tt .jpeg}, or {\tt .jfif}.
+We check the file siganture: it starts with the the SOI (Start of Image) marker |0xFF|, |0xD8|
+followed by the JIFI-Tag. The JIFI-Tag starts with the segment marker APP0 (|0xFF|, |0xE0|) followed by the
+2 byte segment size, followed by the ASCII codes `J', `F', `I', `F' followed by a zero byte.
+Next is a two byte version number which we do not read.
+Before the resolution proper there is a resolution unit indicator byte (0 = no units,
+1 = dots per inch, 2 = dots per cm) and then comes the horizontal and vertical resolution both
+as 16 Bit big-endian integers.
+To find the actual width and height, we have to search for a start of frame marker (|0xFF|, |0xC0|+$n$ with $0\le n\le 15$). Which is followed by the 2 byte segment size, the 1 byte sample precission, the
+2 byte height and the 2 byte width.
+
+
+@<Hi\TeX\ auxiliar routines@>=
+
+static bool get_JPG_info(FILE *f, char *fn, pointer p)
+{ int pos, size;
+ double w,h;
+ double xppu,yppu;
+ int unit;
+ GET_IMG_BUF(18);
+
+ if (!Match4(0, 0xFF,0xD8, 0xFF, 0xE0)) return false;
+ size=BigEndian16(4);
+ if (!Match4(6,'J', 'F', 'I', 'F')) return false;
+ if (img_buf[10] != 0) return false;
+ unit=img_buf[13];
+ xppu=(double)BigEndian16(14);
+ yppu=(double)BigEndian16(16);
+ pos=4+size;
+ while (true)
+ { if (fseek(f,pos,SEEK_SET)!=0) return false;
+ img_buf_size=0;
+ GET_IMG_BUF(10);
+ if (img_buf[0] != 0xFF) return false; /* Not the start of a segment */
+ if ( (img_buf[1]&0xF0) == 0xC0) /* Start of Frame */
+ { h =(double)BigEndian16(5);
+ w =(double)BigEndian16(7);
+ if (unit==0)
+ return set_image_dimensions(p,w/xppu,h/yppu,false);
+ else if (unit==1)
+ return set_image_dimensions(p,72.27*w/xppu,72.27*h/yppu,true);
+ else if (unit==2)
+ return set_image_dimensions(p,(72.27/2.54)*w/xppu,(72.27/2.54)*h/yppu,true);
+ else
+ return false;
+ }
+ else
+ { size= BigEndian16(2);
+ pos=pos+2+size;
+ }
+ }
+ return false;
+}
+@
+
+\subsubsection{SVG}
+There is still one image format missing: scalable vector graphics.
+In the moment, I tend not to include a further image format into
+the definition of the \HINT\ file format but instead use the
+PostScript subset that is used for Type 1 fonts to encode
+vector graphics. Any \HINT\ viewer must support Type 1
+PostScript fonts and hence it has already the necessary interpreter.
+So it seems reasonable to put the burden of converting vector graphics
+into a Type 1 PostScript font on the generator of \HINT\ files
+and keep the \HINT\ viewer as small and simple as possible.
+
+
+\subsection{Links, Labels, and Outlines}
+The \HINT\ format knows about labels, links, and outlines.
+When generating a short format \HINT\ file, links are part of
+the content section, where as labels and outlines are found in
+the definition section. Because labels are defined while
+writing the content section, the writing of labels and outlines, which
+reference the labels, must be postponed. For that reason,
+we store information about labels and outlines in dynamic arrays,
+and map labels, which are identified by a name or a number,
+to their index using a dynamic hash table.
+
+We start with two functions that allocate new entries in the
+dynamic arrays increasing their size if necessary.
+@<Hi\TeX\ auxiliar routines@>=
+static int next_label(void)
+{ static int label_no=-1;
+ static int labels_allocated =0;
+ label_no++;
+ if (label_no>0xFFFF)
+ overflow("labels",0xFFFF);
+ if (label_no>=labels_allocated)
+ { if (labels_allocated==0)
+ { labels_allocated=32; ALLOCATE(labels,labels_allocated,label_t); }
+ else RESIZE(labels,labels_allocated,label_t);
+ }
+ max_ref[label_kind]=label_no;
+ return label_no;
+}
+
+static int next_outline(void)
+{ static int outlines_allocated =0;
+ static int outline_no=-1;
+ outline_no++;
+ if (outline_no>0xFFFF)
+ overflow("outlines",0xFFFF);
+ if (outline_no>=outlines_allocated)
+ { if (outlines_allocated==0)
+ { outlines_allocated=32; ALLOCATE(outlines,outlines_allocated,outline_t); }
+ else RESIZE(outlines,outlines_allocated,outline_t);
+ }
+ max_outline=outline_no;
+ return outline_no;
+}
+@
+
+While processing the content nodes, access to the labels is provided either
+by name or by number through a hash table. We store tabel entries in linked
+lists starting with a reasonably sized table of pointers. This keeps
+the fixed costs low and guards against overflow and rapidly increasing
+inefficiency. We start with a function to insert a new entry into
+the hash table.
+
+@<Hi\TeX\ auxiliar routines@>=
+typedef struct hash_entry
+{int num; char *nom; uint16_t n; struct hash_entry *next;} hash_entry_t;
+#define LABEL_HASH 1009 /* MIX a prime number */
+static hash_entry_t *label_hash[LABEL_HASH]={NULL};
+
+static int insert_hash(int h, int num, char *nom)
+{ hash_entry_t *e;
+ ALLOCATE(e,1,hash_entry_t);
+ e->n= next_label();
+ if (nom!=NULL) e->nom=strdup(nom);
+ else e->num=num;
+ e->next= label_hash[h];
+ label_hash[h]=e;
+ if (e->nom!=NULL)
+ DBG(DBGLABEL,"Creating new label *%d: name='%s'\n",e->n,e->nom);
+ else
+ DBG(DBGLABEL,"Creating new label *%d: num=%d\n",e->n,e->num);
+ return e->n;
+}
+@
+
+There are two cases: finding a label by name or by number.
+We start with the simpler case where the number is given.
+The process is straigth forward:
+
+@<Hi\TeX\ auxiliar routines@>=
+static int find_label_by_number(int p)
+{ unsigned int h=(unsigned int)p%LABEL_HASH;
+ hash_entry_t *e= label_hash[h];
+ while (e!=NULL)
+ if (e->nom==NULL && e->num==p) return e->n;
+ else e=e->next;
+ return insert_hash(h,p,NULL);
+}
+@
+
+To look up a label by its name as given by a token list,
+we prepare ourselfs by implementing two functions:
+one to extract the character codes from the token list
+forming the ``name''
+and one to compute the hash value for a name.
+The routine to find the label by name is then equivalent to the
+routine we have just seen. Given a pointer |p| to
+either a label, a link, or an outline node, the last function
+returns the correct label reference.
+Currently, we limit label names to at most 255 byte not counting the
+zero byte.
+
+@<Hi\TeX\ auxiliar routines@>=
+static char *tokens_to_name(pointer p)
+{ static char s[256];
+ int i=0;
+ bool skip_space=0;
+ while (i<255 && p!=0)@/
+ { int m = info(p)/0400;@+
+ int c = info(p)%0400;
+ if (m==spacer && ! skip_space) @/
+ { s[i++]=' '; skip_space=true;@+}
+ else if ((m==letter || m==other_char) && ' '< c && c < 0x7F)@/
+ {@+ s[i++]=c; skip_space=false;@+}
+ p=link(p);
+ }
+ s[i]=0;
+ return s;
+}
+
+static unsigned int name_hash(char *s)
+{ unsigned int h=0;
+ while (*s!=0)
+ h=(h<<2)+*(s++);
+ return h;
+}
+
+static int find_label_by_name(pointer p)
+{ char *s=tokens_to_name(link(p));
+ unsigned int h=name_hash(s)%LABEL_HASH;
+ hash_entry_t *e= label_hash[h];
+ while (e!=NULL)
+ if (e->nom!=NULL && strcmp(e->nom,s)==0) return e->n;
+ else e=e->next;
+ return insert_hash(h,0,s);
+}
+@
+
+We combine both ways of finding a label reference in the following function:
+
+@<Hi\TeX\ auxiliar routines@>=
+static int find_label(pointer p)
+{@+ if (label_has_name(p)) return find_label_by_name(label_ptr(p));
+ else return find_label_by_number(label_ptr(p));
+}
+@
+
+After these preparations, we can implement the functions needed
+when labels, links, and outlines are delivered to the page builder.
+
+We start with looking at the labels:
+When a labels is defined, the current position is recorded.
+Further labels are linked together in order of descending positions,
+to allow the efficient adjustment of label positions when
+moving lists.
+
+@<Hi\TeX\ auxiliar routines@>=
+static void new_label(pointer p)
+{ int n=find_label(p);
+ if (n!=zero_label_no && labels[n].where!=LABEL_UNDEF)
+ { MESSAGE("WARNING: Ignoring duplicate definition of label ");
+ if (label_has_name(p)) MESSAGE("name %s\n",tokens_to_name(link(label_ptr(p))));
+ else MESSAGE("num %d\n",label_ptr(p));
+ }
+ else
+ { labels[n].where=label_where(p);
+ labels[n].pos=hpos-hstart;
+ labels[n].pos0=hpos0-hstart;
+ labels[n].next=first_label;
+ first_label=n;
+ DBG(DBGLABEL,"Defining label *%d: pos=0x%x\n",n,labels[n].pos);
+ }
+}
+@
+
+When a link node is written to the output, we can check
+that start links and end links properly match.
+
+@<Hi\TeX\ auxiliar routines@>=
+static int last_link=-1;
+static int new_start_link(pointer p)
+{ int n=find_label(p);
+ if (last_link>=0)
+ fatal_error("Missing end link before start link");
+ labels[n].used=true;
+ last_link=n;
+ DBG(DBGLABEL,"New link to label *%d\n",n);
+ return n;
+}
+
+static int new_end_link(void)
+{ int n;
+ if (last_link<0)
+ fatal_error("Missing start link before end link");
+ n=last_link;
+ last_link=-1;
+ return n;
+}
+@
+
+For outline nodes, we use the next two functions.
+The node list representing the title can be an arbitrary
+list in horizontal mode.
+In general, the front end should be able to render such a
+horizontal list, but at least it should be able to extract
+the UTF8 characters codes and display those.
+
+@<Hi\TeX\ auxiliar routines@>=
+static void new_outline(pointer p)
+{ int r=find_label(p);
+ int m=next_outline();
+ list_t l;
+ uint32_t pos;
+ pos=hpos-hstart;
+ l.k=list_kind; /* this eventually should be |text_kind| */
+ hout_list_node(outline_ptr(p),pos,&l);
+ hset_outline(m,r,outline_depth(p),pos);
+ DBG(DBGLABEL,"New outline for label *%d\n",r);
+}
+@
+
+One last function is needed, which better schould be in the {\tt htex.w}
+file. It is called when the |outline_group| ends that was started
+when scanning the {\tt\BS HINToutline} primitive.
+@<Hi\TeX\ routines@>=
+void hfinish_outline_group(void)
+{ pointer s=link(head);
+ unsave();
+ pop_nest();
+ outline_ptr(tail)=s;
+}
+@
+
+\subsection{The New Page Builder}\label{buildpage}
+Here is the new |build_page| routine of Hi\TeX:
+
+@<Hi\TeX\ routines@>=
+void build_page(void)
+{ static bool initial=true;
+ if(link(contrib_head)==null||output_active)return;
+ do
+ { pointer p= link(contrib_head);
+ pointer q=null; /* for output nodes */
+ pointer *t; /*the tail of the output nodes*/
+ @<Record the bottom mark@>@;
+ @<Suppress empty pages if requested@>@;
+ link(contrib_head)= link(p);link(p)= null;
+ if (link(contrib_head)==null)
+ { if(nest_ptr==0) tail= contrib_head;
+ else contrib_tail= contrib_head;
+ }
+ update_last_values(p);
+ @<Freeze the page specs if called for@>@;
+ page_goal=0x3fffffff; /* maximum dimension */
+ t=collect_output(&p,&q);
+ if (p!=null)
+ { hpos0=hpos; hout_node(p); }
+recycle_p:
+ flush_node_list(p);
+ if (q!=null)
+ @<Fire up the output routine for |q|@>@;
+ } while(link(contrib_head)!=null);
+ DBG(DBGBUFFER,"after build page dyn_used= %d\n", dyn_used);
+}
+@
+
+@<Freeze the page specs if called for@>=
+if (page_contents<box_there)
+{ switch(type(p))
+ { case whatsit_node:
+ if (subtype(p)==baseline_node) goto recycle_p;
+ else if (subtype(p)!=hset_node && subtype(p)!=vset_node &&
+ subtype(p)!=hpack_node && subtype(p)!=vpack_node &&
+ subtype(p)!=graf_node && subtype(p)!=disp_node &&
+ subtype(p)!=image_node && subtype(p)!=align_node)
+ break; /* else fall through */
+ case hlist_node: case vlist_node: case rule_node:
+ if (page_contents==empty)
+ { freeze_page_specs(box_there);
+ hfix_defaults();
+ }
+ else page_contents=box_there;
+ break;
+ case ins_node:
+ if (page_contents==empty)
+ { freeze_page_specs(inserts_only);
+ hfix_defaults();
+ }
+ break;
+ case kern_node:
+ case penalty_node:
+ case glue_node: goto recycle_p;
+ default:
+ break;
+ }
+}
+@
+
+Users of \TeX\ often force the generation of empty pages for example to start
+a new chapter on a right hand page with an odd page number.
+This makes sense for a printed book but not for a screen reader where
+there are no page numbers nor right or left hand pages.
+Using a screen reader, empty pages are just annoying.
+The common way to achive an empty page is the use of {\tt \BS eject}
+followed by a an empty box, a fill glue, and another {\tt \BS eject}.
+
+The following code tries to detect such a sequence of nodes and will eliminate
+them if requested. To do so, we delay the output of nodes after
+an eject penalty until either something gets printed on the page or
+another eject penalty comes along. To override the delayed output,
+a penalty less or equal to a double |eject_penalty| can be used.
+The function |its_all_over| (see section~\secref{itsallover})
+is an example for such a use.
+
+
+@<Suppress empty pages if requested@>=
+if (option_no_empty_page &&
+ ((type(p)==penalty_node &&
+ penalty(p)<=eject_penalty && penalty(p)>2*(eject_penalty)) ||
+ (page_contents==empty && !is_visible(p))))
+{ pointer q= link(p);
+ while (true)
+ { if (q==null) return;
+ else if (is_visible(q)) break;
+ else if (type(q)==penalty_node && penalty(q)<=eject_penalty)
+ { while (p!=q)
+ { pointer r=p;
+ DBG(DBGPAGE,"Eliminating node (%d,%d)\n",
+ type(p), type(p)==penalty_node?penalty(p):subtype(p));
+ p=link(p);
+ link(r)=null;
+ flush_node_list(r);
+ }
+ link(contrib_head)= p;
+ DBG(DBGPAGE,"Eliminating empty page done\n");
+ if (penalty(q)<=2*(eject_penalty)) break;
+ }
+ q=link(q);
+ }
+}
+@
+It remains to test a node for visibility. This is a quick (and dirty) test
+because the test will not look inside boxes; it simply tests whether
+the list pointer is |null|. We consider an |open_node|, |write_node|,
+|close_node|, |label_node|, or |outline_node| a as visible,
+because deleting them could cause unwanted
+side effects. Possibly it would be better to regard them as invisible,
+but still pass them on to the rest of the output routine.
+
+@<Hi\TeX\ auxiliar routines@>=
+static bool is_visible(pointer p)
+{ switch (type(p))
+ { case penalty_node:
+ case kern_node:
+ case glue_node:
+ case mark_node:
+ return false;
+ case ins_node:
+ return ins_ptr(p)!=null;
+ case adjust_node:
+ return adjust_ptr(p)!=null;
+ case hlist_node:
+ case vlist_node:
+ return list_ptr(p)!=null;
+ case whatsit_node:
+ if (subtype(p)==image_node || subtype(p)==align_node || subtype(p)==disp_node ||
+ subtype(p)==open_node ||subtype(p)==write_node ||subtype(p)==close_node ||
+ subtype(p)==label_node || subtype(p)==outline_node )
+ return true;
+ else if (subtype(p)==hset_node || subtype(p)==vset_node ||
+ subtype(p)==hpack_node || subtype(p)==vpack_node)
+ return list_ptr(p)!=null;
+ else if (subtype(p)==graf_node)
+ return graf_list(p)!=null;
+ else
+ return false;
+ default: return true;
+ }
+}
+@
+
+An important feature of the new routine is the call to
+|hfix_defaults|. It occurs when the first ``visible mark'' is placed
+on the page. At that point we record the current values of \TeX's
+parameters which we will use to generate the definition section of the
+\HINT\ file. It is still possible to specify alternative values for
+these parameters by using parameter lists but only at an additional
+cost in space and time.
+
+Furthermore, this is the point where we freeze the definition of
+|hsize| and |vsize|. The current values will be regarded as the sizes
+as recommended by the author.
+
+From then on |hsize| and |vsize| are replaced by the equivalent
+extended dimensions and any attempt to modify them on the global level
+will be ignored. |hhsize| and |hvsize| will contain the sizes that a
+regular \TeX\ engine would use.
+
+We also compute the total page size from the page template defined
+last.
+
+
+@<Compute the page size@>=
+{ pointer p;
+ p=link(setpage_head);
+ if (p==null)
+ { scaled margin;
+ if (hhsize<hvsize) margin=hhsize; else margin=hvsize;
+ margin = margin/6 -6*unity;
+ if (margin<0) margin=0;
+ page_h=hhsize+2*margin;
+ page_v=hvsize+2*margin;
+ }
+ else
+ { pointer x;
+ x=setpage_height(p);
+ page_v=xdimen_width(x)
+ +round(((double)xdimen_hfactor(x)*hhsize+(double)xdimen_vfactor(x)*hvsize)/unity);
+ x=setpage_width(p);
+ page_h=xdimen_width(x)
+ +round(((double)xdimen_hfactor(x)*hhsize+(double)xdimen_vfactor(x)*hvsize)/unity);
+ }
+}
+@
+
+@<Hi\TeX\ variables@>=
+static scaled page_h, page_v;
+@
+
+@<Switch |hsize| and |vsize| to extended dimensions@>=
+ hsize=0; vsize=0;
+ dimen_par_hfactor(hsize_code)= unity;
+ dimen_par_vfactor(vsize_code)= unity;
+@
+
+There is one point where we can not simpy forego the
+output routine: \.{\\write} commands. Unless the \.{\\write} is
+decorated with an \.{\\immediate}, the whatsit node generated from it
+will lay dormant in the contribution list and then the page until
+the output routine passes it inside the finished page to the |ship_out|
+routine where it will come to life and write its token list out.
+The whatsit nodes from \.{\\openout} and \.{\\closeout} commands
+behave similary.
+
+It is not possible to ignore the output routine
+because the output routine may change the environment in which the
+token list of a \.{\\write} will be expanded.
+For example \LaTeX\ redefines \.{\\protect} to be \.{\\noexpand}.
+As a consequence we have to implement a simplified version
+of \TeX's usual process to fire up the output routine.
+
+The |collect_output| routine takes a node list |p|,
+removes the output nodes and appends them to |q|, with |q|
+always pointing to the tail pointer.
+
+@<Hi\TeX\ auxiliar routines@>=
+static pointer *collect_output(pointer *p, pointer *q)
+{ while (*p!=null)
+ { @<Collect output nodes from |*p|@>@;
+ p=&(link(*p));
+ }
+ return q;
+}
+@
+
+\TeX\ does not permit output nodes in leaders, so we dont check them;
+further we do not check the pre- and post-break lists of
+discretionary breaks.
+
+@<Collect output nodes from |*p|@>=
+if (!is_char_node(*p))
+{ pointer r=*p;
+ switch (type(r))
+ { case whatsit_node:
+ switch (subtype(r))
+ { case open_node: case write_node: case close_node:
+ { *p=link(r); link(r)=null; *q=r; q=&(link(r));
+ if (*p==null) return q;
+ }
+ break;
+ case graf_node: q=collect_output(&graf_list(r),q);
+ break;
+ case disp_node:
+ if (display_left(r)) q=collect_output(&display_eqno(r),q);
+ q=collect_output(&display_formula(r),q);
+ if (!display_left(r)) q=collect_output(&display_eqno(r),q);
+ break;
+ case hset_node: case vset_node: case hpack_node: case vpack_node:
+ q=collect_output(&list_ptr(r),q);
+ break;
+ case align_node:
+ q=collect_output(&align_list(r),q);
+ break;
+ default: break;
+ }
+ break;
+ case hlist_node: case vlist_node:
+ q=collect_output(&list_ptr(r),q);
+ break;
+ case ins_node:
+ q=collect_output(&ins_ptr(r),q);
+ break;
+ case adjust_node:
+ q=collect_output(&adjust_ptr(r),q);
+ break;
+ default: break;
+ }
+}
+@
+
+
+
+@<Fire up the output routine for |q|@>=
+{ pointer r=new_null_box();type(r)=vlist_node;
+ subtype(r)=0;shift_amount(r)=0;height(r)=hvsize;
+ list_ptr(r)=q;
+ *t=new_glue(ss_glue);
+ flush_node_list(box(255)); /* just in case \dots */
+ box(255)=r;
+ if (output_routine!=null)
+ {@+output_active=true;
+ if (bot_mark!=null)
+ {@+if (top_mark!=null) delete_token_ref(top_mark);
+ top_mark=bot_mark;add_token_ref(top_mark);
+ if (first_mark!=null) delete_token_ref(first_mark);
+ first_mark=bot_mark;add_token_ref(first_mark);
+ }
+ 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;
+ }
+ else
+ {
+ ship_out(box(255)); box(255)=null;
+ }
+}
+@
+
+The |ship_out| routine just calls |execute_output|.
+Because the output routine might have added plenty
+of decorations around the list of output nodes,
+we have to find them again.
+
+@<Hi\TeX\ routines@>=
+void execute_output(pointer p)
+{@+while (p!=null)
+ { @<Execute output nodes from |p|@>@;
+ p=link(p);
+ }
+}
+@
+
+@<Execute output nodes from |p|@>=
+if (!is_char_node(p))
+ switch (type(p))
+ { case whatsit_node:
+ switch (subtype(p))
+ { case open_node: case write_node: case close_node:
+ out_what(p);
+ break;
+ case graf_node: execute_output(graf_list(p));
+ break;
+ case disp_node:
+ if (display_left(p)) execute_output(display_eqno(p));
+ execute_output(display_formula(p));
+ if (!display_left(p)) execute_output(display_eqno(p));
+ break;
+ case hset_node: case vset_node: case hpack_node: case vpack_node:
+ execute_output(list_ptr(p));
+ break;
+ case align_node:
+ execute_output(align_list(p));
+ break;
+ default: break;
+ }
+ break;
+ case hlist_node: case vlist_node:
+ execute_output(list_ptr(p));
+ break;
+ case ins_node:
+ execute_output(ins_ptr(p));
+ break;
+ case adjust_node:
+ execute_output(adjust_ptr(p));
+ break;
+ default: break;
+ }
+@
+
+Invoking the user's output routine is a risky endeavor
+if marks are not initialized properly. In our case
+we will have always |top_mark| equal to |first_mark| and
+|bot_mark|.
+
+@<Record the bottom mark@>=
+if (type(p)==mark_node)
+{ if (bot_mark!=null) delete_token_ref(bot_mark);
+ bot_mark=mark_ptr(p);add_token_ref(bot_mark);
+}
+@
+
+
+\subsection{Replacing {\tt hpack} and {\tt vpack}}
+
+
+@<Hi\TeX\ routines@>=
+
+pointer hpack(pointer p,scaled w, scaled hf, scaled vf, small_number m)
+{
+ pointer r; /*the box node that will be returned*/
+ pointer prev_p; /*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 sto, sho; /*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;
+ prev_p= r+list_offset;link(prev_p)= p;
+ h= 0; 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;
+ while(p!=null)
+ {
+ reswitch:
+ while(is_char_node(p))
+ { 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);
+ }
+ if (p!=null)
+ { switch(type(p)){
+ case hlist_node:case vlist_node:case rule_node:case unset_node:
+ { 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;
+ }
+ break;
+ case ins_node:case mark_node:case adjust_node:if (adjust_tail!=null)
+ { while(link(prev_p)!=p)prev_p= link(prev_p);
+ 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(prev_p),small_node_size);
+ }
+ else
+ { link(adjust_tail)= p;adjust_tail= p;p= link(p);
+ }
+ link(prev_p)= p;p= prev_p;
+ }
+ break;
+ case whatsit_node:
+ if (subtype(p)==graf_node)
+ goto repack;
+ else if (subtype(p)==disp_node )
+ goto repack;
+ else if (subtype(p)==vpack_node )
+ goto repack;
+ else if (subtype(p)==hpack_node )
+ goto repack;
+ else if (subtype(p)==hset_node )
+ goto repack;
+ else if (subtype(p)==vset_node )
+ goto repack;
+ else if (subtype(p)==stream_node )
+ goto repack;
+ else if (subtype(p)==image_node)
+ { glue_ord o;
+ if (image_height(p)> h) h= image_height(p);
+ x= x+image_width(p);
+ o= image_stretch_order(p);total_stretch[o]= total_stretch[o]+image_stretch(p);
+ o= image_shrink_order(p);total_shrink[o]= total_shrink[o]+image_shrink(p);
+ }
+ break;
+ break;
+ case glue_node:
+ { glue_ord o;
+ 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);
+ }
+ }
+ break;
+ case kern_node:case math_node:x= x+width(p);break;
+ case ligature_node:
+ { mem[lig_trick]= mem[lig_char(p)];link(lig_trick)= link(p);
+ p= lig_trick;goto reswitch;
+ }
+ default:do_nothing;
+ }
+ p= link(p);
+ }
+ }
+ if (adjust_tail!=null) link(adjust_tail)= null;
+ height(r)= h;depth(r)= d;
+ if (total_stretch[filll]!=0)sto= filll;
+ else if (total_stretch[fill]!=0)sto= fill;
+ else if (total_stretch[fil]!=0)sto= fil;
+ else sto= normal;
+
+ if (total_shrink[filll]!=0)sho= filll;
+ else if (total_shrink[fill]!=0)sho= fill;
+ else if (total_shrink[fil]!=0)sho= fil;
+ else sho= normal;
+
+ if (hf!=0 || vf!=0 ) /* convert to a hset node */
+ { pointer q;
+ q=new_set_node();
+ subtype(q)=hset_node;
+ height(q)=h;
+ depth(q)=d;
+ width(q)=x; /* the natural width */
+ shift_amount(q)=shift_amount(r);
+ list_ptr(q)=list_ptr(r);
+ list_ptr(r)=null;
+ free_node(r, box_node_size);
+ if (m==exactly)
+ set_extent(q)=new_xdimen(w,hf,vf);
+ else
+ set_extent(q)=new_xdimen(x+w,hf,vf);
+ set_stretch_order(q)=sto;
+ set_shrink_order(q)=sho;
+ set_stretch(q)=total_stretch[sto];
+ set_shrink(q)=total_shrink[sho];
+ return q;
+ }
+
+
+
+
+ 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)
+ {
+ glue_order(r)= sto;glue_sign(r)= stretching;
+ if (total_stretch[sto]!=0)glue_set(r)= unfloat(x/(double)total_stretch[sto]);
+ else
+ { glue_sign(r)= normal;
+ set_glue_ratio_zero(glue_set(r));
+ }
+ if (sto==normal)
+ { if (list_ptr(r)!=null)
+ { 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);
+ goto common_ending;
+ }
+ }
+ }
+ goto end;
+ }
+ else
+ {
+ glue_order(r)= sho;glue_sign(r)= shrinking;
+ if (total_shrink[sho]!=0)
+ glue_set(r)= unfloat((-x)/(double)total_shrink[sho]);
+ else
+ { glue_sign(r)= normal;
+ set_glue_ratio_zero(glue_set(r));
+ }
+ if ((total_shrink[sho]<-x)&&(sho==normal)&&(list_ptr(r)!=null))
+ { last_badness= 1000000;
+ set_glue_ratio_one(glue_set(r));
+ if ((-x-total_shrink[normal]> hfuzz)||(hbadness<100))
+ { if ((overfull_rule> 0)&&(-x-total_shrink[normal]> hfuzz))
+ { while(link(prev_p)!=null)prev_p= link(prev_p);
+ link(prev_p)= new_rule();
+ width(link(prev_p))= overfull_rule;
+ }
+ print_ln();print_nl("Overfull \\hbox (");
+ print_scaled(-x-total_shrink[normal]);print("pt too wide");
+ goto common_ending;
+ }
+ }
+ else if (sho==normal)
+ { if (list_ptr(r)!=null)
+ { last_badness= badness(-x,total_shrink[normal]);
+ if (last_badness> hbadness)
+ { print_ln();print_nl("Tight \\hbox (badness ");print_int(last_badness);
+ goto common_ending;
+ }
+ }
+ }
+ goto end;
+ }
+ common_ending:
+ 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);
+ end:return r;
+
+
+repack:
+ { /* convert the box to a |hpack_node| */
+ pointer q;
+ q=new_pack_node();
+ height(q)=h;
+ depth(q)=d;
+ width(q)=x;
+ subtype(q)=hpack_node;
+ list_ptr(q)=list_ptr(r);
+ list_ptr(r)=null;
+ free_node(r, box_node_size);
+ pack_limit(q)=max_dimen; /* no limit, not used */
+ pack_m(q)=m;
+ pack_extent(q)=new_xdimen(w,hf,vf);
+ return q;
+ }
+}
+
+@
+
+@<Hi\TeX\ routines@>=
+pointer vpackage(pointer p, scaled h, scaled hf, scaled vf, 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=0; /*shift amount*/
+ pointer g; /*points to a glue specification*/
+ glue_ord sho, sto; /*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;
+ 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;
+ while(p!=null)
+ { if (is_char_node(p))
+ confusion("vpack");
+ else
+ switch(type(p))
+ { case hlist_node:case vlist_node:case rule_node:case unset_node:
+ 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;
+ break;
+ case whatsit_node:
+ if (subtype(p)==graf_node)
+ goto repack;
+ else if (subtype(p)==disp_node )
+ goto repack;
+ else if (subtype(p)==vpack_node )
+ goto repack;
+ else if (subtype(p)==hpack_node )
+ goto repack;
+ else if (subtype(p)==hset_node )
+ goto repack;
+ else if (subtype(p)==vset_node )
+ goto repack;
+ else if (subtype(p)==stream_node )
+ goto repack;
+ else if (subtype(p)==image_node)
+ { glue_ord o;
+ if (image_width(p)> w) w= image_width(p);
+ x= x+d+image_height(p);d=0;
+ o= image_stretch_order(p);total_stretch[o]= total_stretch[o]+image_stretch(p);
+ o= image_shrink_order(p);total_shrink[o]= total_shrink[o]+image_shrink(p);
+ }
+ break;
+ case glue_node:
+ { glue_ord o;
+ 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);
+ }
+ }
+ break;
+ case kern_node:
+ x= x+d+width(p);d= 0;
+ break;
+ default:do_nothing;
+ }
+ p= link(p);
+ }
+ width(r)= w;
+
+
+ if (total_stretch[filll]!=0) sto= filll;
+ else if (total_stretch[fill]!=0) sto= fill;
+ else if (total_stretch[fil]!=0) sto= fil;
+ else sto= normal;
+
+ if (total_shrink[filll]!=0) sho= filll;
+ else if (total_shrink[fill]!=0) sho= fill;
+ else if (total_shrink[fil]!=0) sho= fil;
+ else sho= normal;
+
+ if (hf!=0 || vf!=0) /* convert to a vset node */
+ { pointer q;
+ q=new_set_node();
+ subtype(q)=vset_node;
+ width(q)=w;
+ if (d> l)
+ { x= x+d-l;depth(r)= l;
+ }
+ else depth(r)= d;
+ height(q)=x;
+ depth(q)=d;
+ shift_amount(q)=shift_amount(r);
+ list_ptr(q)=list_ptr(r);
+ list_ptr(r)=null;
+ free_node(r, box_node_size);
+ if (m==exactly)
+ set_extent(q)=new_xdimen(h,hf,vf);
+ else
+ set_extent(q)=new_xdimen(x+h,hf,vf);
+ set_stretch_order(q)=sto;
+ set_shrink_order(q)=sho;
+ set_stretch(q)=total_stretch[sto];
+ set_shrink(q)=total_shrink[sho];
+ return q;
+ }
+
+ if (d> l)
+ { x= x+d-l;depth(r)= l;
+ }
+ else depth(r)= d;
+ 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)
+ { glue_order(r)= sto;glue_sign(r)= stretching;
+ if (total_stretch[sto]!=0)glue_set(r)= unfloat(x/(double)total_stretch[sto]);
+ else
+ { glue_sign(r)= normal;
+ set_glue_ratio_zero(glue_set(r));
+ }
+ if (sto==normal)
+ { if (list_ptr(r)!=null)
+ { 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);
+ goto common_ending;
+ }
+ }
+ }
+ goto end;
+ }
+ else /* if (x<0) */
+ {
+ glue_order(r)= sho;glue_sign(r)= shrinking;
+ if (total_shrink[sho]!=0)glue_set(r)= unfloat((-x)/(double)total_shrink[sho]);
+ else
+ { glue_sign(r)= normal;
+ set_glue_ratio_zero(glue_set(r));
+ }
+ if ((total_shrink[sho]<-x)&&(sho==normal)&&(list_ptr(r)!=null))
+ { last_badness= 1000000;
+ set_glue_ratio_one(glue_set(r));
+ if ((-x-total_shrink[normal]> vfuzz)||(vbadness<100))
+ { print_ln();print_nl("Overfull \\vbox (");
+ print_scaled(-x-total_shrink[normal]);print("pt too high");
+ goto common_ending;
+ }
+ }
+ else if (sho==normal)
+ { if (list_ptr(r)!=null)
+ { last_badness= badness(-x,total_shrink[normal]);
+ if (last_badness> vbadness)
+ { print_ln();print_nl("Tight \\vbox (badness ");print_int(last_badness);
+ goto common_ending;
+ }
+ }
+ }
+ goto end;
+ }
+
+
+ common_ending:
+ if (pack_begin_line!=0)
+ { 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);
+ end:
+ return r;
+
+
+repack:
+ { /* convert the box to a |vpack_node| */
+ pointer q;
+ q=new_pack_node();
+ subtype(q)=vpack_node;
+ height(q)=x;
+ depth(q)=d;
+ width(q)=w;
+ list_ptr(q)=list_ptr(r);
+ list_ptr(r)=null;
+ free_node(r, box_node_size);
+ pack_limit(q)=l;
+ pack_m(q)=m;
+ pack_extent(q)=new_xdimen(h,hf,vf);
+ return q;
+ }
+}
+
+@
+
+\subsection{Streams}
+\HINT\ stream numbers start at 0 for the main text and continue
+upwards. \TeX, on the other hand, numbers insertions starting with
+{\tt box255} for the main text and continues downwards. Some mapping is
+needed, and we use the array |insert2stream| to map \TeX's insert
+numbers to \HINT\ stream numbers.
+The predefined stream for the main content has stream number 0.
+
+@<Hi\TeX\ variables@>=
+int insert2stream[0x100]={0};
+@
+
+The following function returns the stream number for a given insert number $i$
+with $255>|i|\ge 0$. A new stream number is allocated if necessary.
+Note that no overflow test is necessary since \TeX\ allocates less
+than 233 inserts.
+The initial value of |max_ref[stream_kind]| is 0 and therefore
+stream number 0, reserved for the main content
+is never allocated. Stream definitions might also be loaded
+as part of a format file. Then the maximum stream number ist stored in |max_stream|.
+So if we do not find a stream number
+in the |insert2stream| array, we scan the stream definitions
+once and cache the associations found there.
+
+@<Hi\TeX\ routines@>=
+int hget_stream_no(int i)
+{ static bool init=false;
+ int s;
+ if (i==0) return 0;
+ s=insert2stream[i];
+ if (s!=0) return s;
+ if (!init)
+ { pointer t,s;
+ for (t=link(setpage_head); t!=null; t=link(t))
+ for(s=setpage_streams(t); s!=null; s=link(s))
+ insert2stream[setstream_insertion(s)]=setstream_number(s);
+ max_ref[stream_kind]=max_stream;
+ init=true;
+ }
+ s=insert2stream[i];
+ if (s==0)
+ s=insert2stream[i]=max_ref[stream_kind]=++max_stream;
+ return s;
+}
+@
+
+\subsection{Stream Definitions}
+
+A stream definition is stored as a whatsit node with subtype |setstream_node| as
+defined in section~\secref{whatsit}.
+Given a pointer |p| to such a node, here are the macros used to access the data stored there:
+\itemize
+\item |setstream_number(p)| the \HINT\ stream number $n$.
+\item |setstream_insertion(p)| the corresponding \TeX\ insertion number $i$.
+\item |setstream_max(p)| the maximum height $x$: This extended dimension is the maximum size
+ per page for this insertion.
+\item |setstream_mag(p)| the magnification factor $f$:
+ Inserting a box of height $h$ will contribute $h*f/1000$
+ to the main page.
+\item |setstream_prefered(p)| the prefered stream $p$:
+ If $p\ge0$ we move the insert to stream $p$ if possible.
+\item |setstream_next(p)| the next stream $n$:
+ If $n\ge0$ we move the insert to stream $n$ if it can not be
+ accomodated otherwise.
+\item |setstream_ratio(p)| the split ratio $r$:
+ If $r>0$ split the final contribution of this streams between
+ stream $p$ and $n$ in the ratio $r/1000$ for $p$ and $1-r/1000$ for $n$
+ before contributing streams $p$ and $r$ to the page.
+\item |setstream_before(p)| the ``before'' list $b$:
+ For a nonempty stream the material that is added before the stream content.
+\item |setstream_after(p)| the ``after'' list $a$:
+ For a nonempty stream, material that is added after the stream conten.
+\item |setstream_topskip(p)| the top skip glue $t$: This glue is inserted between
+ the $b$ list and the stream content and ajusted for the height for the first box
+ of the stream content.
+\item |setstream_width(p)| the width $w$:
+ This extended dimension is the width used for example
+ to break paragraphs in the stream content into lines.
+\item |setstream_height(p)| a glue specification $h$ reflecting the total height,
+ strechability and shrinkability of the material in lists $a$ and $b$.
+\enditemize
+
+Currently Hi\TeX\ handles only normal streams. First or last streams will come later.
+
+The stream definition nodes are created and initialized with the following function:
+@<Hi\TeX\ routines@>=
+pointer new_setstream_node(uint8_t n)
+{ pointer p=get_node(setstream_node_size);
+ type(p)=whatsit_node;subtype(p)=setstream_node;
+ setstream_insertion(p)=n;
+ setstream_number(p)=hget_stream_no(n);
+ setstream_mag(p)=1000;
+ setstream_prefered(p)=255;
+ setstream_next(p)=255;
+ setstream_ratio(p)=0;
+ setstream_max(p)=new_xdimen(0,0,ONE);
+ setstream_width(p)=new_xdimen(0,ONE,0);
+ setstream_topskip(p)=zero_glue; add_glue_ref(zero_glue);
+ setstream_height(p)=zero_glue; add_glue_ref(zero_glue);
+ setstream_before(p)=null;
+ setstream_after(p)=null;
+
+ return p;
+}
+@
+
+
+The prefered stream, the next stream, and the split ratio are scanned as part of the
+{\tt \BS setstream} primitive as described in section~\secref{primitive}.
+When \TeX\ finds the right brace that terminates the stream definition,
+it calls |handle_right_brace|. Then it is time to obtain the remaining parts of the
+stream definition.
+For insertion class $i$,
+we can extract the maximum height $x$ of the insertions from
+the corresponding {\tt dimen$i$} register
+the magnification factor $f$ from the {\tt count$i$} register,
+and the total height $h$ from the {\tt skip$i$} register.
+The width $w$ is taken from {\tt \BS hsize} and the
+topskip $t$ from {\tt \BS topskip}.
+
+@<Hi\TeX\ routines@>=
+void hfinish_stream_group(void)
+{ pointer s;
+ end_graf();
+ s=hget_current_stream();
+ if (s!=null)
+ { pointer t;
+ uint8_t i;
+ i= setstream_insertion(s);
+ setstream_mag(s)=count(i);
+ setstream_width(s)=new_xdimen(dimen_par(hsize_code),
+ dimen_par_hfactor(hsize_code),dimen_par_vfactor(hsize_code));
+ t=zero_glue;add_glue_ref(t);delete_glue_ref(setstream_topskip(s)); setstream_topskip(s)=t;
+ t=skip(i);add_glue_ref(t);delete_glue_ref(setstream_height(s)); setstream_height(s)=t;
+ setstream_max(s)=new_xdimen(dimen(i),
+ dimen_hfactor(i),dimen_vfactor(i));
+ }
+ unsave();
+ flush_node_list(link(head));
+ pop_nest();
+}
+@
+
+The before list $b$ and the after list $a$ are defined using the
+{\tt \BS before} and {\tt \BS after} primitives. When the corresponding list
+has ended with a right brace, \TeX\ calls |handle_right_brace| and we can store
+the lists.
+
+@<Hi\TeX\ routines@>=
+void hfinish_stream_before_group(void)
+{ pointer s;
+ end_graf();
+ s=hget_current_stream();
+ if (s!=null)
+ setstream_before(s)=link(head);
+ unsave();
+ pop_nest();
+}
+
+void hfinish_stream_after_group(void)
+{ pointer s;
+ end_graf();
+ s=hget_current_stream();
+ if (s!=null)
+ setstream_after(s)=link(head);
+ unsave();
+ pop_nest();
+}
+@
+
+\subsection{Page Template Definitions}
+
+The data describing a page template is stored in a whatsit node with subtype
+|setpage_node| as defined insection~\secref{whatsit}.
+Given a pointer |p| to such a node, here are the macros used to access the data stored there:
+\itemize
+\item |setpage_name(p)|: The name of the page template
+ can be used in the user interface of a \HINT\ viewer.
+\item |setpage_number(p)|: The number of the page template that is used in the \HINT\
+ file to reference this page template.
+\item |setpage_id(p)|: The number of the page template that is used in \TeX\
+ to reference this page template.
+\item |setpage_priority(p)|: The priority helps in selecting a page template.
+\item |setpage_topskip(p)|: The topskip glue is added at the top of a page and
+ adjusted by the height of the first bos on the page.
+\item |setpage_height(p)|: The height of the full page including the margins.
+\item |setpage_width(p)|: The width of the full page including the margins.
+\item |setpage_depth(p)|: The maximum depth of the page content. If the last box is deeper
+than this maximum, the difference is subtracted from the height of the page body.
+\item |setpage_list(p)|: The list that defines the page template. After the page builder
+has completed a page this list is scanned and page body and nonempty streams
+are added at the corresponding insertion points.
+\item |setpage_streams(p)|: The list of stream definitions that
+belong to this page template.
+\enditemize
+
+To allow \TeX\ to use arbitrary numbers between 1 and 255 for the page templates
+while in \HINT\ the numbers of page templates are best consecutive
+from 1 to |max_ref[page_kind]==max_page|, we let \TeX\ assign an id and generate
+the template number. Because templates might be in format files, the variable
+|max_page| will hold the true number.
+
+The function |new_setpage_node| is called with the page template id
+$0<|i|<256$ and a string number for the name |n|.
+It allocates and initializes a node if necessary and moves it to the front of the
+list of templates.
+
+@<Hi\TeX\ routines@>=
+pointer new_setpage_node(uint8_t i, str_number n)
+{ pointer p, prev_p;
+ prev_p=setpage_head;
+ 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|@>@;
+ else
+ link(prev_p)=link(p);
+ link(p)=link(setpage_head);
+ link(setpage_head)=p;
+ return 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;
+ setpage_name(p)=n;
+ setpage_priority(p)=1;
+ setpage_topskip(p)=zero_glue; add_glue_ref(zero_glue);
+ setpage_height(p)=new_xdimen(0,0,ONE);
+ setpage_width(p)=new_xdimen(0,ONE,0);
+ setpage_depth(p)=max_depth;
+ setpage_list(p)=null;
+ setpage_streams(p)=null;
+}
+@
+
+The default values are replaced by parameters given to the {\tt\BS setpage}
+primitive (see section~\secref{pageext}) and by the current values of certain
+\TeX\ registers when finishing the page template.
+page template itself,
+
+@<Hi\TeX\ routines@>=
+void hfinish_page_group(void)
+{ uint8_t k;
+ pointer p,q,r;
+ end_graf();
+ p=hget_current_page();
+ if (p!=null)
+ { delete_glue_ref(setpage_topskip(p));
+ setpage_topskip(p)=top_skip;add_glue_ref(top_skip);
+ setpage_depth(p)=max_depth;
+ flush_node_list(setpage_list(p));
+ setpage_list(p)=link(head);
+ }
+ unsave();
+ pop_nest();
+}
+@
+@<Hi\TeX\ auxiliar routines@>=
+
+static pointer hget_current_page(void)
+{ pointer p=link(setpage_head);
+ if (p==null)
+ print_err("end of output group without setpage node");
+ return p;
+}
+
+static pointer hget_current_stream(void)
+{ pointer p,s;
+ p=hget_current_page();
+ if (p==null) return null;
+ s=setpage_streams(p);
+ if (s==null)
+ print_err("end of setstream group without setstream node");
+ return s;
+}
+@
+
+
+
+\section{\HINT\ Output}
+
+\subsection{Initialization}
+\noindent
+
+@<Hi\TeX\ routines@>=
+static void hout_init(void)
+{
+ new_directory(dir_entries);
+ new_output_buffers();
+ max_section_no=2;
+ hdef_init();
+ hput_content_start();
+ @<insert an initial language node@>@;
+}
+
+extern int option_global;
+void hint_open(void)
+{ if (job_name==0) open_log_file();
+ pack_job_name(".hnt");
+ while (!(hout=open_out((char *)name_of_file+1,"wb")))
+ prompt_file_name("file name for output",".hnt");
+ hlog=stderr;
+ option_global=true;
+ hout_init();
+}
+@
+
+\subsection{Termination}
+
+@<Hi\TeX\ routines@>=
+#define HITEX_VERSION "1.1"
+static void hput_definitions();
+static void hout_terminate(void)
+{ hput_content_end();
+ hput_definitions();
+ hput_directory();
+ hput_hint("created by HiTeX Version " HITEX_VERSION);
+}
+
+void hint_close(void)
+{ hout_terminate();
+ if (hout!=NULL)
+ fclose(hout);
+ hout=NULL;
+}
+@
+
+
+\subsection{\HINT\ Directory}
+There is not much to do here: some code to find a new or existing directory entry,
+a variable to hold the number of directory entries allocated,
+a function to allocate a new file section, and an auxiliar function to
+convert \TeX's file names to ordinary \CEE/ strings.
+
+@<Find an existing directory entry@>=
+for (i=3; i<= max_section_no;i++)
+ if (dir[i].file_name!=NULL && strcmp(dir[i].file_name,file_name)==0)
+ return i;
+@
+
+@<Allocate a new directory entry@>=
+ i = max_section_no;
+ i++;
+ if (i>0xFFFF) QUIT("Too many file sections");
+ if (i>=dir_entries)
+ RESIZE(dir,dir_entries,entry_t);
+ max_section_no=i;
+ if (max_section_no>0xFFFF) QUIT("Too many sections");
+ dir[i].section_no=i;
+@
+
+@<Hi\TeX\ macros@>=
+#define @[RESIZE(P,S,T)@] \
+{ int _n = (S)*1.4142136 +0.5; \
+ if (_n<32) _n=32; \
+ { REALLOCATE(P,_n,T); \
+ memset((P)+(S),0,(_n-(S))*sizeof(T)); \
+ (S)=_n; \
+ } \
+}
+@
+
+@<Hi\TeX\ variables@>=
+static int dir_entries=4;
+@
+
+@<Hi\TeX\ auxiliar routines@>=
+static uint16_t hnew_file_section(char *file_name)
+{ uint16_t i;
+ @<Find an existing directory entry@>@;
+ @<Allocate a new directory entry@>@;
+ dir[i].file_name=strdup(file_name);
+ return i;
+}
+@
+
+The following function uses \TeX's function |pack_file_name|
+to create a new filename from a name |n|, a direcory or ``area'' |a|,
+and an extension |e|. \TeX\ will truncate the new filename
+to |file_name_size| characters without warning. The new function
+will take a |name_length| equal to | file_name_size| as an
+indication that truncation has taken place and terminates the
+program. The return value converts a \Pascal\ array, starting with index 1,
+into a \CEE/ array starting with index 0.
+
+@<Hi\TeX\ auxiliar routines@>=
+static char *hfile_name(str_number n, char *a, char *e)
+{ pack_file_name(n,a,e);
+ if (name_length>=file_name_size)
+ QUIT("File name too long %d >= %d",name_length,file_name_size);
+ return (char *)name_of_file+1;
+}
+
+@
+
+\section{\HINT\ Definitions}\label{definitions}
+Definitions are used for two reasons: they provide default values for the parameters
+that drive \TeX's algorithms running in the \HINT\ viewer, and they provide a compact notation
+for \HINT\ content nodes.
+
+To find the optimal coding for a \HINT\ file, a global knowledge of the \HINT\ file is necessary.
+This would require a two pass process:
+in the first pass Hi\TeX\ could gather statistics on the use of parameter values and content
+nodes as a basis for making definitions and in the second pass it could encode the content using
+these definitions. I consider it, however, more reasonable to write such a two pass optimizer
+as a separate program which can be used on any \HINT\ file.
+Hence Hi\TeX\ uses a much simpler one pass approach:
+\itemize
+
+\item Hi\TeX\ generates definitions for \TeX-parameters using the
+ values they have when the first non discardable item appears in
+ |build_page|. This is usually the case after initial style files have
+ been processed and we can expect that they set useful default values.
+
+The procedure that generates these definitions is called |hfix_defaults|:
+
+@<Hi\TeX\ auxiliar routines@>=
+
+void hfix_defaults(void)
+{ @+int i;
+ DBG(DBGDEF,"Freezing HINT file defaults\n");
+ @<Compute the page size@>@;
+ @<Fix definitions for integer parameters@>@;
+ @<Fix definitions for dimension parameters@>@;
+ @<Fix definitions for glue parameters@>@;
+ @<Fix definitions of page templates@>@;
+}
+
+@
+
+\item Hi\TeX\ generates definitions to be used in content nodes on the fly:
+Whenever a routine outputs an item for which a definition might be available,
+it calls a {\it hget\_\dots\_no} function. This function returns, if possible,
+the reference number of a suitable definition.
+If no definition is available, the function will try to allocate a new one,
+only if all reference numbers from 0 to |0xFF| are already in use, a $-1$ is
+returned to indicate failure.
+
+There are two possible problems with this approach: We might miss a
+very common item because it occurs for the first time late in the
+input when all reference numbers are already in use. For example an
+extensive index might repeat a certain pattern for each entry. And second, we
+might make a definition for an item that occurs only once. Taken
+together the definition plus the reference to it requires more space
+than the same item without a definition.
+
+We can hope that the first effect does not occur too often, especially if the
+\TeX\ file is short, and we know that the second effect is limited by the
+total number of definitions we can make plus four byte of overhead per instance.
+\enditemize
+
+Here we initialize the necessary data structures for definitions.
+@<Hi\TeX\ auxiliar routines@>=
+static void hdef_init(void)
+{@+ int i;
+ @<Switch |hsize| and |vsize| to extended dimensions@>@;
+ @<Initialize definitions for extended dimensions@>@;
+ @<Initialize definitions for baseline skips@>@;
+ @<Initialize definitions for fonts@>@;
+ @<Initialize definitions for labels@>@;
+#if 0
+ overfull_rule=0; /* no overfull rules please */
+#endif
+}
+@
+
+After all definitions are ready, we write them using the function
+|hput_definitions|. When we output the definitions,
+we have to make sure to define references before we use them.
+This is achived by using a specific ordering of the
+definitions in the function |hput_definitions| and by preventing
+the allocation of new definitions as soon as the output of the definition
+section has started. The latter has the aditional benefit that the
+maximum values do no longer change.
+
+@<Hi\TeX\ routines@>=
+static void hput_definitions()
+/* write the definitions into the definitions buffer */
+{ int i;
+ uint32_t d, m, s;
+ hput_definitions_start();
+ hput_max_definitions();
+ @<Output language definitions@>@;
+ @<Output font definitions@>@;
+ @<Output integer definitions@>@;
+ @<Output dimension definitions@>@;
+ @<Output extended dimension definitions@>@;
+ @<Output glue definitions@>@;
+ @<Output baseline skip definitions@>@;
+ @<Output parameter list definitions@>@;
+ @<Output discretionary break definitions@>@;
+ @<Output page template definitions@>@;
+ hput_definitions_end();
+ hput_range_defs(); /* expects the definitions section to be ended */
+ hput_label_defs();
+}
+@
+
+
+In the following, we present for each node type the code to generate
+the definitions, using a common schema: We define a data structure
+called {\it\dots\_defined}, to hold the definitions; we define, if
+applicable, the \TeX-parameters; we add an {\it hget\_\dots\_no}
+function to allocate new definitions; and we finish with the code to
+output the collected definitions.
+
+Lets start with the most simple case: integers.
+
+\subsection{Integers}
+\subsubsection{Data}
+The data structure to hold the integer definitions is a simple array with |0x100| entries.
+A more complex data structure, for example a hash table, could speed up searching for
+existing definitions but lets keep things simple for now.
+
+@<Hi\TeX\ variables@>=
+static int32_t int_defined[0x100]={0};
+@
+\subsubsection{Mapping}
+Before we can generate definitions for \TeX-parameters, we have to map \TeX's
+parameter numbers to \HINT\ definition numbers. While it seems more convenient here
+to have the reverse mapping, we need the mapping only once to record parameter definitions,
+but we will need it repeatedly in the function |hdef_param_node| and the overhead here does
+not warant having the mapping in both directions.
+
+@<Hi\TeX\ variables@>=
+static const int hmap_int[] ={@/
+pretolerance_no, /* |pretolerance_code| 0 */
+tolerance_no, /* |tolerance_code| 1 */
+line_penalty_no, /* |line_penalty_code| 2 */
+hyphen_penalty_no, /* |hyphen_penalty_code| 3 */
+ex_hyphen_penalty_no, /* |ex_hyphen_penalty_code| 4 */
+club_penalty_no, /* |club_penalty_code| 5 */
+widow_penalty_no, /* |widow_penalty_code| 6 */
+display_widow_penalty_no, /* |display_widow_penalty_code| 7 */
+broken_penalty_no, /* |broken_penalty_code| 8 */
+-1, /* |bin_op_penalty_code| 9 */
+-1, /* |rel_penalty_code| 10 */
+pre_display_penalty_no, /* |pre_display_penalty_code| 11 */
+post_display_penalty_no, /* |post_display_penalty_code| 12 */
+inter_line_penalty_no, /* |inter_line_penalty_code| 13 */
+double_hyphen_demerits_no, /* |double_hyphen_demerits_code| 14 */
+final_hyphen_demerits_no, /* |final_hyphen_demerits_code| 15 */
+adj_demerits_no, /* |adj_demerits_code| 16 */
+-1, /* |mag_code| 17 */
+-1, /* |delimiter_factor_code| 18 */
+looseness_no, /* |looseness_code| 19 */
+time_no, /* |time_code| 20 */
+day_no, /* |day_code| 21 */
+month_no, /* |month_code| 22 */
+year_no, /* |year_code| 23 */
+-1, /* |show_box_breadth_code| 24 */
+-1, /* |show_box_depth_code| 25 */
+-1, /* |hbadness_code| 26 */
+-1, /* |vbadness_code| 27 */
+-1, /* |pausing_code| 28 */
+-1, /* |tracing_online_code| 29 */
+-1, /* |tracing_macros_code| 30 */
+-1, /* |tracing_stats_code| 31 */
+-1, /* |tracing_paragraphs_code| 32 */
+-1, /* |tracing_pages_code| 33 */
+-1, /* |tracing_output_code| 34 */
+-1, /* |tracing_lost_chars_code| 35 */
+-1, /* |tracing_commands_code| 36 */
+-1, /* |tracing_restores_code| 37 */
+-1, /* |uc_hyph_code| 38 */
+-1, /* |output_penalty_code| 39 */
+-1, /* |max_dead_cycles_code| 40 */
+hang_after_no, /* |hang_after_code| 41*/
+floating_penalty_no /* |floating_penalty_code| 42*/
+};
+
+
+@
+\subsubsection{Parameters}
+Now we can generate the definitions for integer parameters:
+
+@<Fix definitions for integer parameters@>=
+ int_defined[zero_int_no]=0;
+ for (i=pretolerance_code; i<=hang_after_code;i++)
+ if ( hmap_int[i]>=0) int_defined[hmap_int[i]]=int_par(i);
+ max_ref[int_kind]=MAX_INT_DEFAULT;
+@
+
+
+\subsubsection{Allocation}
+The function |hget_int_no| tries to allocate a predefined integer number;
+if not successful, it returns $-1$.
+
+@<Hi\TeX\ auxiliar routines@>=
+static int hget_int_no(int32_t n)
+{ int i;
+ int m =max_ref[int_kind];
+ for (i=0; i<=m; i++)
+ if (n== int_defined[i]) return i;
+ if (m<0xFF && section_no==2)
+ { m=++max_ref[int_kind]; int_defined[m]=n; return m; }
+ else
+ return -1;
+}
+@
+
+\subsubsection{Output}
+Before we give the code to output an integer definition, we declare a macro that
+is usefull for all the definitions. |HPUTDEF| takes a function |F| and a reference number |R|.
+It is assumed that |F| writes a definition into the output and returns a tag. The macro
+will then add the reference number and both tags to the output.
+@<Hi\TeX\ macros@>=
+#define HPUTDEF(F,R) \
+ { uint32_t _p; \
+ uint8_t _t; \
+ HPUTNODE; /* allocate */ \
+ _p=hpos-hstart; \
+ HPUT8(0); /* tag */ \
+ HPUT8(R); /* reference */ \
+ _t=F; \
+ hstart[_p]=_t; DBGTAG(_t,hstart+_p); \
+ DBGTAG(_t,hpos); HPUT8(_t); \
+ }
+@
+
+Definitions are written to the output only if they differ from Hi\TeX's built in defaults.
+@<Output integer definitions@>=
+ DBG(DBGDEF,"Maximum int reference: %d\n",max_ref[int_kind]);
+ for (i=max_fixed[int_kind]+1;i<=max_default[int_kind]; i++)
+ { if (int_defined[i]!=int_defaults[i])@/
+ HPUTDEF(hput_int(int_defined[i]),i);
+ }
+ for (;i<=max_ref[int_kind]; i++)@/
+ HPUTDEF(hput_int(int_defined[i]),i);
+@
+
+
+\subsection{Dimensions}
+We proceed as we did for integers, starting with the array that holds the defined dimensions.
+\subsubsection{Data}
+@<Hi\TeX\ variables@>=
+static scaled dimen_defined[0x100]={0};
+@
+\subsubsection{Mapping}
+@<Hi\TeX\ variables@>=
+static const int hmap_dimen[] ={@/
+ -1, /* |par_indent_code| 0 */
+ -1, /* |math_surround_code| 1 */
+ line_skip_limit_no, /* |line_skip_limit_code| 2 */
+ hsize_dimen_no, /* |hsize_code| 3 */
+ vsize_dimen_no, /* |vsize_code| 4 */
+ max_depth_no, /* |max_depth_code| 5 */
+ split_max_depth_no, /* |split_max_depth_code| 6 */
+ -1, /* |box_max_depth_code| 7 */
+ -1, /* |hfuzz_code| 8 */
+ -1, /* |vfuzz_code| 9 */
+ -1, /* |delimiter_shortfall_code| 10 */
+ -1, /* |null_delimiter_space_code| 11 */
+ -1, /* |script_space_code| 12 */
+ -1, /* |pre_display_size_code| 13 */
+ -1, /* |display_width_code| 14 */
+ -1, /* |display_indent_code| 15 */
+ -1, /* |overfull_rule_code| 16 */
+ hang_indent_no, /* |hang_indent_code| 17 */
+ -1, /* |h_offset_code| 18 */
+ -1, /* |v_offset_code| 19 */
+ emergency_stretch_no /* |emergency_stretch_code| 20 */
+};
+@
+
+\subsubsection{Parameters}
+@<Fix definitions for dimension parameters@>=
+ dimen_defined[zero_dimen_no]=0;
+ for (i=par_indent_code; i<=emergency_stretch_code;i++)
+ if ( hmap_dimen[i]>=0) dimen_defined[hmap_dimen[i]]=dimen_par(i);
+ dimen_defined[hsize_dimen_no]=page_h;
+ dimen_defined[vsize_dimen_no]=page_v;
+ dimen_defined[quad_no]=quad(cur_font);
+ dimen_defined[math_quad_no]=math_quad(text_size);
+ max_ref[dimen_kind]=MAX_DIMEN_DEFAULT;
+@
+
+\subsubsection{Allocation}
+@<Hi\TeX\ auxiliar routines@>=
+static int hget_dimen_no(scaled s)
+/* tries to allocate a predefined dimension number in the range 0 to 0xFF
+ if not successful return -1 */
+{ int i;
+ int m =max_ref[dimen_kind];
+ for (i=0; i<=m; i++)
+ if (s== dimen_defined[i]) return i;
+ if (m<0xFF && section_no==2)
+ { m=++max_ref[dimen_kind]; dimen_defined[m]=s; return m; }
+ else
+ return -1;
+}
+@
+
+\subsubsection{Output}
+@<Output dimension definitions@>=
+ DBG(DBGDEF,"Maximum dimen reference: %d\n",max_ref[dimen_kind]);
+ for (i=max_fixed[dimen_kind]+1;i<=max_default[dimen_kind]; i++)
+ { if (dimen_defined[i]!=dimen_defaults[i])
+ HPUTDEF(hput_dimen(dimen_defined[i]),i);
+ }
+ for (;i<=max_ref[dimen_kind]; i++)
+ HPUTDEF(hput_dimen(dimen_defined[i]),i);
+@
+
+
+\subsection{Extended Dimensions}
+
+\subsubsection{Data}
+@<Hi\TeX\ variables@>=
+static struct {
+scaled w,h,v; } xdimen_defined[0x100];
+@
+
+\subsubsection{Initialization}
+@<Initialize definitions for extended dimensions@>=
+ for (i=0; i<=max_fixed[xdimen_kind]; i++)
+ { xdimen_defined[i].w = xdimen_defaults[i].w;
+ xdimen_defined[i].h = ONE*xdimen_defaults[i].h;
+ xdimen_defined[i].v = ONE*xdimen_defaults[i].v;
+ }
+@
+
+\subsubsection{Allocation}
+To obtain a reference number for an extended dimension, we search the
+array and if no match was found, we allocate a new entry,
+reallocating the array if needed.
+We use the variable |rover| to mark the place where the
+last entry was inserted, because
+quite often we repeatedly search for the same values.
+
+@<Hi\TeX\ auxiliar routines@>=
+int hget_xdimen_no(pointer p)
+{ int i;
+ for (i=0;i<=max_ref[xdimen_kind];i++)
+ { if (xdimen_defined[i].w== xdimen_width(p) &&
+ xdimen_defined[i].h== xdimen_hfactor(p) &&
+ xdimen_defined[i].v== xdimen_vfactor(p))
+ return i;
+ }
+ if (section_no!=2) return -1;
+ if (i>=0x100) return -1;
+ max_ref[xdimen_kind]=i;
+ xdimen_defined[i].w= xdimen_width(p);
+ xdimen_defined[i].h= xdimen_hfactor(p);
+ xdimen_defined[i].v= xdimen_vfactor(p);
+ return i;
+}
+@
+
+@<Hi\TeX\ routines@>=
+pointer new_xdimen(scaled w, scaled h, scaled v)
+{ pointer p=get_node(xdimen_node_size);
+ type(p)=whatsit_node;subtype(p)=xdimen_node;
+ xdimen_width(p)=w;
+ xdimen_hfactor(p)=h;
+ xdimen_vfactor(p)=v;
+ return p;
+}
+@
+
+\subsubsection{Output}
+@<Output extended dimension definitions@>=
+ DBG(DBGDEF,"Maximum xdimen reference: %d\n",max_ref[xdimen_kind]);
+ for (i=max_fixed[xdimen_kind]+1;i<=max_default[xdimen_kind]; i++)
+ { xdimen_t x;
+ x.w=xdimen_defined[i].w;
+ x.h=xdimen_defined[i].h/(double)ONE;
+ x.v=xdimen_defined[i].v/(double)ONE;
+
+ if (x.w!=xdimen_defaults[i].w ||
+ x.h!=xdimen_defaults[i].h ||
+ x.v!=xdimen_defaults[i].v)
+ HPUTDEF(hput_xdimen(&x),i);
+ }
+ for (;i<=max_ref[xdimen_kind]; i++)
+ { xdimen_t x;
+ x.w=xdimen_defined[i].w;
+ x.h=xdimen_defined[i].h/(double)ONE;
+ x.v=xdimen_defined[i].v/(double)ONE;
+ HPUTDEF(hput_xdimen(&x),i);
+ }
+@
+
+
+@
+\subsection{Glues}
+In general there are two choices on how to store a definition: We can use the data structures used by \TeX\
+or we can use the data structures defined by \HINT. If we are lucky, both of them are the same
+as we have seen for integers and dimensions. For extended dimensions, we had to use the \HINT\ data type
+|xdimen_t| because \TeX\ has no corresponding data type and uses only reference numbers.
+In the case of glue, we definitely have a choice. We decide to use \TeX's pointers to glue specifications
+in the hope to save some work when comparing glues for equality, because \TeX\ already reuses
+glue specifications and often a simple comparison of pointers might suffice.
+\subsubsection{Data}
+@<Hi\TeX\ variables@>=
+static pointer glue_defined[0x100];
+@
+\subsubsection{Mapping}
+@<Hi\TeX\ variables@>=
+static int hmap_glue[] ={
+
+line_skip_no, /* |line_skip_code| 0 */
+baseline_skip_no, /* |baseline_skip_code| 1 */
+-1, /* |par_skip_code| 2 */
+above_display_skip_no, /* |above_display_skip_code| 3 */
+below_display_skip_no, /* |below_display_skip_code| 4 */
+above_display_short_skip_no, /* |above_display_short_skip_code| 5 */
+below_display_short_skip_no, /* |below_display_short_skip_code| 6 */
+left_skip_no, /* |left_skip_code| 7 */
+right_skip_no, /* |right_skip_code| 8 */
+top_skip_no, /* |top_skip_code| 9 */
+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 */
+par_fill_skip_no /* |par_fill_skip_code| 14 */
+};
+@
+\subsubsection{Parameters}
+@<Fix definitions for glue parameters@>=
+ glue_defined[zero_skip_no]=zero_glue; incr(glue_ref_count(zero_glue));
+ for (i=line_skip_code; i<=par_fill_skip_code;i++)
+ if ( hmap_glue[i]>=0)
+ { glue_defined[hmap_glue[i]]=glue_par(i); incr(glue_ref_count(glue_par(i)));}
+ max_ref[glue_kind]=MAX_GLUE_DEFAULT;
+@
+
+
+\subsubsection{Allocation}
+Next we define some auxiliar routines to compare glues for equality and to convert glues beween the different representations.
+
+@<Hi\TeX\ auxiliar routines@>=
+int glue_spec_equal(pointer p, pointer q)
+{ return (width(q)==width(p) && stretch(q)==stretch(p) && shrink(q)==shrink(p) &&
+ (stretch_order(q)==stretch_order(p) || stretch(q)==0) &&
+ (shrink_order(q)==shrink_order(p)|| shrink(q)==0));
+}
+
+int glue_equal(pointer p, pointer q)
+{ return p==q || glue_spec_equal(p,q);
+}
+
+int glue_t_equal(glue_t *p, glue_t *q)
+{ return(p->w.w==q->w.w && p->w.h==q->w.h && p->w.v==q->w.v &&
+ p->p.f== q->p.f && p->m.f==q->m.f &&
+ (p->p.o==q->p.o || p->p.f==0.0) &&
+ (p->m.o==q->m.o || q->m.f==0.0));
+}
+
+@
+
+To find a matching glue we make two passes over the defined glues:
+on the first pass we just compare pointers and on the second pass
+we also compare values. An alternative approach to speed up searching
+is used in section~\secref{paramlist} below.
+
+@<Hi\TeX\ auxiliar routines@>=
+static int hget_glue_no(pointer p)
+{ static int rover=0;
+ int i;
+ if (p==zero_glue) return zero_skip_no;
+ for(i=0; i<= max_ref[glue_kind] ; i++)
+ { if (p==glue_defined[rover]) return rover;
+ else if (rover==0)
+ rover=max_ref[glue_kind];
+ else
+ rover--;
+ }
+ for(i=0; i<= max_ref[glue_kind] ; i++)
+ { pointer q=glue_defined[rover];
+ if (glue_spec_equal(p,q))
+ return rover;
+ else if (rover==0)
+ rover=max_ref[glue_kind];
+ else
+ rover--;
+ }
+ if (max_ref[glue_kind]<0xFF && section_no==2)
+ { rover=++max_ref[glue_kind];
+ glue_defined[rover]=p;
+ incr(glue_ref_count(p));
+ DBG(DBGDEF,"Defining new glue %d\n",rover);
+ return rover;
+ }
+ else
+ return -1;
+}
+@
+\subsubsection{Output}
+@<Output glue definitions@>=
+ DBG(DBGDEF,"Maximum glue reference: %d\n",max_ref[glue_kind]);
+ for (i=max_fixed[glue_kind]+1;i<=max_default[glue_kind]; i++)
+ { glue_t g;
+ to_glue_t(glue_defined[i],&g);
+ if (!glue_t_equal(&g,&glue_defaults[i]))
+ HPUTDEF(hput_glue(&g),i);
+ }
+ for (;i<=max_ref[glue_kind]; i++)
+ HPUTDEF(hout_glue_spec(glue_defined[i]),i);
+@
+
+The above code uses the following conversion routine.
+While \HINT\ supports glue that depends on {\tt hsize} and {\tt vsize},
+this is currently not supported by Hi\TeX.
+Future versions of Hi\TeX\ should extend glue spec nodes (and kern nodes)
+by fields for |hfactor| and |vfactor| which are zero by default.
+This would leave most parts of \TeX\ unchanged.
+As a work-around one can combine a box with an extended dimension with
+a regular glue or kern.
+% Care should be taken for the statically allocated glue specs
+% deallocation of glue specs is relatively simple
+% extended glue and kern values can be restricted to non math mode
+@<Hi\TeX\ auxiliar routines@>=
+void to_glue_t(pointer p, glue_t *g)
+{ g->w.w=width(p);
+ g->w.h=g->w.v=0.0;
+ g->p.f=stretch(p)/(double)ONE; g->p.o= stretch_order(p);
+ g->m.f=shrink(p)/(double)ONE; g->m.o= shrink_order(p);
+}
+@
+
+\subsection{Baseline Skips}
+TeX's baseline nodes just store a baseline skip reference number.
+We have seen this sitation before when dealing with extended dimensions
+and the solution here is the same: a dynamicaly allocated array.
+\subsubsection{Data}
+@<Hi\TeX\ variables@>=
+typedef struct {
+ pointer ls, bs; /* line skip and baselineskip gluespecs */
+ scaled lsl; /* lineskip limit */
+} bl_defined_t;
+
+static bl_defined_t *bl_defined=NULL;
+static int bl_used=0,bl_allocated=0;
+@
+
+\subsubsection{Initialization}
+The zero baseline skip is predefined which prevents an ambiguous info value of zero
+in a baseline node.
+
+@<Initialize definitions for baseline skips@>=
+ bl_allocated=8;
+ ALLOCATE(bl_defined,bl_allocated,bl_defined_t);
+ bl_defined[zero_baseline_no].bs=zero_glue; incr(glue_ref_count(zero_glue));
+ bl_defined[zero_baseline_no].ls=zero_glue; incr(glue_ref_count(zero_glue));
+ bl_defined[zero_baseline_no].lsl=0;
+ bl_used= MAX_BASELINE_DEFAULT+1;
+ max_ref[baseline_kind]= MAX_BASELINE_DEFAULT;
+@
+\subsubsection{Allocation}
+@<Hi\TeX\ auxiliar routines@>=
+int hget_baseline_no(pointer bs, pointer ls, scaled lsl)
+{
+ static int rover=0;
+ int i;
+ for(i=0; i< bl_used; i++) /* search for an existing spec */
+ { bl_defined_t *q=&(bl_defined[rover]);
+ if (glue_equal(bs,q->bs) && glue_equal(ls,q->ls) && lsl==q->lsl)
+ return rover;
+ else if (rover==0)
+ rover=bl_used-1;
+ else
+ rover--;
+ }
+ if (bl_used>=bl_allocated)
+ RESIZE(bl_defined,bl_allocated, bl_defined_t);
+ rover=bl_used++;
+ if (rover<0x100 && section_no==2) max_ref[baseline_kind]=rover;
+ if (glue_equal(bs,zero_glue))
+ { bl_defined[rover].bs=zero_glue; incr(glue_ref_count(zero_glue)); }
+ else
+ { bl_defined[rover].bs=bs; incr(glue_ref_count(bs));}
+ if (glue_equal(ls,zero_glue))
+ { bl_defined[rover].ls=zero_glue; incr(glue_ref_count(zero_glue)); }
+ else
+ { bl_defined[rover].ls=ls; incr(glue_ref_count(ls));}
+ bl_defined[rover].lsl=lsl;
+ return rover;
+}
+
+@
+\subsubsection{Output}
+
+The following routine does not allocate a new glue definition, because the
+baselinedefinitions are output after the glue definitions. This is not perfect.
+@<Hi\TeX\ auxiliar routines@>=
+static uint8_t hout_glue_spec(pointer p);
+uint8_t hout_baselinespec(int n)
+{ info_t i=b000;
+ pointer p;
+ scaled s;
+ s=bl_defined[n].lsl;
+ if (s!=0) {HPUT32(s); i|=b001;}
+ p=bl_defined[n].bs;
+ if (p!=zero_glue)
+{ uint8_t *pos;
+ uint8_t tag;
+ HPUTNODE; /* allocate */
+ pos=hpos;
+ hpos++; /* tag */
+ tag=hout_glue_spec(p);
+ *pos=tag; DBGTAG(tag,pos);
+ DBGTAG(tag,hpos); HPUT8(tag);
+ i|=b100;
+}
+ p=bl_defined[n].ls;
+ if (p!=zero_glue)
+{ uint8_t *pos;
+ uint8_t tag;
+ HPUTNODE; /* allocate */
+ pos=hpos;
+ hpos++; /* tag */
+ tag=hout_glue_spec(p);
+ *pos=tag; DBGTAG(tag,pos);
+ DBGTAG(tag,hpos); HPUT8(tag);
+ i|=b010;
+}
+ return TAG(baseline_kind,i);
+}
+@
+
+@<Output baseline skip definitions@>=
+ DBG(DBGDEF,"Defining %d baseline skips\n",max_ref[baseline_kind]);
+ for (i=1;i<=max_ref[baseline_kind]; i++)
+ { uint32_t pos=hpos-hstart;
+ uint8_t tag;
+ hpos++; /* space for the tag */
+ HPUT8(i); /* reference */
+ tag=hout_baselinespec(i);
+ hstart[pos]=tag;
+ HPUT8(tag);
+ }
+@
+\subsubsection{Printing}
+The following function is needed in Hi\TeX\ to produce debugging output if needed.
+@<Hi\TeX\ routines@>=
+void print_baseline_skip(int i)
+{ if (0<=i && i < bl_used)
+ { print_spec(bl_defined[i].bs,0); print_char(',');
+ print_spec(bl_defined[i].ls,0); print_char(',');
+ print_scaled(bl_defined[i].lsl);
+ }
+ else
+ print("unknown");
+}
+@
+
+
+
+\subsection{Discretionary breaks}
+\subsubsection{Data}
+For discretionary breaks, we use again the pointer representation.
+@<Hi\TeX\ variables@>=
+static pointer dc_defined[0x100];
+@
+\subsubsection{Allocation}
+There are no predefined discretionary breaks and so we start with
+three auxiliar functions and the
+function to get a ``disc'' number.
+
+The first two routines are used to compare discretionary breaks
+in order to reuse already defined disc numbers.
+The pre and post break lists must consist entirely of character,
+kern, box, rule, and ligature nodes.
+Unfortunately a box node might contain all kinds of nodes
+and its content might be huge and deeply nested.
+The following routine will not make a complete comparison but will give
+up if the box content is ``too complex''.
+
+@<Hi\TeX\ auxiliar routines@>=
+static bool list_equal(pointer p, pointer q);
+static bool node_equal(pointer p, pointer q)
+{ if (is_char_node(p) && is_char_node(q) &&
+ font(p)==font(q) && character(p)==character(q))
+ return true;
+ if (!is_char_node(p) && !is_char_node(q))
+ { if (type(p)!=type(q)) return false;
+ if (type(p)==kern_node &&
+ subtype(p)==subtype(q) && width(p)==width(q))
+ return true;
+ if (type(p)==ligature_node &&
+ character(lig_char(p)) == character(lig_char(q)) &&
+ font(lig_char(p)) == font(lig_char(q)))
+ return true;
+ if (type(p)==rule_node &&
+ width(p)==width(q) && height(p)==height(q) && depth(p)==depth(q))
+ return true;
+ if ((type(p)==hlist_node || type(p)==vlist_node) &&
+ width(p)==width(q) && height(p)==height(q) && depth(p)==depth(q) &&
+ shift_amount(p)==shift_amount(q) &&
+ glue_sign(p) == glue_sign(q) &&
+ glue_order(p) == glue_order(q) &&
+ glue_set(p) == glue_set(q) &&
+ list_equal(list_ptr(p),list_ptr(q)))
+ return true;
+ }
+ return false;
+}
+
+static bool list_equal(pointer p, pointer q)
+{@+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);
+ }
+}
+
+static pointer copy_disc_node(pointer p)
+{ pointer q;
+ q=get_node(small_node_size);
+ pre_break(q)=copy_node_list(pre_break(p));
+ post_break(q)=copy_node_list(post_break(p));
+ type(q)=type(p);
+ subtype(q)=subtype(p); /* replace count and explicit bit */
+ return q;
+}
+@
+
+@<Hi\TeX\ routines@>=
+int hget_disc_no(pointer p)
+{
+ static int rover=0;
+ int i;
+ for(i=0; i<= max_ref[disc_kind]; i++)
+ { pointer q=dc_defined[rover];
+ if ( is_auto_disc(p)==is_auto_disc(q) && replace_count(p)==replace_count(q) &&
+ list_equal(pre_break(p),pre_break(q)) &&
+ list_equal(post_break(p),post_break(q)))
+ return rover;
+ else if (rover==0)
+ rover=max_ref[disc_kind];
+ else
+ rover--;
+ }
+ if (max_ref[disc_kind]>=0xFF || section_no!=2) return -1;
+
+ rover=++max_ref[disc_kind];
+ dc_defined[rover]=copy_disc_node(p);
+ @<Allocate font numbers for glyphs in the pre- and post-break lists@>@;
+ return rover;
+}
+@
+
+When we allocate disc numbers we might have fonts inside the pre-
+or post-break list, that never show up anywhere else in the content.
+These fonts would then be undefined once we start the definition section.
+So we have to make sure, all necessary fonts get defined.
+
+@<Allocate font numbers for glyphs in the pre- and post-break lists@>=
+ensure_font_no(pre_break(p));
+ensure_font_no(post_break(p));
+@
+
+\subsubsection{Output}
+@<Output discretionary break definitions@>=
+ DBG(DBGDEF,"Maximum disc reference: %d\n",max_ref[disc_kind]);
+ for (i=0;i<=max_ref[disc_kind]; i++)
+ HPUTDEF(hout_disc(dc_defined[i]),i);
+@
+
+
+
+\subsection{Parameter Lists}\label{paramlist}
+\subsubsection{Data}
+We store predefined parameter lists in a hash table in order to speed up
+finding existing parameter lists. The parameter list itself is stored as
+a byte sequence using the short \HINT\ file format.
+We link the table entries in order of increasing reference numbers to be able
+to output them in a more ``orderly'' fashion.
+
+@<Hi\TeX\ variables@>=
+
+#define PLH_SIZE 313 /* a prime number $\approx2^8\times 1.2$. */
+
+struct {int l; /* link */
+ uint32_t h; /* hash */
+ uint32_t n; /* number */
+ uint32_t s; /* size */
+ uint8_t *p; /* pointer */} pl_defined[PLH_SIZE]={{0}};
+int pl_head=-1, *pl_tail=&pl_head;
+@
+
+\subsubsection{Allocation}
+Next we define three short auxiliar routines and the |hget_param_list_no| function.
+
+@<Hi\TeX\ routines@>=
+
+static uint32_t hparam_list_hash(list_t *l)
+{ uint32_t h=0;
+ uint32_t i;
+ for (i=0;i<l->s;i++)
+ h=3*h+hstart[l->p+i];
+ return i;
+}
+
+static bool pl_equal(list_t *l, uint8_t *p)
+{ uint8_t *q=hstart+l->p;
+ uint32_t i;
+ for (i=0; i<l->s; i++)
+ if (q[i]!=p[i]) return false;
+ return true;
+}
+
+static void pl_copy(list_t *l, uint8_t *p)
+{ uint8_t *q=hstart+l->p;
+ memcpy(p,q,l->s);
+}
+
+int hget_param_list_no(list_t *l)
+{ uint32_t h;
+ int i;
+ if (l->s<=0) return -1;
+ h= hparam_list_hash(l);
+ i = h%PLH_SIZE;
+ while (pl_defined[i].p!=NULL)
+ { if (pl_defined[i].h==h && pl_equal(l,pl_defined[i].p))
+ return pl_defined[i].n;
+ i=i+199; /* some other prime */
+ if (i>=PLH_SIZE) i=i-PLH_SIZE;
+ }
+ if (max_ref[param_kind]>=0xFF || section_no!=2) return -1;
+ pl_defined[i].n=++max_ref[param_kind];
+ *pl_tail=i; pl_tail=&(pl_defined[i].l);
+ pl_defined[i].l=-1;
+ pl_defined[i].h=h;
+ pl_defined[i].s=l->s;
+ ALLOCATE(pl_defined[i].p,l->s,uint8_t);
+ pl_copy(l,pl_defined[i].p);
+ return pl_defined[i].n;
+}
+@
+
+\subsubsection{Output}
+To output parameter lists, we need a function to output a parameter node:
+
+@<Hi\TeX\ routines@>=
+void hdef_param_node(int ptype, int pnumber,int pvalue)
+{
+ if (ptype==int_type)
+ { if (pvalue==int_defined[hmap_int[pnumber]]) return;
+ else HPUTDEF(hput_int(pvalue),hmap_int[pnumber]);
+ }
+ else if (ptype==dimen_type)
+ { if (pvalue==dimen_defined[hmap_dimen[pnumber]]) return;
+ else HPUTDEF(hput_dimen(pvalue),hmap_dimen[pnumber]);
+ }
+ else if (ptype==glue_type)
+ { if (glue_equal(pvalue,glue_defined[hmap_glue[pnumber]])) return;
+ else HPUTDEF(hout_glue_spec((pointer)pvalue),hmap_glue[pnumber]);
+ }
+ else QUIT("Unexpected parameter type %d",ptype);
+}
+@
+
+
+Now we use the linked list starting with |pl_head| to output the predefined
+parameter lists sorted by their reference number.
+
+ @<Output parameter list definitions@>=
+ DBG(DBGDEF,"Defining %d parameter lists\n",max_ref[param_kind]+1);
+ for (i=pl_head;i>=0;i=pl_defined[i].l)
+ { int j;
+ DBG(DBGDEF,"Defining parameter list %d, size 0x%x\n",i,pl_defined[i].s);
+ j=hsize_bytes(pl_defined[i].s);
+ HPUTX(1+1+j+1+pl_defined[i].s+1+j+1);
+ HPUTTAG(param_kind,j+1);
+ HPUT8(pl_defined[i].n);
+ hput_list_size(pl_defined[i].s,j);
+ HPUT8(0x100-j);
+ memcpy(hpos,pl_defined[i].p,pl_defined[i].s);
+ hpos=hpos+pl_defined[i].s;
+ HPUT8(0x100-j);
+ hput_list_size(pl_defined[i].s,j);
+ HPUTTAG(param_kind,j+1);
+ }
+@
+
+\subsection{Fonts}
+
+It seems I need: (see email by Karl)
+
+
+|kpse_find_file(name,kpse_fontmap_format,false);|
+with name "ps2pk.map" or "psfonts.map" or "ttfonts.map" or cmfonts.map
+to get from the tfm name the postscript name.
+then I get the |psfont_name| ususaly its the same font name
+with .pfb appended
+so I can skip it
+%#ifdef HAVE_KPSE_ENC_FORMATS
+% enc_file=kpse_find_file(encoding,kpse_enc_format,false);
+%#else
+% enc_file=kpse_find_file(encoding,kpse_tex_ps_header_format,false);
+%#endif
+%reading the encoding is found in dvipng/dvipnd-src/enc.c
+
+
+
+\subsubsection{Data}
+To store a font definition, we define the data type |font_t|
+and an array |hfonts| of pointers indexed by \HINT\ font numbers.
+To map \HINT\ font numbers to \TeX\ font numbers, the |font_t| contains
+the |i| field; to map \TeX\ font numbers to \HINT\ font numbers,
+we use the array |hmap_font|.
+
+@<Hi\TeX\ variables@>=
+#define MAX_FONTS 0x100
+
+typedef struct {
+ uint8_t i; /* the \TeX\ font number */
+ pointer g; /* space glue */
+ pointer h; /* default hyphen */
+ pointer p[MAX_FONT_PARAMS]; /* font parameters */
+ uint16_t m; /* section number of font metric file */
+ uint16_t y; /* section number of font glyph file */
+} font_t;
+
+static font_t *hfonts[MAX_FONTS]={NULL};
+static int hmap_font[MAX_FONTS];
+@
+
+
+
+@<Initialize definitions for fonts@>=
+ for (i=0;i<0x100;i++) hmap_font[i]=-1;
+ max_ref[font_kind]=-1;
+@
+
+\subsubsection{Allocation}
+Allocation of a |font_t| record takes place when we translate a \TeX\ font
+number to a \HINT\ font number using the function |hget_font_no|, and while
+doing so discover that the corresponding \HINT\ font number does not yet exist.
+Because the |font_t| structure must be initialized after allocating it,
+we start with some auxiliar routines for that purpose.
+
+@<Hi\TeX\ auxiliar routines@>=
+static pointer find_space_glue(internal_font_number f)
+{@+font_index @!main_k;
+ pointer main_p=font_glue[f];
+if (main_p==null)
+ {@+main_p=new_spec(zero_glue);main_k=param_base[f]+space_code;
+ width(main_p)=font_info[main_k].sc; /*that's |space(f)|*/
+ stretch(main_p)=font_info[main_k+1].sc; /*and |space_stretch(f)|*/
+ shrink(main_p)=font_info[main_k+2].sc; /*and |space_shrink(f)|*/
+ font_glue[f]=main_p;
+ }
+ return main_p;
+}
+static pointer hget_font_space(uint8_t f)
+{ pointer p;
+ if (space_skip==zero_glue)
+ p = find_space_glue(f);
+ else
+ p=glue_par(space_skip_code);
+ add_glue_ref(p);
+ return p;
+}
+
+
+static pointer hget_font_hyphen(uint8_t f)
+{ pointer p;
+ int c;
+ p=new_disc();
+ c= hyphen_char[f];
+ if (c >= 0 && c < 256) pre_break(p)=new_character(f, c);
+ return p;
+}
+
+
+static void hdef_font_params(pointer p[MAX_FONT_PARAMS])
+{ /* used only for texts */
+}
+@
+\goodbreak
+
+In the following code, |f| is a \TeX\ internal font number
+and |g| is the corresponding \HINT\ font number.
+\TeX's null-font, a kind of undefined font containing no characters
+is replaced by \HINT's font number zero. Actually the nullfont should
+never appear in the output, but if it does so, either an error message
+or a more sensible replacement font might be in order.
+
+@<Hi\TeX\ auxiliar routines@>=
+
+static char *hfind_glyphs(char *filename)
+{ char *fname=NULL;
+ kpse_glyph_file_type file_ret;
+ fname=kpse_find_file(filename,kpse_type1_format,true);
+ if (fname==NULL) fname=kpse_find_file(filename,kpse_truetype_format,true);
+ if (fname==NULL) fname=kpse_find_file(filename,kpse_opentype_format,true);
+ if (fname==NULL) fname = kpse_find_glyph(filename, option_dpi,kpse_pk_format, &file_ret);
+ if (fname==NULL)
+ fprintf(stderr,"Unable to find glyph data for font %s\n",filename),exit(1);
+ return fname;
+}
+
+static uint8_t hget_font_no(uint8_t f)
+
+{ int g;
+ char *n,*fn;
+ int l;
+ if (f==0)
+ { DBG(DBGFONT,"TeX nullfont -> 0\n");
+ return 0;@+
+ }
+ g=hmap_font[f];
+ DBG(DBGFONT,"Mapping TeX font %d->%d\n",f,g);
+ if (g>=0) return g;
+ DBG(DBGDEF,"New TeX font %d\n",f);
+ if (max_ref[font_kind]>=0x100)
+ QUIT("too many fonts in use");
+ g = ++(max_ref[font_kind]);
+ ALLOCATE(hfonts[g],1,font_t);
+ hfonts[g]->i=f;
+ hmap_font[f]=g;
+ hfonts[g]->g=hget_font_space(f);
+ hfonts[g]->h=hget_font_hyphen(f);
+ pack_file_name(font_name[f], "",".tfm");
+ n = kpse_find_tfm((char*)name_of_file+1);
+ if (n==NULL)
+ QUIT("Unable to find .tfm file for font %s",(char*)name_of_file+1);
+ hfonts[g]->m= hnew_file_section(n);
+ free(n);
+ pack_file_name(font_name[f], "","");
+ n= hfind_glyphs((char*)name_of_file+1);
+ if (n==NULL)
+ QUIT("Unable to find glyph file for font %s",(char*)name_of_file+1);
+ hfonts[g]->y= hnew_file_section(n);
+ free(n);
+ return g;
+}
+@
+
+Surprisingly, not all characters that occur in a \HINT\ file are inside the
+content section; some characters might hide in the definition section
+inside the pre- or post-break list of a predefined discretionary break.
+To make sure that the fonts necessary for these characters are included
+in the final \HINT\ file, we check these lists to make sure all \TeX\ font
+numbers have a corresponting \HINT\ font number.
+
+@<Hi\TeX\ auxiliar routines@>=
+static void ensure_font_no(pointer p)
+{ while (p!=null)
+ { if (is_char_node(p))
+ hget_font_no(font(p));
+ else if (type(p)==hlist_node||type(p)==vlist_node)
+ ensure_font_no(list_ptr(p));
+ p=link(p);
+ }
+}
+@
+
+\subsubsection{Output}
+@<Output font definitions@>=
+{ int f;
+ DBG(DBGDEF,"Defining %d fonts\n",max_ref[font_kind]+1);
+ for (f=0;f<=max_ref[font_kind];f++)
+ { font_t *hf=hfonts[f];
+ internal_font_number g=hf->i;
+ uint32_t pos=hpos-hstart;
+ info_t i= b000;
+ DBG(DBGDEF,"Defining font %d size 0x%x\n",f,font_size[g]);
+ hpos++; HPUTNODE; /* space for the tag and the node */
+ HPUT8(f); /* reference */
+ hout_string(font_id_text(g));
+ if(font_size[g]>0) HPUT32(font_size[g]);
+ else HPUT32(font_dsize[g]);
+ HPUT16(hf->m);HPUT16(hf->y);
+ DBG(DBGDEF,"Defining font space\n");
+ HPUTCONTENT(hout_glue_spec,hf->g);
+ DBG(DBGDEF,"Defining font hyphen\n");
+ HPUTCONTENT(hout_disc,hf->h);
+ hdef_font_params(hf->p);
+ DBG(DBGDEF,"End of font %d\n",f);
+ hput_tags(pos,TAG(font_kind,i));
+ }
+}
+@
+
+We used the following function to write a \TeX\ string to the \HINT\ file:
+@<Hi\TeX\ auxiliar routines@>=
+
+void hout_string(int s)
+{ pool_pointer j;
+ uint8_t c;
+ j= str_start[s];
+ while(j<str_start[s+1])
+ { c= so(str_pool[j++]);
+ if (c=='%' || c < 0x20 ||c >= 0x7F)
+ { char str[4];
+ snprintf(str,4,"%%%02X",c);/* convert to printable ASCII */
+ HPUTX(3);
+ HPUT8(str[0]); HPUT8(str[1]); HPUT8(str[2]);
+ }
+ else
+ { HPUTX(1);
+ HPUT8(c);
+ }
+ }
+ HPUT8(0);
+}
+@
+
+
+We used the following macro to add tags around the font glue and the font hyphen:
+
+
+@<Hi\TeX\ macros@>=
+
+#define HPUTCONTENT(F,D) \
+ { uint8_t *_p; \
+ uint8_t _t; \
+ HPUTNODE; /* allocate */ \
+ _p=hpos++; /* tag */ \
+ _t=F(D); \
+ *_p=_t; DBGTAG(_t,_p); \
+ DBGTAG(_t,hpos); HPUT8(_t); \
+ }
+@
+
+\subsection{Labels}
+The only label that must exist always is the zero label used
+to mark the ``home'' position of a document.
+
+\subsubsection{Initialization}
+We allocate the zero label with the first call to |next_label|
+and initialize is with the value from |label_defaults|.
+We then make sure it can be found under the name ``HINT.home''.
+
+@<Initialize definitions for labels@>=
+{ char nom[]="HINT.home";
+ unsigned int h=name_hash(nom)%LABEL_HASH;
+ int i=insert_hash(h,0,nom);
+ if (i!=zero_label_no)
+ QUIT("Trying to allocate the zero label, got %d",i);
+ labels[zero_label_no]=label_defaults[zero_label_no];
+ labels[zero_label_no].next=first_label;
+ first_label=zero_label_no;
+ DBG(DBGLABEL,"Defining zero label: pos=0x%x\n",labels[zero_label_no].pos);
+}
+@
+
+\subsection{Page Templates}
+
+Once we start producing content nodes, we update the maximum numbers
+of page templates and streams from |max_page| and |max_stream|.
+These values might have changed because the templates were stored in a
+format file.
+
+@<Fix definitions of page templates@>=
+max_ref[page_kind]=max_page;
+max_ref[stream_kind]=max_stream;
+@
+
+
+\subsubsection{Output}
+
+As part of a page template, we will see stream insertion nodes.
+When we encounter an |stream_node| inside a template definition,
+we output a stream insertion point.
+
+@<cases to output whatsit content nodes@>=
+ case stream_node:
+ HPUT8(setstream_number(p));
+ tag=TAG(stream_kind,b100);
+ break;
+@
+
+
+@<Output page template definitions@>=
+ DBG(DBGDEF,"Maximum page template reference: %d\n",max_page);
+ { pointer t;
+ for (t=link(setpage_head);t!=null;t=link(t))
+ { uint32_t pos=hpos-hstart;
+ DBG(DBGDEF,"Defining page template %d\n",setpage_number(i));@/
+ hpos++; HPUTNODE; /* space for the tag and the node */
+ HPUT8(setpage_number(t));
+ hout_string(setpage_name(t));
+ HPUT8(setpage_priority(t));
+ hout_glue_node(setpage_topskip(t));
+ hput_dimen(setpage_depth(t));
+ hout_xdimen_node(setpage_height(t));
+ hout_xdimen_node(setpage_width(t));
+ hout_list_node2(setpage_list(t));
+ @<output stream definitions@>@;
+ hput_tags(pos,TAG(page_kind,0));
+ }
+}
+@
+As part of the output of page template definitions, we output
+stream definitions:
+
+@<output stream definitions@>=
+{ pointer p,q;
+ p = setpage_streams(t);
+ while (p!=null)
+ { uint8_t n;
+ n=setstream_number(p);
+ DBG(DBGDEF,"Defining stream %d at " SIZE_F "\n",n,hpos-hstart);
+ HPUTTAG(stream_kind,b100);
+ HPUT8(n);
+ hout_xdimen_node(setstream_max(p)); /* maximum height */
+ HPUT16(setstream_mag(p)); /* factor */
+ HPUT8(setstream_prefered(p)); /* prefered */
+ HPUT8(setstream_next(p)); /* next */
+ HPUT16(setstream_ratio(p)); /* ratio */
+ q=setstream_before(p);setstream_before(p)=null;
+ hout_list_node2(q);flush_node_list(q);
+ hout_xdimen_node(setstream_width(p));
+ q=setstream_topskip(p);
+ hout_glue_node(q); delete_glue_ref(q);
+ q=setstream_after(p);setstream_after(p)=null;
+ hout_list_node2(q);flush_node_list(q);
+ q=setstream_height(p);
+ hout_glue_node(q); delete_glue_ref(q);
+ HPUTTAG(stream_kind,b100);
+ p=link(p);
+ }
+}
+@
+
+
+
+\section{\HINT\ Content}\label{output}
+\TeX\ puts content nodes on the contribution list and once in a while calls |build_page| to
+move nodes from the contribution list to the current page. Hi\TeX\ has a special version of
+|build_page| that will simply remove nodes from the contribution list and passe them
+to the function |hout_node|.
+
+\noindent
+@<Hi\TeX\ routines@>=
+static void hout_node(pointer p)
+{ uint32_t pos=hpos-hstart;
+ uint8_t tag;
+ HPUTNODE;
+ hpos++;
+ if(is_char_node(p))
+ @<output a character node@>@;
+ else switch(type(p)) @/
+ { @<cases to output content nodes@>@t\1@>@;
+ default:
+ MESSAGE("\nOutput of node type=%d subtype=%d not implemented\n", type(p),subtype(p));
+ display_node(p);
+ MESSAGE("End of node");
+ hpos--;
+ return;@t\2@>@;
+ }
+ hput_tags(pos,tag);
+}
+@
+@<Hi\TeX\ function declarations@>=
+static void hout_node(pointer p);
+@
+
+To output \HINT\ nodes, we use the functions defined in {\tt hput.c} from the {\tt shrink} program
+(see~\cite{MR:format}).
+
+
+Let's start with character nodes.
+
+\subsection{Characters}
+The processing of a character node consist of three steps: checking for definitions, converting the
+\TeX\ node pointed to by |p| to a \HINT\ data type, here a |glyph_t|, and using the
+corresponding {\tt hput\_\dots} function to output the node and return the |tag|.
+In the following, we will see the same approach in many
+small variations for all kinds of nodes.
+
+@<output a character node@>=
+{ glyph_t g;
+ g.f=hget_font_no(font(p));
+ g.c = character(p);
+ tag=hput_glyph(&g);
+}
+@
+
+\subsection{Penalties}
+Integer nodes, which as content nodes are used for penalties, come next.
+Except for the embedding between |case| and |break|, the
+processing of penalty nodes follows the same pattern we have just seen.
+ @<cases to output content nodes@>=
+ case penalty_node:
+ { int n,i;
+ i = penalty(p);
+ if (i>10000) i=10000;
+ else if (i<-10000) i=-10000;
+ n=hget_int_no(i);
+ if (n<0) tag=hput_int(i);
+ else { HPUT8(n); tag=TAG(penalty_kind,0);}
+ }
+ break;
+@
+
+\subsection{Kerns}
+The kern nodes of \TeX\ contain a single dimension and a flag to mark ``explicit'' kerns.
+ @<cases to output content nodes@>=
+ case kern_node:
+ { int n;
+ n=hget_dimen_no(width(p));
+ if (n<0)
+ { kern_t k;
+ k.x=(subtype(p)==explicit);
+ k.d.w=width(p);
+ k.d.h=k.d.v=0.0;
+ tag=hput_kern(&k);
+ }
+ else
+ { HPUT8(n);
+ if (subtype(p)==explicit) tag=TAG(kern_kind,b100); else tag=TAG(kern_kind,b000);
+ }
+ }
+ break;
+@
+
+\subsection{Extended Dimensions}
+Extended dimensions do not consitute content on their own, but nodes
+containing an extended dimension are part of other nodes. Here we
+define an auxiliar function that checks for a predefined extended
+dimension and if found outputs the reference number and returns false;
+otherwise it outputs the extended dimension and returns true.
+
+
+@<Hi\TeX\ auxiliar routines@>=
+void hout_xdimen_node(pointer p)
+{ xdimen_t x;
+ x.w=xdimen_width(p);
+ x.h=xdimen_hfactor(p)/(double)ONE;
+ x.v=xdimen_vfactor(p)/(double)ONE;
+ hput_xdimen_node(&x);
+}
+bool hout_xdimen(pointer p)
+{int n = hget_xdimen_no(p);
+ if (n>=0) { HPUT8(n); return false; @+}
+ else
+ { hout_xdimen_node(p); return true;@+}
+}
+
+@
+
+\subsection{Languages}
+We map the language numbers of \TeX\ to \HINT\ language numbers
+using the |hlanguage| array.
+@<Hi\TeX\ variables@>=
+static struct {
+ uint8_t n;
+ str_number s;
+} hlanguage[0x100];
+@
+
+For any language number of \TeX, the following function returns
+the corresponding \HINT\ language number.
+Since \TeX\ knowns about a maximum of 255 languages, there is
+no need for overflow checking. The next function writes a language
+node to the output stream.
+
+@<Hi\TeX\ auxiliar routines@>=
+uint8_t hget_language_no(uint8_t n)
+{ int i;
+ for (i=0;i<=max_ref[language_kind]; i++)
+ if (hlanguage[i].n==n) return i;
+ i=++max_ref[language_kind];
+ hlanguage[i].n=n;
+ hlanguage[i].s=0; /* language unknown*/
+ return i;
+}
+
+uint8_t hout_language(uint8_t n)
+{ n=hget_language_no(n);
+ if (n<7) return TAG(language_kind,n+1);
+ else
+ { HPUT8(n); return TAG(language_kind,0); }
+}
+@
+
+After these preparations, the output of a language node is
+simple:
+
+@<cases to output whatsit content nodes@>=
+case language_node:
+ tag=hout_language(what_lang(p));
+ break;
+@
+
+Normaly \TeX\ does not produce an initial language node and
+then the language in the \HINT\ file would not be known until
+it changes for the first time.
+
+@<insert an initial language node@>=
+{ uint32_t pos = hpos-hstart;
+ hpos++;
+ hput_tags(pos,hout_language(language));
+}
+@
+
+\TeX\ offers currently no simple way to obtain a standardized
+language identifier for the current language. So if the
+string number of the language is zero, we output the string |"unknown"|;
+if somehow the language is known, we output the corresponding string
+from \TeX's string pool.
+
+@<Output language definitions@>=
+ DBG(DBGDEF,"Maximum language reference: %d\n",max_ref[language_kind]);
+ for (i=max_fixed[language_kind]+1;i<=max_ref[language_kind]; i++)
+ { HPUTNODE;
+ HPUT8(TAG(language_kind,0));
+ HPUT8(i);
+ if (hlanguage[i].s==0)
+ hput_string("unknown");
+ else
+ hout_string(hlanguage[i].s);
+ HPUT8(TAG(language_kind,0));
+ }
+@
+
+
+
+\subsection{Mathematics}
+\TeX's math nodes have an optional width---a copy of the mathsurround parameter---while
+\HINT\ math nodes do not. Therefore we have to add an explicit kern node if
+the width is nonzero. We add it before a ``math on'' node or after a ``math off''
+to get the same behavior in respect to line breaking.
+
+@<cases to output content nodes@>=
+ case math_node:
+ { kern_t k;
+ k.x=true;
+ k.d.w=width(p);
+ k.d.h=k.d.v=0.0;
+ if (subtype(p)==before)
+ { tag=TAG(math_kind,b111);
+ if (width(p)!=0)
+ { hput_tags(pos,hput_kern(&k));
+ pos=hpos-hstart;
+ HPUTNODE;
+ hpos++;
+ }
+ }
+ else
+ { tag=TAG(math_kind,b011);
+ if (width(p)!=0)
+ { hput_tags(pos,tag);
+ pos=hpos-hstart;
+ HPUTNODE;
+ hpos++;
+ tag=hput_kern(&k);
+ }
+ }
+ }
+ break;
+@
+
+\subsection{Glue and Leaders}
+Because glue specifications and glue nodes are sometimes part of other
+nodes, we start with three auxiliar functions: The first simply
+converts a Hi\TeX\ glue node into a \HINT\ |glue_t|, outputs it and
+returns the tag; the second checks for predefined glues, and the third
+outputs a complete glue node including tags.
+@<Hi\TeX\ auxiliar routines@>=
+
+static uint8_t hout_glue_spec(pointer p)
+{ @+glue_t g;
+ to_glue_t(p,&g);
+ return hput_glue(&g);@+
+}
+
+static uint8_t hout_glue(pointer p)
+{ int n;
+ n = hget_glue_no(p);
+ if (n<0)
+ return hout_glue_spec(p);
+ else
+ {@+ HPUT8(n); return TAG(glue_kind,0);@+}
+}
+
+static void hout_glue_node(pointer p)
+{ uint8_t *pos;
+ uint8_t tag;
+ HPUTNODE; /* allocate */
+ pos=hpos;
+ hpos++; /* tag */
+ tag=hout_glue(p);
+ *pos=tag; DBGTAG(tag,pos);
+ DBGTAG(tag,hpos); HPUT8(tag);
+}
+@
+
+Since \TeX\ implements leaders as a kind of glue, we have one case statement covering glue and leaders.
+
+@<cases to output content nodes@>=
+ case glue_node:
+ if (subtype(p)< cond_math_glue) /* normal glue */
+ tag= hout_glue(glue_ptr(p));
+ else if (a_leaders<=subtype(p) && subtype(p)<=x_leaders) /*leaders */
+ { hout_glue_node(glue_ptr(p));
+ { bool outer_doing_leaders=doing_leaders;
+ doing_leaders=true;
+ hout_node(leader_ptr(p));
+ doing_leaders=outer_doing_leaders;
+ }
+ tag=TAG(leaders_kind,b100|(subtype(p)-a_leaders+1));
+ }
+ else
+ QUIT("glue subtype %d not implemented\n", subtype(p));
+ break;
+@
+
+\subsection{Discretionary breaks}
+Discretionary breaks are needed in font descriptions (see section~\secref{fontdef}).
+Therefore we define a function that converts \TeX's |disc_node| pointers
+to \HINT's |disc_t|, outputs the discretionary break, and returns the tag.
+
+@<Hi\TeX\ auxiliar routines@>=
+
+uint8_t hout_disc(pointer p)
+{ disc_t h;
+ h.x=!is_auto_disc(p);
+ h.r=replace_count(p);
+ if (h.x) h.r|=0x80;
+ if (h.r!=0) HPUT8(h.r);
+ if (pre_break(p)==null && post_break(p)==null)
+ h.p.s=h.q.s=0;
+ else
+ { uint32_t lpos;
+ lpos=hpos-hstart;
+ h.p.k=list_kind;
+ hout_list_node(pre_break(p),lpos,&(h.p));
+ if (post_break(p)==null)
+ h.q.s=0;
+ else
+ { uint32_t lpos;
+ lpos=hpos-hstart;
+ h.q.k=list_kind;
+ hout_list_node(post_break(p),lpos,&(h.q));
+ }
+ }
+ return hput_disc(&h);
+}
+
+@
+
+
+@<cases to output content nodes@>=
+ case disc_node:
+ { int n;
+ n=hget_disc_no(p);
+ if (n<0)
+ tag=hout_disc(p);
+ else { HPUT8(n); tag=TAG(disc_kind,0);}
+ }
+ break;
+@
+
+\subsection{Ligatures}
+The subtype giving information on left and right boundary characters
+is ignored since the \HINT\ viewer will not do ligature or kerning
+programms and neither attempt hyphenation.
+
+@<cases to output content nodes@>=
+ case ligature_node:
+ { lig_t l;
+ pointer q;
+ l.f=hget_font_no(font(lig_char(p)));
+ HPUT8(l.f);
+ l.l.p=hpos-hstart;
+ hput_utf8(qo(character(lig_char(p))));
+ q=lig_ptr(p);
+ while(q> null)
+ { hput_utf8(qo(character(q)));
+ q=link(q);
+ }
+ l.l.s=(hpos-hstart)-l.l.p;
+ tag=hput_ligature(&l);
+ }
+ break;
+@
+\subsection{Rules}
+@<cases to output content nodes@>=
+ case rule_node:
+ { rule_t r;
+ if (is_running(height(p))) r.h=RUNNING_DIMEN; else r.h=height(p);
+ if (is_running(depth(p))) r.d=RUNNING_DIMEN; else r.d=depth(p);
+ if (is_running(width(p))) r.w=RUNNING_DIMEN; else r.w=width(p);
+ tag=hput_rule(&r);
+ }
+ break;
+@
+
+\subsection{Boxes}
+@<cases to output content nodes@>=
+ case hlist_node:
+ case vlist_node:
+ if (type(p)==hlist_node) tag=TAG(hbox_kind,0);
+ else tag=TAG(vbox_kind,0);
+ tag |= hput_box_dimen(height(p),depth(p),width(p));
+ tag |= hput_box_shift(shift_amount(p));
+ tag |= hput_box_glue_set((glue_sign(p)==stretching)?+1:-1,glue_set(p),glue_order(p));
+ hout_list_node2(list_ptr(p));
+ break;
+ @
+
+\subsection{Adjustments}
+@<cases to output content nodes@>=
+ case adjust_node:
+ hout_list_node2(adjust_ptr(p));
+ tag=TAG(adjust_kind,1);
+ break;
+@
+
+
+\subsection{Insertions}
+\TeX's insertions are mapped to \HINT\ streams.
+
+@<cases to output content nodes@>=
+case ins_node:
+@<output stream content@>@;
+break;
+@
+
+Here we consider stream content and come back to stream
+definitions in section~\secref{streamdef}.
+In a \HINT\ stream content node the stream
+parameters |floating_penalty|, |split_max_depth|, and
+|split_top_skip| are optional. If omitted, the defaults from the stream definition are
+used. This is probably also for \TeX\ the most common situation.
+It is, however, possible to supply more than one page template with different defaults
+and while not very common, \TeX\ might change the parameters at any time.
+Because we dont know which is the current page template,
+it is not posible to compare the current parameter values against the defaults,
+and we have to supply all the parameters always.
+In a future version, we might have a \TeX\ primitive that allows us to
+signal ``use the defaults''.
+
+@<output stream content@>=
+{ int k,n;
+ uint32_t pos;
+ list_t l;
+ info_t i=b000;
+ k=subtype(p);
+ n=hget_stream_no(k);
+ HPUT8(n);
+ link(temp_head)=null;
+ add_par_node(int_type,floating_penalty_code,float_cost(p));
+ add_par_node(dimen_type,split_max_depth_code,depth(p));
+ add_par_node(glue_type,split_top_skip_code,split_top_ptr(p));
+ pos=hpos-hstart;
+ l.k=param_kind;
+ n=hout_param_list(link(temp_head),pos,&l);
+ flush_node_list(link(temp_head));@+ link(temp_head)=null;
+ if (n>=0) HPUT8(n); else i=b010;
+ hout_list_node2(ins_ptr(p));
+ tag=TAG(stream_kind,i);
+}
+@
+
+
+\subsection{Marks}
+We currently ignore Marks.
+
+@<cases to output content nodes@>=
+ case mark_node: hpos--; return;
+@
+
+\subsection{Whatsit Nodes}\label{whatsit}
+We have added custom whatsit nodes and now we switch based on the subtype.
+
+@<cases to output content nodes@>=
+ case whatsit_node:
+ switch(subtype(p))
+ { @<cases to output whatsit content nodes@>@;
+ default:
+ MESSAGE("\nOutput of whatsit nodes subtype=%d not implemented\n", subtype(p));
+ display_node(p);
+ MESSAGE("End of node");
+ hpos--;
+ return;
+ }
+ break;
+@
+
+For \TeX's whatsit nodes that handle output files, no code is generated;
+hence, we call |out_what| and simply remove the tag byte that is already
+in the output.
+When the \.{\\write} node arrives here, it is normaly handled
+in |hlist_out| or |vlist_out| in an environment determined by
+the output routine. For example \LaTeX\ redefines \.{\\protect}
+as \.{\\noexpand} and these redefinitions need to be made
+before calling |out_what| which expands the token list.
+We should therefore add the definitions contained in the output routine
+to mimic expanding inside an output routine.
+
+
+@<cases to output whatsit content nodes@>=
+ case open_node:
+ case write_node:
+ case close_node: out_what(p);
+ case special_node: hpos--; return;
+@
+
+\subsection{Paragraphs}
+When we output a paragraph node, we have to consider a special case:
+The parameter list is given by a reference number but the extended dimension
+needs an |xdimen| node. In this case the reference number for the parameter
+list comes first, while otherwise the extended dimension would come first.
+To determine whether ther is a reference number for the parameter list,
+the function |hout_param_list| is writing the parameter list to the output.
+\noindent
+@<cases to output whatsit content nodes@>=
+case graf_node:
+ { uint32_t pos, xpos, xsize;
+ list_t l;
+ pointer q;
+ int n,m;
+ info_t i=b000;
+ q=graf_extent(p);
+ n=hget_xdimen_no(q);
+ if (n>=0) HPUT8(n);
+ else
+ { xpos=hpos-hstart; hout_xdimen_node(p); xsize=(hpos-hstart)-xpos; i|=b100; }
+ pos=hpos-hstart;
+ l.k=param_kind;
+ m=hout_param_list(graf_params(p),pos,&l);
+ if (m>=0)
+ { if (i&b100)
+ { HPUTX(1);
+ memmove(hstart+xpos+1,hstart+xpos,xsize);
+ hpos++;
+ hstart[xpos]=m;
+ }
+ else
+ HPUT8(m);
+ }
+ else i|=b010;
+ pos=hpos-hstart;
+ l.k=list_kind;
+ hout_list_node(graf_list(p),pos,&l);
+ tag=TAG(par_kind,i);
+ }
+ break;
+@
+
+\subsection{Baseline Skips}
+@<cases to output whatsit content nodes@>=
+ case baseline_node:
+ { int n;
+ n= baseline_node_no(p);
+ if (n>0xFF) tag=hout_baselinespec(n);
+ else
+ { HPUT8(n);
+ tag=TAG(baseline_kind,b000);
+ }
+ }
+ break;
+ @
+
+\subsection{Displayed Equations}
+@<cases to output whatsit content nodes@>=
+ case disp_node:
+ { uint32_t pos;
+ list_t l;
+ int n;
+ info_t i=b000;
+ pos=hpos-hstart;
+ l.k=param_kind;
+ n=hout_param_list(display_params(p),pos,&l);
+ if (n>=0) HPUT8(n); else i|=b100;
+ if (display_eqno(p)!=null && display_left(p))
+ { hout_node(display_eqno(p)); i|=b010; }
+ pos=hpos-hstart;
+ l.k=list_kind;
+ hout_list_node(display_formula(p),pos,&l);
+ if (display_eqno(p)!=null && !display_left(p))
+ { hout_node(display_eqno(p)); i|=b001; }
+ tag=TAG(math_kind,i);
+ /* the |display_no_bs(p)| tells whether the baseline skip is ignored */
+ }
+ break;
+@
+\subsection{Extended Boxes}
+When we output an extended box, we have to consider a special case: the page templates.
+Page templates are boxes that contain insertion points. These insertion points look
+like regular insertions but with an empty content list. As a result the |hpack| and
+|vpackage| routines might belive that they can compute the dimensions of the box
+conntent when in fact they can not.
+
+
+@<cases to output whatsit content nodes@>=
+ case hset_node:
+ case vset_node:
+ { kind_t k= subtype(p)==hset_node?hset_kind:vset_kind;
+ info_t i=b000;
+ stretch_t s;
+ int n=set_extent(p);
+ i|=hput_box_dimen(height(p),depth(p),width(p));
+ i|=hput_box_shift(shift_amount(p));
+ s.f=set_stretch(p)/(double)ONE; s.o=set_stretch_order(p);
+ hput_stretch(&s);
+ s.f=set_shrink(p)/(double)ONE; s.o=set_shrink_order(p);
+ hput_stretch(&s);
+ if (hout_xdimen(n)) i|=b001;
+ hout_list_node2(list_ptr(p));
+ tag=TAG(k,i);
+ }
+ break;
+@
+
+@<cases to output whatsit content nodes@>=
+ case hpack_node:
+ case vpack_node:
+ { kind_t k= (subtype(p)==hpack_node?hpack_kind:vpack_kind);
+ info_t i=b000;
+ int n=pack_extent(p);
+ if (pack_m(p)==additional) i|=b001;
+ if (shift_amount(p)!=0) { HPUT32(shift_amount(p)); i|=b010; }
+ if (k==vpack_kind) HPUT32(pack_limit(p));
+ if (hout_xdimen(n)) i|=b100;
+ hout_list_node2(list_ptr(p));
+ tag=TAG(k,i);
+ }
+ break;
+@
+
+\subsection{Extended Alignments}
+@<cases to output whatsit content nodes@>=
+case align_node:
+ { info_t i=b000;
+ if (align_m(p)==additional) i|=b001;
+ if (align_v(p)) i|=b010;
+ if (hout_xdimen(align_extent(p))) i|=b100;
+ hout_preamble(align_preamble(p));
+ hout_align_list(align_list(p),align_v(p));
+ tag=TAG(table_kind,i);
+ }
+break;
+@
+
+In the preamble, we remove the unset nodes and retain only the list of tabskip glues.
+@<Hi\TeX\ auxiliar routines@>=
+void hout_preamble(pointer p)
+{ pointer q,r;
+ DBG(DBGBASIC,"Writing Preamble\n");
+ q=p;
+ if (q!=null) r=link(q); else r=null;
+ while (r!=null)
+ { if (type(r)==unset_node)
+ { link(q)=link(r);
+ link(r)=null; flush_node_list(r);
+ }
+ else
+ q=r;
+ r=link(q);
+ }
+ hout_list_node2(p);
+ DBG(DBGBASIC,"End Preamble\n");
+}
+@
+
+In the |align_list| we have to convert the unset nodes back to box nodes or extended box nodes
+packaged inside an item node.
+When the viewer reads an item node, it will package the extended boxes to their natural size.
+This is the size that is needed to compute the maximum width of a column.
+
+@<Hi\TeX\ auxiliar routines@>=
+
+static void hout_item(pointer p, uint8_t t, uint8_t s)
+{ info_t i=b000;
+ uint8_t n;
+ n=span_count(p)+1;
+ DBG(DBGBASIC,"Writing Item %d/%d->%d/%d\n",type(p),n,t,s);
+ display_node(p);
+ if (n==0) QUIT("Span count of item must be positive");
+ if (n<7) i=n; else i=7;
+ HPUTTAG(item_kind,i);
+ if (i==7) HPUT8(n);
+ type(p)=t; subtype(p)=s;
+ hout_node(p);
+ HPUTTAG(item_kind,i);
+ DBG(DBGBASIC,"End Item\n");
+}
+
+
+static void hout_item_list(pointer p, bool v)
+{ list_t l;
+ uint32_t pos;
+ DBG(DBGBASIC,"Writing Item List\n");
+ l.k=list_kind;
+ HPUTTAG(item_kind,b000);
+ pos=hpos-hstart;
+ HPUTX(2);
+ HPUT8(0); /* space for the list tag */
+ HPUT8(0); /* space for the list size */
+ l.p=hpos-hstart;
+ while(p> mem_min)
+ { if (is_char_node(p)) hout_node(p);
+ else if (type(p)==unset_node) hout_item(p,v?vlist_node:hlist_node,0);
+ else if (type(p)==unset_set_node) hout_item(p,whatsit_node,v?vset_node:hset_node);
+ else if (type(p)==unset_pack_node) hout_item(p,whatsit_node,v?vpack_node:hpack_node);
+ else hout_node(p);
+ p=link(p);
+ }
+ l.s=(hpos-hstart)-l.p;
+ hput_tags(pos,hput_list(pos+1,&l));
+ HPUTTAG(item_kind,b000);
+ DBG(DBGBASIC,"End Item List\n");
+}
+
+void hout_align_list(pointer p, bool v)
+{ list_t l;
+ uint32_t pos;
+ DBG(DBGBASIC,"Writing Align List\n");
+ l.k=list_kind;
+ pos=hpos-hstart;
+ HPUTX(2);
+ HPUT8(0); /* space for the tag */
+ HPUT8(0); /* space for the list size */
+ l.p=pos+2;
+ while(p> mem_min)
+ { if (!is_char_node(p) && (type(p)==unset_node||type(p)==unset_set_node||type(p)==unset_pack_node))
+ hout_item_list(list_ptr(p),v);
+ else
+ hout_node(p);
+ p=link(p);
+ }
+ l.s=(hpos-hstart)-l.p;
+ hput_tags(pos,hput_list(pos+1,&l));
+ DBG(DBGBASIC,"End Align List\n");
+}
+@
+
+Inside the alignment list we will find various types of unset nodes, we convert them back
+to regular nodes and put them inside an item node.
+
+@<cases to output content nodes@>=
+case unset_node:
+case unset_set_node:
+case unset_pack_node:
+
+
+@
+
+\subsection{Lists}
+Two functions are provided here: |hout_list| will write a list given by the pointer |p| to the
+output at the current position |hpos|. After the list has finished, it will move the list, if necessary,
+and add the size information so that the final list will be at position |pos|;
+|hout_list_node| uses |hout_list| but adds the tags to form a complete node.
+
+
+@<Hi\TeX\ routines@>=
+
+static uint8_t hout_list(pointer p, uint32_t pos, list_t *l)
+{ l->p=hpos-hstart;
+ while(p> mem_min)
+ { hout_node(p);
+ p=link(p);
+ }
+ l->s=(hpos-hstart)-l->p;
+ return hput_list(pos,l);
+}
+
+static void hout_list_node(pointer p, uint32_t pos, list_t *l)
+/* p is a pointer to a node list,
+ output the node list at position pos (thats where the tag goes)
+ using l->k as list kind,
+ and set l->p and l->s.
+*/
+{
+ hpos=hstart+pos;
+ HPUTX(3);
+ HPUT8(0); /* space for the tag */
+ HPUT8(0); /* space for the list size */
+ HPUT8(0); /* space for the size boundary byte */
+ hput_tags(pos,hout_list(p,pos+1,l));
+}
+
+
+static void hout_list_node2(pointer p)
+{ list_t l;
+ uint32_t pos;
+ pos=hpos-hstart;
+ l.k=list_kind;
+ hout_list_node(p,pos,&l);
+}
+@
+
+@<Hi\TeX\ function declarations@>=
+static void hout_list_node(pointer p, uint32_t pos, list_t *l);
+static void hout_list_node2(pointer p);
+static uint8_t hout_list(pointer p, uint32_t pos, list_t *l);
+@
+
+\subsection{Parameter Lists}
+
+The next function is like |hout_list_node| but restricted to parameter nodes.
+
+@<Hi\TeX\ routines@>=
+
+static int hout_param_list(pointer p, uint32_t pos, list_t *l)
+/* p is a pointer to a param node list,
+ either find a reference number to a predefined parameter list
+ and return the reference number or
+ output the node list at position pos (thats where the tag goes)
+ and set l->k, l->p and l->s,
+ and return -1;
+*/
+{ int n;
+ hpos=hstart+pos;
+ if (p==null)
+ {HPUTX(2); hpos++;hput_tags(pos,TAG(param_kind,1)); l->s=0; return -1;}
+ HPUTX(3);
+ HPUT8(0); /* space for the tag */
+ HPUT8(0); /* space for the list size */
+ HPUT8(0); /* space for the size boundary byte*/
+ l->p=hpos-hstart;
+ while(p> mem_min)
+ { hdef_param_node(par_type(p),par_number(p),par_value(p).i);
+ p=link(p);
+ }
+ l->s=(hpos-hstart)-l->p;
+ n=hget_param_list_no(l);
+ if (n>=0)
+ hpos=hstart+pos;
+ else
+ hput_tags(pos,hput_list(pos+1,l));
+ return n;
+}
+@
+
+@<Hi\TeX\ function declarations@>=
+static int hout_param_list(pointer p, uint32_t pos, list_t *l);
+@
+
+\subsection{Labels, Links, and Outlines}
+\indent
+@<cases to output whatsit content nodes@>=
+case label_node: hpos--; new_label(p); return;
+case start_link_node:
+{ info_t i;
+ int n=new_start_link(p);
+ i=b010;
+ if (n>0xFF) { i|=b001; HPUT16(n);@+} @+else HPUT8(n);
+ tag= TAG(link_kind,i);
+}
+break;
+case end_link_node:
+{ info_t i;
+ int n=new_end_link();
+ i=b000;
+ if (n>0xFF) { i|=b001; HPUT16(n);@+} @+else HPUT8(n);
+ tag= TAG(link_kind,i);
+}
+break;
+case outline_node: hpos--; new_outline(p); return;
+@
+
+The routines to put labels and outlines into the definition section
+are defined in {\tt hput.c}.
+
+\subsection{Images}
+\indent
+@<cases to output whatsit content nodes@>=
+ case image_node:
+ { image_t i;
+ i.n=image_no(p);
+ i.w=image_width(p);
+ i.h=image_height(p);
+ i.p.f=image_stretch(p)/(double)ONE;
+ i.p.o=image_stretch_order(p);
+ i.m.f=image_shrink(p)/(double)ONE;
+ i.m.o=image_shrink_order(p);
+ tag=hput_image(&i);
+ }
+ break;
+@
+
+
+
+\subsection{Text}
+The routines in this section are not yet ready.
+
+@<Hi\TeX\ routines@>=
+
+#if 0
+static void hchange_text_font(internal_font_number f)
+{ uint8_t g;
+ if (f!=hfont)
+ { g=get_font_no(f);
+ if (g<8)
+ hputcc(FONT0_CHAR+g);
+ else
+ { hputcc(FONTN_CHAR);
+ hputcc(g);
+ }
+ hfont=f;
+ }
+}
+
+static void hprint_text_char(pointer p)
+{ uint8_t f,c;
+ f = font(p);
+ c = character(p);
+ hchange_text_font(f);
+ if (c<=SPACE_CHAR) hputcc(ESC_CHAR);
+ hputcc(c);
+}
+
+
+static void hprint_text_node(pointer p)
+{ switch(type(p))
+ { case hlist_node:
+ /* this used to be the |par_indent| case */
+ goto nodex;
+ case glue_node:
+ if (subtype(p)>= cond_math_glue) goto nodex;
+ else
+ { pointer q=glue_ptr(p);
+ int i;
+ if (glue_equal(f_space_glue[hfont],q))
+ { hputc(SPACE_CHAR); return; }
+ if (glue_equal(f_xspace_glue[hfont],q))
+ { hputcc(XSPACE_CHAR); return; }
+ if (f_1_glue[hfont]==0 && (subtype(p)-1==space_skip_code))
+ { pointer r=glue_par(subtype(p)-1);
+ add_glue_ref(r);
+ f_1_glue[hfont]=r;
+ }
+ if (f_1_glue[hfont]!=0 && glue_equal(f_1_glue[hfont],q))
+ { hputcc(GLUE1_CHAR); return; }
+ if (f_2_glue[hfont]==0 && (subtype(p)-1==space_skip_code || subtype(p)-1==xspace_skip_code))
+ { pointer r=glue_par(subtype(p)-1);
+ add_glue_ref(r);
+ f_2_glue[hfont]=r;
+ }
+ if (f_2_glue[hfont]!=0 && glue_equal(f_2_glue[hfont],q))
+ { hputcc(GLUE2_CHAR); return; }
+ if (f_3_glue[hfont]==0)
+ { f_3_glue[hfont]=q;
+ add_glue_ref(q);
+ }
+ if (f_3_glue[hfont]!=0 && glue_equal(f_3_glue[hfont],q))
+ { hputcc(GLUE3_CHAR); return; }
+ i = hget_glue_no(q);
+ if (i>=0)
+ { hputcc(GLUEN_CHAR); hputcc(i); return; }
+ }
+ break;
+ case ligature_node:
+ { int n;
+ pointer q;
+ for (n=0,q=lig_ptr(p); n<5 && q!=null; n++,q=link(q)) continue;
+ if (n==2) hputcc(LIG2_CHAR);
+ else if (n==3) hputcc(LIG3_CHAR);
+ else if (n==0) hputcc(LIG0_CHAR);
+ else goto nodex;
+ hprint_text_char(lig_char(p));
+ for (q=lig_ptr(p);q!=null; q=link(q)) hprint_text_char(q);
+ return;
+ }
+ case disc_node:
+ if (post_break(p)==null && pre_break(p)!=null && replace_count(p)==0)
+ { pointer q;
+ q=pre_break(p);
+ if (is_char_node(q) && link(q)==null && font(q)==hfont && character(q)== hyphen_char[hfont])
+ { if (is_auto_disc(p)) hputcc(DISC1_CHAR);
+ else hputcc(DISC2_CHAR);
+ return;
+ }
+ }
+ else if (post_break(p)==null && pre_break(p)==null && replace_count(p)==0 && !is_auto_disc(p))
+ { hputcc(DISC3_CHAR); return; }
+ break;
+ case math_node:
+ if(width(p)!=0) goto nodex;
+ if(subtype(p)==before) hputcc(MATHON_CHAR);
+ else hputcc(MATHOFF_CHAR);
+ return;
+ default:
+ break;
+ }
+nodex:
+ hout_node(p);
+}
+
+static void hprint_text(pointer p)
+{ internal_font_number f=hfont;
+ nesting++;
+ hprint_nesting();
+ hprintf("<text ");
+ while(p> mem_min)
+ { if (is_char_node(p))
+ hprint_text_char(p);
+ else
+ hprint_text_node(p);
+ p=link(p);
+ }
+ hchange_text_font(f);
+ hprintf(">\n");
+ nesting--;
+}
+#endif
+
+@
+
+
+\section{\HINT\ Limitations}
+\itemize
+\item Kerns and glues using a width that depends on \.{\\hsize} or
+ \.{\\vsize} are not yet supported.
+\item Tables where the width of a column depends on \.{\\hsize} or
+ \.{\\vsize} are not tested and probably not yet supported.
+\item The encoding of horizontal lists as texts is not yet supported,
+ but it would make the \HINT\ file shorter and much better to read
+ when stretched into long \HINT\ format.
+\enditemize
+
+
+\appendix
+
+\section{Source Files}
+
+\subsection{Basic types}
+To define basic types we use the file {\tt bastypes.h} from~\cite{MR:format}.
+
+\subsection{Hi\TeX\ routines: \tt hitex.c}
+\indent
+@p
+#include <stdio.h>
+#include <kpathsea/kpathsea.h>
+#include "basetypes.h"
+#include "error.h"
+#include "hformat.h"
+#include "hput.h"
+#include "htex.h"
+#include "hitex.h"
+
+/* from ktex.ch */
+
+@<Hi\TeX\ macros@>@;
+@<Hi\TeX\ variables@>@;
+@<Hi\TeX\ function declarations@>@;
+
+@<Hi\TeX\ auxiliar routines@>@;
+
+@<Hi\TeX\ routines@>@;
+
+@
+
+
+\subsection{Hi\TeX\ prototypes: \tt hitex.h}
+@(hitex.h@>=
+extern void hint_open(void);
+extern void hint_close(void);@#
+
+extern pointer new_xdimen(scaled w, scaled h, scaled v);
+extern void hget_image_information(pointer p);
+extern pointer new_baseline_node(pointer bs, pointer ls, scaled lsl);
+extern pointer new_set_node(void);
+extern void hline_break(int final_widow_penalty);
+extern pointer new_disp_node(void);
+extern void add_par_node(eight_bits t, eight_bits n, int v);
+extern pointer new_image_node(str_number n, char *a, char *e);
+extern pointer new_setpage_node(eight_bits k, str_number n);
+extern int hget_stream_no(int i);
+extern pointer new_setstream_node(eight_bits n);
+extern void hfinish_stream_group(void);
+extern void hfinish_page_group(void);
+extern void hfinish_stream_before_group(void);
+extern void hfinish_stream_after_group(void);
+extern void hfinish_outline_group(void);
+extern void execute_output(pointer p);
+extern void hint_debug_help(void);
+@
+
+\crosssections
+
+\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 hitex.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/hitexdir/htex.w b/Build/source/texk/web2c/hitexdir/htex.w
new file mode 100644
index 00000000000..1115344785d
--- /dev/null
+++ b/Build/source/texk/web2c/hitexdir/htex.w
@@ -0,0 +1,29180 @@
+% This file is part of HINT
+% Copyright 2017-2021 Martin Ruckert
+%
+% 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.
+
+% The HINT program is derived from Donald E. Knuth's TeX and the
+% subsequent e-TeX extension of TeX.
+
+% e-TeX is copyright (C) 1999-2012 by P. Breitenlohner (1994,98 by the NTS
+% team); all rights are reserved. Copying of this file is authorized only if
+% (1) you are P. Breitenlohner, or if (2) you make absolutely no changes to
+% your copy. (Programs such as TIE allow the application of several change
+% files to tex.web; the master files tex.web and etex.ch should stay intact.)
+
+% See etex_gen.tex for hints on how to install this program.
+% And see etripman.tex for details about how to validate it.
+
+% This program is directly derived from Donald E. Knuth's TeX;
+% the change history which follows and the reward offered for finders of
+% bugs refer specifically to TeX; they should not be taken as referring
+% to e-TeX, although the change history is relevant in that it
+% demonstrates the evolutionary path followed. This program is not TeX;
+% that name is reserved strictly for the program which is the creation
+% and sole responsibility of Professor Knuth.
+
+% 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).
+
+% A reward of $327.68 will be paid to the first finder of any remaining bug.
+
+% A preliminary version of TeX--XeT was released in April 1992.
+% TeX--XeT version 1.0 was released in June 1992,
+% version 1.1 prevented arith overflow in glue computation (Oct 1992).
+% A preliminary e-TeX version 0.95 was operational in March 1994.
+% Version 1.0beta was released in May 1995.
+% Version 1.01beta fixed bugs in just_copy and every_eof (December 1995).
+% Version 1.02beta allowed 256 mark classes (March 1996).
+% Version 1.1 changed \group{type,level} -> \currentgroup{type,level},
+% first public release (October 1996).
+% Version 2.0 development was started in March 1997;
+% fixed a ligature-\beginR bug in January 1998;
+% was released in March 1998.
+% Version 2.1 fixed a \marks bug (when min_halfword<>0) (January 1999).
+% Version 2.2 development was started in Feb 2003; released in Oct 2004.
+% fixed a bug in sparse array handling (0=>null), Jun 2002;
+% fixed a bug in \lastnodetype (cur_val=>cur_val_level)
+% reported by Hartmut Henkel <hartmut_henkel@@gmx.de>,
+% fix by Fabrice Popineau <Fabrice.Popineau@@supelec.fr>,
+% Jan 2004;
+% another bug in sparse array handling (cur_ptr=>cur_chr)
+% reported by Taco Hoekwater <taco@@elvenkind.com>, Jul 2004;
+% fixed a sparse array reference count bug (\let,\futurelet),
+% fix by Bernd Raichle <berd@@dante.de>, Aug 2004;
+% reorganized handling of banner, additional token list and
+% integer parameters, and similar in order to reduce the
+% interference between eTeX, pdfTeX, and web2c change files.
+% adapted to tex.web 3.141592, revised glue rounding for mixed
+% direction typesetting;
+% fixed a bug in the revised glue rounding code, detected by
+% Tigran Aivazian <tigran@@aivazian.fsnet.co.uk>, Oct 2004.
+% Version 2.3 development was started in Feb 2008; released in Apr 2011.
+% fixed a bug in hyph_code handling (\savinghyphcodes)
+% reported by Vladimir Volovich <vvv@@vsu.ru>, Feb 2008.
+% fixed the error messages for improper use of \protected,
+% reported by Heiko Oberdiek
+% <heiko.oberdiek@@googlemail.com>, May 2010.
+% some rearrangements to reduce interferences between
+% e-TeX and pTeX, in part suggested by Hironori Kitagawa
+% <h_kitagawa2001@@yahoo.co.jp>, Mar 2011.
+% Version 2.4 fixed an uninitialized line number bug, released in May 2012.
+% Version 2.5 development was started in Aug 2012; released in Feb 2013.
+% better tracing of font definitions, reported by
+% Bruno Le Floch <blflatex@@gmail.com>, Jul 2012.
+% Version 2.6 development was started in Mar 2013; released in ??? 201?.
+% enable hyphenation of text between \beginL and \endL or
+% between \beginR and \endR, problem reported by
+% Vafa Khalighi <vafalgk@@gmail.com>, Nov 2013.
+% better handling of right-to-left text -- to be done.
+
+% Although considerable effort has been expended to make the e-TeX program
+% correct and reliable, no warranty is implied; the author disclaims any
+% obligation or liability for damages, including but not limited to
+% special, indirect, or consequential damages arising out of or in
+% connection with the use or performance of this software. This work has
+% been a ``labor of love'' and the author hopes that users enjoy it.
+
+% 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\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\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{k\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
+ #1\kern.05em}}
+\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\dotdot{\mathrel{.\,.}} % double dot, used only in math mode
+@s dotdot TeX
+@* Introduction.
+This is \eTeX, a program derived from and extending the capabilities of
+\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 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 `\eTeX' 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 ``\.{e-TRIP} test'' is available for
+helping to determine whether a particular implementation deserves to be
+known as `\eTeX'.
+
+@d eTeX_version 2 /* \.{\\eTeXversion} */
+@d eTeX_revision ".6" /* \.{\\eTeXrevision} */
+@d eTeX_version_string "-2.6" /*current \eTeX\ version*/
+@#
+@d eTeX_banner "This is e-TeX, Version 3.14159265" " (HINT) " eTeX_version_string
+ /*printed when \eTeX\ starts*/
+@#
+@d TeX_banner "This is TeX, Version 3.14159265 (HINT)" /*printed when \TeX\ starts*/
+@#
+@d banner eTeX_banner
+@#
+@d TEX ETEX /*change program name into |ETEX|*/
+@#
+@d eTeX_states 1 /*number of \eTeX\ state variables in |eqtb|*/
+
+@ 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 \AM\ 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 \.{WEB}.
+@.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.
+
+@p @<Header files and function declarations@>@;
+#include "htex.h"
+#include "hitex.h"
+@<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/, but occasionally
+it is necessary to insert header files and declare functions
+very early in the program. For example, the function |s_no|
+which uses |make_string| to convert a \CEE/ string into
+a string number is used in |initialize| and needs a forward
+declaration.
+
+@<Header files and function declarations@>=
+extern int s_no(const char *str);
+
+@ 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 `$|debug|\ldots|debug|$', with apologies
+to people who wish to preserve the purity of English.
+
+Similarly, there is some conditional code delimited by
+`$|stat|\ldots|tats|$' that is intended for use when statistics are to be
+kept about \TeX's memory usage. The |stat| $\ldots$ |tats| code also
+implements diagnostic information for \.{\\tracingparagraphs} and
+\.{\\tracingpages}.
+@^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
+|#ifdef| |INIT|\dots\ |#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) {@+@<Initialize table entries (done by \.{INITEX} only)@>@;@+}
+#endif
+
+@ Most of the external declarations needed are included using standard
+header files.
+
+@s uint8_t int
+@s int16_t int
+@s uint16_t int
+@s int32_t int
+@s uint32_t int
+@s halfword int
+@s in TeX
+@s line normal
+@s to do
+
+@<Header files and function declarations@>=
+#include <stdint.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.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*/
+@!empty_string=256 /*the empty string follows after 256 characters*/
+
+@ 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 \PASCAL's |const| 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 15000 /*maximum number of control sequences; it should be at most
+ about |(mem_max-mem_min)/(double)10|*/
+@d hash_prime 8501 /*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); /* k\TeX\ */
+for (i=0177; i<=0377; i++) xchr[i]=chr(i); /* k\TeX\ */
+
+@ 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...@>=
+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*/
+int @!name_length;@/ /*this many characters are actually
+ relevant in |name_of_file| (the rest are blank)*/
+
+@ k\TeX\ uses the {\tt kpathsearch} library to implement access to files.
+To do so we declare \TeX's functions here and postpone the
+actual implementations. These functions do not to 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 procedures return |false|.
+This allows \TeX\ to undertake appropriate corrective action.
+@^system dependencies@>
+
+@p static bool a_open_in(alpha_file *f); /*open a text file for input*/
+static bool a_open_out(alpha_file *f); /*open a text file for output*/
+static bool b_open_in(byte_file *f); /*open a binary file for input*/
+static bool b_open_out(byte_file *f); /*open a binary file for output*/
+static bool w_open_in(word_file *f); /*open a word file for input*/
+#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 program 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; /* k\TeX\ */
+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...@>=
+packed_ASCII_code @!str_pool[pool_size+1]; /*the characters*/
+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 strn_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;
+}
+
+static bool str_eq_str(str_number @!s, char *@!t)
+ /*test equality of strings*/
+{@+int j, @!k; /*running indices*/
+if (length(s)!=(int)strlen(t)) return false;
+j=str_start[s];k=0;
+while (j < str_start[s+1])
+ if (str_pool[j++]!=t[k++]) return false;
+return true;
+}
+
+@ 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 void 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@>;
+}
+
+@ @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 81 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 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();
+}
+
+@* 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) @[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...@>=
+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...@>=
+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...@>=
+void print(char *s) /* the simple version */
+{ 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 the banner
+and format identifier together will occupy at most |max_print_line|
+character positions.
+
+k\TeX, according to the conventions of \TeX\ Live,
+ prints 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...@>=
+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...@>=
+void print_int(int @!n) /*prints an integer in decimal form*/
+{@+int k; /*index to current digit; we assume that $|n|<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...@>=
+void print_err(char *s)
+{@+if (interaction==error_stop_mode) wake_up_terminal;
+ if (filelineerrorstylep) print_file_line(); /* k\TeX\ */
+ 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...@>=
+int @!interaction; /*current level of interaction*/
+
+@ @<Set init...@>=interaction=error_stop_mode;
+
+@ \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);@/
+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: 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 continue|@>@;@+break;
+@t\4\4@>@;
+#ifdef @!DEBUG
+case 'D': {@+debug_help();goto resume;@+}
+#endif
+case 'E': if (base_ptr > 0)
+ {@+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 continue|@>@;
+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) 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...@>=
+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...@>=
+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...@>=
+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 some instructions 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 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 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 float(A) ((double)(A)) /*convert from |glue_ratio| to type |double|*/
+@d unfloat(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 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 {
+ 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*float(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...@>=
+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*/
+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$
+|tats| is not ``commented out,'' \TeX\ will run a bit slower but it will
+report these statistics when |tracing_stats| is sufficiently large.
+
+@<Glob...@>=
+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 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 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 9 /*number of words to allocate for a box, set, or pack 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|, since that's the desired
+|span_count| value if this |hlist_node| is changed to an |unset_node|.
+
+@p 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 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.
+This field occupies a full word instead of a halfword, because
+there's nothing to put in the other halfword; it is easier in \PASCAL\ to
+use the full word than to risk leaving garbage in the unused half.
+
+@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) mem[A+1].i /*head of the token list for a mark*/
+
+@ 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) mark_ptr(A) /*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)&0x7F) /*how many subsequent nodes to replace*/
+@d set_replace_count(A,B) (subtype(A)=(B)&0x7F)
+@d set_auto_disc(A) (subtype(A)|=0x80)
+@d is_auto_disc(A) (subtype(A)&0x80)
+@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 pointer new_disc(void) /*creates an empty |disc_node|*/
+{@+pointer p; /*the new node*/
+p=get_node(small_node_size);type(p)=disc_node;
+set_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 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 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 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 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 unset_set_node 32 /*|type| for an unset |set_node|*/
+@d unset_pack_node 33 /*|type| for an unset |pack_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 setpage_head mem_top-14 /*head of page template list build by |new_setpage_node|*/
+@d max_page type(setpage_head) /* maximum page template number */
+@d max_stream subtype(setpage_head) /* maximum stream number */
+@d hi_mem_stat_min mem_top-14 /*smallest statically allocated word in
+ the one-word |mem|*/
+@d hi_mem_stat_usage 15 /*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...@>=
+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 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: case unset_set_node: case unset_pack_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 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|@>@;
+static void print_xdimen(pointer p)
+{ print_scaled(xdimen_width(p));
+ if (xdimen_hfactor(p)!=0)
+ { print_char('+');print_scaled(xdimen_hfactor(p));print("*hsize");}
+ if (xdimen_vfactor(p)!=0)
+ { print_char('+');print_scaled(xdimen_vfactor(p));print("*vsize");}
+}
+extern void print_baseline_skip(int i);
+
+@ 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...@>=
+int @!depth_threshold; /*maximum nesting depth in box displays*/
+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 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);
+ }
+
+}
+
+void display_node(pointer p)@t~@>
+{
+if (is_char_node(p)) print_font_and_char(p);
+else switch (type(p)) {
+ case hlist_node: case vlist_node: case unset_node: case unset_set_node: case unset_pack_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_set_node) print("set");
+else if (type(p)==unset_pack_node) print("pack");
+else 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=float(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("; 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");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 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 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 void delete_glue_ref(pointer @!p) /*|p| points to a glue specification*/
+fast_delete_glue_ref(p)
+void delete_xdimen_ref(pointer @!p) /*|p| points to a xdimen specification*/
+{@+if (xdimen_ref_count(p)==null) free_node(p, xdimen_node_size);
+ else decr(xdimen_ref_count(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 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: case unset_set_node: case unset_pack_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*/
+@d add_xdimen_ref(A) incr(xdimen_ref_count(A)) /*new reference to an xdimen*/
+
+@ 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 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: case unset_set_node: case unset_pack_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} )*/
+@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 in the |ship_out| routine.
+
+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 (-1000*unity) /*|prev_depth| value that is ignored*/
+@d unknown_depth (-2000*unity) /*|prev_depth| value that is unknown*/
+
+@<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...@>=
+list_state_record @!nest[nest_size+1];
+int @!nest_ptr; /*first unused location of |nest|*/
+static int @!max_nest_stack; /*maximum of |nest_ptr| when pushing*/
+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 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 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)
+ { if (a.sc <= unknown_depth) print("unknown"); else 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 be 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_null_font (frozen_control_sequence+10)
+ /*permanent `\.{\\nullfont}'*/
+@d font_id_base (frozen_null_font-font_base)
+ /*begins table of 257 permanent font identifiers*/
+@d undefined_control_sequence (frozen_null_font+257) /*dummy location*/
+@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 tex_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 heldover 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 tex_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 eTeX_state_code (etex_int_base+5) /*\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)
+
+@<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.
+@^system dependencies@>
+k\TeX\ calls |tl_now| to obtain the current time as a |tm| structure.
+@p static void fix_date_and_time(void)
+{@+struct tm *gmt=tl_now();
+ time= gmt->tm_hour*60+gmt->tm_min;
+ day= gmt->tm_mday;
+ month=gmt->tm_mon+1;
+ year=gmt->tm_year+1900;
+}
+
+@ @<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 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;
+ }
+}
+@#
+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 another global variable, if the previous
+routines are going to work.
+
+@<Glob...@>=
+static int @!old_setting;
+
+@ 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 dimen_pars 21 /*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 dimen_hfactor(A) hfactor_eqtb[scaled_base+A].sc
+@d dimen_vfactor(A) vfactor_eqtb[scaled_base+A].sc
+@d dimen_par_hfactor(A) hfactor_eqtb[dimen_base+A].sc
+@d dimen_par_vfactor(A) vfactor_eqtb[dimen_base+A].sc
+@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)
+
+@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;
+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++) hfactor_eqtb[k].sc=vfactor_eqtb[k].sc=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...@>=
+memory_word @!eqtb0[eqtb_size-active_base+1], *const @!eqtb = @!eqtb0-active_base;
+memory_word hfactor_eqtb0[dimen_pars+256]={{{0}}}, *const @!hfactor_eqtb = @!hfactor_eqtb0-dimen_base;
+memory_word vfactor_eqtb0[dimen_pars+256]={{{0}}}, *const @!vfactor_eqtb = @!vfactor_eqtb0-dimen_base;
+scaled par_shape_hfactor=0, par_shape_vfactor=0;
+scaled hhsize=0,hvsize=0;
+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...@>=
+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{@+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*/
+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;
+}
+#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: if (chr_code==0) print_esc("expandafter")
+ @<Cases of |expandafter| for |print_cmd_chr|@>;@+break;
+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");@+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 current 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 page_group 17
+@d stream_group 18
+@d stream_before_group 19
+@d stream_after_group 20
+@d outline_group 21
+@d max_group_code 21
+
+@<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 memory_word @!save_hfactor[save_size+1];
+static memory_word @!save_vfactor[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
+@d saved_hfactor(A) save_hfactor[save_ptr+A].i
+@d saved_vfactor(A) save_vfactor[save_ptr+A].i
+
+@p 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];
+ if (p>=dimen_base)
+ { save_hfactor[save_ptr]=hfactor_eqtb[p];
+ save_vfactor[save_ptr]=vfactor_eqtb[p];
+ }
+ else if (p==par_shape_loc)
+ { save_hfactor[save_ptr].i=par_shape_hfactor;
+ save_vfactor[save_ptr].i=par_shape_vfactor;
+ }
+ 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;
+if (p==par_shape_loc)
+{ par_shape_hfactor=cur_hfactor;
+ par_shape_vfactor=cur_vfactor;
+}
+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 (cur_level==level_one)@t\1@>
+ { if (p==dimen_base+hsize_code)
+ { hhsize=w+round(((double)cur_hfactor*hhsize +(double)cur_vfactor*hvsize)/unity); return; @+}
+ if (p==dimen_base+vsize_code)
+ { hvsize=w+round(((double)cur_hfactor*hhsize +(double)cur_vfactor*hvsize)/unity); return; @+}
+ }
+ if (xeq_level[p]!=cur_level)
+ {@+eq_save(p, xeq_level[p]);xeq_level[p]=cur_level;
+ }
+eqtb[p].i=w;
+if (p>=dimen_base)
+{ hfactor_eqtb[p].i=cur_hfactor;
+ vfactor_eqtb[p].i=cur_vfactor;
+}
+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")@;@/
+{@t\1@>xeq_level[p]=level_one;
+ if (p==dimen_base+hsize_code)
+ hhsize=w+round(((double)cur_hfactor*hhsize +(double)cur_vfactor*hvsize)/unity);
+ else if (p==dimen_base+vsize_code)
+ hvsize=w+round(((double)cur_hfactor*hhsize +(double)cur_vfactor*hvsize)/unity);
+ else
+ { eqtb[p].i=w;
+ if (p>=dimen_base)
+ { hfactor_eqtb[p].i=cur_hfactor;
+ vfactor_eqtb[p].i=cur_vfactor;
+ }
+}
+}
+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);
+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*/
+ if (p==par_shape_loc)
+ { par_shape_hfactor=save_hfactor[save_ptr].i;
+ par_shape_vfactor=save_vfactor[save_ptr].i;
+ }
+#ifdef @!STAT
+ if (tracing_restores > 0) restore_trace(p,"restoring");
+#endif
+@;@/
+ }
+else if (xeq_level[p]!=level_one)
+ {@t\1@>@+eqtb[p]=save_stack[save_ptr];
+ if (p>=dimen_base)
+ { hfactor_eqtb[p]=save_hfactor[save_ptr];
+ vfactor_eqtb[p]=save_vfactor[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 the final right brace of an output routine
+is included at the end of its token list.
+
+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)
+ {@+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;
+ } 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*/
+
+@ 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;
+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|.
+
+@<Glob...@>=
+static int @!align_state; /*group level with respect to current alignment*/
+
+@ 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 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{@+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);
+ }
+ }
+ }
+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@>
+
+@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;
+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]=0; /* k\TeX\ */
+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|*/
+}
+
+@ 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*/
+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;
+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.
+
+@<Glob...@>=
+static pointer @!par_loc; /*location of `\.{\\par}' in |eqtb|*/
+static halfword @!par_token; /*token representing `\.{\\par}'*/
+
+@ @<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 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);
+ reswitch: cur_cmd=cat_code(cur_chr);
+ @<Change state if necessary, and |goto switch| 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.
+
+@<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);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;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;
+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: if (cur_chr==0) @<Expand the token after the next token@>@;
+ else@<Negate a boolean conditional and |goto reswitch|@>@;@+break;
+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.
+
+@d store_new_token(A) {@+q=get_avail();link(p)=q;info(q)=A;
+ p=q; /*|link(p)| is |null|*/
+ }
+@d fast_store_new_token(A) {@+fast_get_avail(q);link(p)=q;info(q)=A;
+ p=q; /*|link(p)| is |null|*/
+ }
+
+@ @<Manufacture a control...@>=
+{@+r=get_avail();p=r; /*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 |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@>
+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 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 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...@>=
+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) {
+ 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");
+ } @+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@>=
+{@+if (cur_mark[cur_chr]!=null)
+ begin_token_list(cur_mark[cur_chr], 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*/
+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 |return| 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;
+}
+
+@ 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 continue|@>;
+@<Contribute the recently matched tokens to the current parameter, and |goto continue|
+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 continue|@>@;
+else@<Store the current token, but |goto continue| 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 continue| 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 continue|@>=
+{@+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)&&(p!=temp_head))
+ {@+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 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*/
+@d has_factor (cur_hfactor!=0 || cur_vfactor!=0)
+
+@<Glob...@>=
+static int @!cur_val, @!cur_hfactor, @!cur_vfactor; /*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);
+ if (m>=dimen_base)@/
+ { cur_hfactor=hfactor_eqtb[m].sc;@+
+ cur_vfactor=vfactor_eqtb[m].sc;@+
+ }
+ else
+ cur_hfactor=cur_vfactor=0;@/
+ @+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 (badness_code+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*/
+
+@<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 >= 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;
+ cur_val_level=cur_chr;
+ if (!is_char_node(tail)&&(mode!=0))
+ switch (cur_chr) {
+ case int_val: if (type(tail)==penalty_node) cur_val=penalty(tail);@+break;
+ case dimen_val: if (type(tail)==kern_node) cur_val=width(tail);@+break;
+ case glue_val: if (type(tail)==glue_node)
+ {@+cur_val=glue_ptr(tail);
+ if (subtype(tail)==mu_glue) cur_val_level=mu_val;
+ }
+ } /*there are no other cases*/
+ else if ((mode==vmode)&&(tail==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_hfactor=dimen_hfactor(cur_val);
+ cur_vfactor=dimen_vfactor(cur_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);@+ negate(cur_hfactor); @+negate(cur_vfactor); @+}
+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;
+cur_hfactor=cur_vfactor=0;
+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)||
+(abs(cur_hfactor) >= 010000000000) || (abs(cur_vfactor) >= 010000000000))
+ @<Report that this dimension is out of range@>;
+if (negative) @/{@+ negate(cur_val);@+ negate(cur_hfactor); @+negate(cur_vfactor);@+ }
+}
+
+@ @<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;
+if (has_factor)
+{ print_err("Factor is not constant. Linear component ignored");
+ cur_hfactor=cur_vfactor=0;
+}
+@<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:
+if (has_factor)
+{ cur_hfactor=nx_plus_y(save_cur_val, cur_hfactor, xn_over_d(cur_hfactor, f, unity));
+ cur_vfactor=nx_plus_y(save_cur_val, cur_vfactor, xn_over_d(cur_vfactor, f, unity));
+}
+ cur_val=nx_plus_y(save_cur_val, v, xn_over_d(v, f, unity));
+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); negate(cur_hfactor); negate(cur_vfactor);}
+ }
+@<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)
+ /*changes the string |str_pool[b dotdot pool_ptr]| 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.
+
+\eTeX\ adds \.{\\eTeXrevision} such that |job_name_code| remains last.
+
+@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 job_name_code etex_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 eTeX_revision_code: print_esc("eTeXrevision");@+break;
+ default:print_esc("jobname");
+ } @+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 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*/
+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 eTeX_revision_code: do_nothing;@+break;
+case job_name_code: if (job_name==0) open_log_file();
+} /*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);
+} /*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;
+}
+
+@ @<Scan and build the parameter part...@>=
+{@+loop{@+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_cmd==left_brace)
+ {@+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...@>
+ help1("I'm going to ignore the # sign you just used.");error();
+ }
+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;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 level zero 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 char *@!cur_area; /*file area just scanned, or \.{""}*/
+static char *@!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*/
+static int @!cur_file_name_length;
+
+@ 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@>
+
+The filename is scanned into |cur_file_name|; |cur_ext| and |cur_aire|
+will point into this buffer.
+
+@d MAX_CUR_FILE_NAME 1024
+
+@p static char cur_file_name[MAX_CUR_FILE_NAME+1];
+static void begin_name(void)
+{@+area_delimiter=ext_delimiter=cur_file_name_length=0;
+}
+
+@ 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==' ') return false;
+ if (cur_file_name_length>= MAX_CUR_FILE_NAME )
+ { overflow("file name length", MAX_CUR_FILE_NAME);
+ return false;
+ }
+ else
+ { /*contribute |c| to the current string*/
+ cur_file_name[cur_file_name_length]=c;
+ if (c=='/')
+ {@+area_delimiter=cur_file_name_length;ext_delimiter=0;
+ }
+ else if (c=='.') ext_delimiter=cur_file_name_length;
+ cur_file_name_length++;
+ return true;
+ }
+}
+
+@ The third.
+@^system dependencies@>
+
+@p static void end_name(void)
+{@+int i=0;
+ if (str_ptr+1 > max_strings)
+ overflow("number of strings", max_strings-init_str_ptr);
+@:TeX capacity exceeded number of strings}{\quad number of strings@>
+cur_file_name[cur_file_name_length]=0;
+if (area_delimiter==0) cur_area=cur_file_name+cur_file_name_length;
+else @+cur_area=cur_file_name,i=area_delimiter+1;
+if (ext_delimiter==0) ext_delimiter=cur_file_name_length;
+cur_ext=cur_file_name+ext_delimiter;
+for (;i<ext_delimiter;i++) append_char(cur_file_name[i]);
+if (area_delimiter!=0) cur_file_name[area_delimiter+1]=0;
+cur_name=make_string();
+}
+
+static void set_cur_area_ext(str_number a, str_number e)
+{ int i;
+ if (length(a)+length(e)+2>=MAX_CUR_FILE_NAME)
+ { overflow("file name length", MAX_CUR_FILE_NAME);
+ return;
+ }
+ cur_file_name_length=0;
+ area_delimiter=0;
+ for(i=str_start[a]; i<str_start[a+1];i++) cur_file_name[cur_file_name_length++]=str_pool[i];
+ cur_file_name[cur_file_name_length++]=0;
+ cur_area=cur_file_name;
+ cur_ext=cur_file_name+cur_file_name_length;
+ for(i=str_start[e]; i<str_start[e+1];i++) cur_file_name[cur_file_name_length++]=str_pool[i];
+ cur_file_name[cur_file_name_length++]=0;
+}
+
+@ 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, char *@!a, char *@!e)
+{@+print(a);slow_print(n);print(e);
+}
+
+static void printn_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 void pack_file_name(str_number @!n, char *@!a, char *@!e)
+{@+int k; /*number of positions filled in |name_of_file|*/
+ASCII_code @!c; /*character being packed*/
+int @!j; /*index into |str_pool|*/
+k=0;
+while(*a!=0) append_to_name(so(*a++));
+for (j=str_start[n]; j<=str_start[n+1]-1; j++) append_to_name(so(str_pool[j]));
+while(*e!=0) append_to_name(so(*e++));
+if (k <= file_name_size) name_length=k;@+else name_length=file_name_size;
+name_of_file[name_length+1]=0;
+}
+
+
+@ The global variable |TEX_format_default| is no longer needed
+to supply the text for default system areas
+and extensions related to format files.
+
+@ Consequently there is no initialization of |TEX_format_default| either.
+
+@ And there is no need to check the length of |TEX_format_default|.
+
+@ The |format_extension|, however, is needed later on
+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]!=' '|.
+
+k\TeX\ uses the {\tt kpathsearch} library to implement access to files.
+To do so we declare |open_fmt_file| here and postpone the
+actual implementation.
+
+@<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 in the input by calling |get_x_token| for the information.
+
+@p static void scan_file_name(void)
+{@+
+name_in_progress=true;begin_name();
+@<Get the next non-blank non-call...@>;
+loop@+{@+if ((cur_cmd > other_char)||(cur_chr > 255)) /*not a character*/
+ {@+back_input();goto done;
+ }
+ 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?*/
+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 pack_file_name(cur_name, cur_area, cur_ext)
+
+@p void pack_job_name(char *@!s) /*|s==".log"|, |".dvi"|, or
+ |format_extension|*/
+{@+cur_area="";cur_ext=s;
+cur_name=job_name;pack_cur_name;
+}
+
+@ 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. 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 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@>;
+if (cur_ext[0]==0) cur_ext=e;
+pack_cur_name;
+}
+
+@ @<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 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"); /* k\TeX\ */
+@.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(day);print_char(' ');
+for (k=3*month-2; k<=3*month; k++) wlog("%c",months[k]);
+print_char(' ');print_int(year);print_char(' ');
+print_two(time/60);print_char(':');print_two(time%60);
+if (eTeX_ex)
+ {@+;wlog_cr;wlog("entering extended mode");
+ }
+}
+
+@ Let's turn now to the procedure that is used to initiate file reading
+when an `\.{\\input}' command is being processed.
+
+@p static void start_input(void) /*\TeX\ will \.{\\input} something*/
+{@+
+scan_file_name(); /*set |cur_name| to desired file name*/
+if (cur_ext[0]==0) cur_ext=".tex";
+pack_cur_name;
+loop@+{@+begin_file_reading(); /*set up |cur_file| and new level of input*/
+ if (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);@/
+source_filename_stack[in_open]=name; /* k\TeX\ */
+if (job_name==0)
+ {@+if (c_job_name==NULL) job_name=cur_name;
+ else job_name=s_no(c_job_name); open_log_file(); /* k\TeX\ */
+ } /*|open_log_file| doesn't |show_context|, so |limit|
+ and |loc| needn't be set to meaningful values yet*/
+if (term_offset+length(name) > max_print_line-2) print_ln();
+else if ((term_offset > 0)||(file_offset > 0)) print_char(' ');
+print_char('(');incr(open_parens);slow_print(name);update_terminal;
+state=new_line;
+if (name==str_ptr-1) /*we can conserve string pool space now*/
+ {@+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 right boundary character of this font;
+the value of |next_char| need not lie between |bc| and~|ec|.
+If the very last instruction of the |lig_kern| array has |skip_byte==255|,
+there is a special ligature/kerning program for a left boundary character,
+beginning at location |256*op_byte+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...@>=
+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*/
+scaled @!font_size0[font_max-font_base+1], *const @!font_size = @!font_size0-font_base; /*``at'' size*/
+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*/
+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*/
+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?*/
+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;
+ /*right 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...@>=
+int @!char_base0[font_max-font_base+1], *const @!char_base = @!char_base0-font_base;
+ /*base addresses for |char_info|*/
+int @!width_base0[font_max-font_base+1], *const @!width_base = @!width_base0-font_base;
+ /*base addresses for widths*/
+int @!height_base0[font_max-font_base+1], *const @!height_base = @!height_base0-font_base;
+ /*base addresses for heights*/
+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*/
+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, char *@!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; /*right 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,"");
+ 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, "",".tfm"); /* k\TeX\ */
+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]=s_no(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 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*/
+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(float(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(float(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(float(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(float(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 void ship_out(pointer @!p) /*output the box |p|*/
+{@+
+execute_output(p);
+flush_node_list(p);
+}
+
+@ @<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, 0, 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=cur_hfactor=cur_vfactor=0;
+ goto found;
+ }
+scan_normal_dimen;
+found: if (three_codes)
+ {@+saved(0)=s;incr(save_ptr);
+ }
+saved(0)=spec_code;saved(1)=cur_val;
+saved_hfactor(1)=cur_hfactor;
+saved_vfactor(1)=cur_vfactor;
+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...@>=
+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|*/
+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...@>=
+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 extern pointer hpack(pointer p, scaled w, scaled hf, scaled vf, small_number m);
+
+@ @<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: case unset_set_node: case unset_pack_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)=unfloat(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...@>=
+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)=unfloat((-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*/
+extern pointer vpackage(pointer p, scaled h, scaled hf, scaled vf, small_number m, scaled l);
+
+@ @<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: case unset_set_node: case unset_pack_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)=unfloat(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)=unfloat((-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 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) ||@|
+ (type(b)==whatsit_node && subtype(b)==image_node));@/
+ if (prev_depth > ignore_depth && height_known)@/
+ {@+scaled d;@t\1@> /*deficiency of space between baselines*/
+ pointer @!p; /*a new glue node*/
+ {@+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 )@t\2@>@/
+ {@+pointer p;
+ p=new_baseline_node(baseline_skip, line_skip, line_skip_limit);
+ link(tail)= p;tail= p;
+ }
+ link(tail)=b;tail=b;
+ if (height_known)
+ prev_depth=depth(b); /* then also depth is known */
+ else
+ prev_depth=unknown_depth;
+}
+
+@* 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, Ope,
+Clo, Pun, or Inn, 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 Ope*/
+@d close_noad (ord_noad+5) /*|type| of a noad classified Clo*/
+@d punct_noad (ord_noad+6) /*|type| of a noad classified Pun*/
+@d inner_noad (ord_noad+7) /*|type| of a noad classified Inn*/
+@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);
+ }
+ list_ptr(b)=null; flush_node_list(b);
+ 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, 0, 0, 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;
+ }
+ }
+}
+
+@ @<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);
+list_ptr(z)=null; flush_node_list(z);
+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 |2 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)
+ {@+list_ptr(x)=null; flush_node_list(x);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)@/
+ {@+list_ptr(z)=null;@+flush_node_list(z);@+}
+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);
+ list_ptr(z)=null;flush_node_list(z);
+ }
+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: {@+t=inner_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 a $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);@/
+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");
+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));
+}
+
+@ @<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);
+ if (type(u)==hlist_node) w=width(u);
+ else w=max_dimen+1;
+ cur_tail=adjust_tail;adjust_tail=null;
+ }
+else{@+u=vpackage(link(head), natural, 0);
+ if (type(u)==vlist_node) w=height(u);
+ else w=max_dimen+1;
+ }
+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;
+if (type(u)==whatsit_node)
+{ if (subtype(u)==hset_node ||subtype(u)==vset_node) type(u)=unset_set_node;
+ else type(u)=unset_pack_node;
+ span_count(u)=n;
+}
+else if (type(u)==hlist_node ||type(u)==vlist_node)
+{ 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);@/
+extern 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*/
+bool x=false; /* indicates an extende alignment */
+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@>;
+if (x)
+{ @<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;
+
+ type(r)=whatsit_node; subtype(r)=align_node;
+ align_preamble(r)=preamble;
+ align_list(r)=link(head);
+ align_extent(r)=new_xdimen(saved(1),saved_hfactor(1),saved_vfactor(1));
+ align_m(r)= saved(0);
+ align_v(r)= (mode!=-vmode);
+ link(head)=r; tail=r;
+ pack_begin_line=0;
+
+@ 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;
+ if (width(q)>max_dimen) x=true;
+}@+ 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_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));
+ 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));
+ }
+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(float(glue_set(p))*stretch(v));
+@^real multiplication@>
+ }
+else if (glue_sign(p)==shrinking)
+ {@+if (shrink_order(v)==glue_order(p))
+ t=t-round(float(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)=unfloat((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)=unfloat((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)=unfloat((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)=unfloat((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
+\AM\ 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|@>@;
+void line_break(int @!final_widow_penalty)
+{@+
+@<Local variables for line breaking@>@;
+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;
+}
+@#
+@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;
+ 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();
+ }
+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...@>=
+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 continue|@>;
+ @<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 continue| 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*/
+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*/
+scaled @!second_width; /*the width of all lines | > last_special_line|*/
+static scaled @!first_indent; /*left margin to go with |first_width|*/
+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)
+ hyphenate_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));set_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, 0, 0, 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;
+}
+
+@ 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 small_number @!hn; /*the number of positions occupied in |hc|*/
+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*/
+ASCII_code @!cur_lang, @!init_cur_lang; /*current hyphenation table of interest*/
+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.
+
+@p
+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*/
+
+ 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;
+ if (lc_code(c)!=0)
+ if ((lc_code(c)==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);
+ if (lc_code(c)==0) goto done3;
+ if (hn==63) goto done3;
+ hb=s;incr(hn);hu[hn]=c;hc[hn]=lc_code(c);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));
+ if (lc_code(c)==0) goto done3;
+ if (j==63) goto done3;
+ incr(j);hu[j]=c;hc[j]=lc_code(c);@/
+ 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; /*right 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 continue| 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 continue| 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;set_replace_count(r,r_count);set_auto_disc(r);
+ }
+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;
+@<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{@+if (lc_code(cur_chr)==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]=lc_code(cur_chr);
+ }
+ }
+
+@ @<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@>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;
+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_root==0) /*no patterns were given*/
+ {@+for (r=0; r<=256; r++) trie[r]=h;
+ trie_max=256;
+ }
+else{@+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]|?*/
+ASCII_code @!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@>;
+ }
+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@>=
+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*/
+@<Get ready to compress the trie@>;
+if (trie_root!=0)
+ {@+first_fit(trie_root);trie_pack(trie_root);
+ }
+@<Move the data into |trie|@>;
+trie_not_ready=false;
+}
+
+@* 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.
+
+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) /*adjust top after page break*/
+{@+pointer prev_p; /*lags one step behind |p|*/
+pointer @!q; /*temporary variable 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;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 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);
+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);p=list_ptr(v);list_ptr(v)=null;flush_node_list(v);
+if (q!=null) q=vpack(q, natural);
+change_box(q); /*the |eq_level| of the box stays the same*/
+return vpackage(p, h, 0, 0, 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 (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*/
+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...@>=
+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 held-over insertions*/
+
+@ @<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 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...@>=
+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
+void update_last_values(pointer p)
+{@<Update the values of |last_glue|, |last_penalty|, and |last_kern|@>;
+}
+
+@ @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;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 continue|@>@;
+ 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) 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 (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 ((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) @<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, 0, 0, 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));
+ if (ins_ptr(p)!=null)
+ {@+temp_ptr=vpack(ins_ptr(p), natural);
+ height(p)=height(temp_ptr)+depth(temp_ptr);
+ list_ptr(temp_ptr)=null;flush_node_list(temp_ptr);wait=true;
+ }
+ }
+best_ins_ptr(r)=null;
+n=qo(subtype(r));
+temp_ptr=list_ptr(box(n));
+list_ptr(box(n))=null;flush_node_list(box(n));
+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;
+ }
+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;
+ }
+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*/
+if (every_job!=null) begin_token_list(every_job, every_job_text);
+big_switch: get_x_token();@/
+reswitch: @<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 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: @<Append character |cur_chr| and the following characters (if~any) to the
+current hlist in the current font; |goto 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; /*right 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
+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 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;
+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*/
+@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_set_node());
+ set_extent(tail)=new_xdimen(dimen_par(hsize_code),
+ dimen_par_hfactor(hsize_code),dimen_par_vfactor(hsize_code));
+ tail_append(new_glue(fill_glue));
+ tail_append(new_penalty(2*(eject_penalty)));@/
+ 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{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...@>=
+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_hfactor(2), saved_vfactor(2), saved(1));
+else{@+cur_box=vpackage(link(head), saved(2), saved_hfactor(2), saved_vfactor(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...@>=
+void end_graf(void)
+{@+if (mode==hmode)
+ {@+if (head==tail) pop_nest(); /*null paragraphs are ignored*/
+ else hline_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=link(head);pop_nest();
+ if (saved(0) < 255)
+ {@+tail_append(get_node(ins_node_size));
+ type(tail)=ins_node;subtype(tail)=qi(saved(0));
+ height(tail)=0; ins_ptr(tail)=p; hget_stream_no(subtype(tail));
+ 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)=p;delete_glue_ref(q);
+ }
+ if (nest_ptr==0) build_page();
+ } @+break;
+case output_group: @<Resume the page builder...@>@;@+break;
+case page_group: hfinish_page_group();@+break;
+case stream_group: hfinish_stream_group();@+break;
+case stream_before_group: hfinish_stream_before_group();@+break;
+case stream_after_group: hfinish_stream_after_group();@+break;
+case outline_group: hfinish_outline_group();@+break;
+
+@ @<Declare act...@>=
+static void make_mark(void)
+{@+pointer p; /*new node*/
+p=scan_toks(false, true);p=get_node(small_node_size);
+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");
+ 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?*/
+c=cur_chr;scan_register_num();fetch_box(p);
+if (p==null) return;
+if ((abs(mode)==mmode)||@|
+ ((abs(mode)==vmode)&& (type(p)!=vlist_node)
+ && (type(p)!=whatsit_node || (subtype(p)!=vset_node
+ && subtype(p)!=vpack_node))) ||@|
+ ((abs(mode)==hmode)&& (type(p)!=hlist_node)
+ && (type(p)!=whatsit_node || ( subtype(p)!=hset_node
+ && subtype(p)!=hpack_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);
+ list_ptr(p)=null;flush_node_list(p);
+ }
+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 <= 0x7F) set_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 && !(type(tail)==whatsit_node && subtype(tail)==disp_node))
+ { 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;
+ }
+ tail_append(new_param_glue(par_fill_skip_code));
+ }
+push_math(math_shift_group);mode=mmode;
+eq_word_define(int_base+cur_fam_code,-1);@/
+if (every_display!=null) begin_token_list(every_display, every_display_text);
+}
+
+@ @<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_hfactor(1), saved_vfactor(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*/
+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); link(temp_head)=null;@/
+{@+ pointer q;
+ q=new_disp_node();
+ if (!danger) { display_formula(q)=p; display_eqno(q)=a; display_left(q)=l; }
+ /* adding parameter nodes */
+ if (hang_indent!=0)
+ { add_par_node(dimen_type,hang_indent_code,hang_indent);
+ if (hang_after!=1)
+ add_par_node(int_type,hang_after_code,hang_after);
+ }
+ add_par_node(dimen_type,line_skip_limit_code,line_skip_limit);
+ add_par_node(glue_type,line_skip_code,line_skip);
+ add_par_node(glue_type,baseline_skip_code,baseline_skip);
+ display_params(q)=link(temp_head); link(temp_head)=null;
+ display_no_bs(q)= prev_depth <= ignore_depth;
+ tail_append(q);
+}
+/* this is from |resume_after_display| */
+if (cur_group!=math_shift_group) confusion("display");
+@:this can't happen display}{\quad display@>
+unsave();
+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@>;
+
+@ @<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)))
+ {@+list_ptr(b)=null;flush_node_list(b);
+ b=hpack(p, z-q, 0, 0, exactly);
+ }
+else{@+e=0;
+ if (w > z)
+ {@+list_ptr(b)=null;flush_node_list(b);
+ b=hpack(p, z, 0, 0, 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");
+
+@ 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: {@+if (odd(cur_chr)&&!global&&(global_defs >= 0)) a=a+4;
+ e=(cur_chr >= 2);get_r_token();p=cur_cs;
+ 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);
+ } @+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: {@+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+'}';
+ q=get_avail();info(q)=left_brace_token+'{';
+ link(q)=link(def_ref);link(def_ref)=q;
+ }
+ sa_define(p, def_ref, p, call, def_ref);
+ }
+ } @+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;
+ if (!e && l>=dimen_base)
+ { cur_hfactor+=hfactor_eqtb[l].sc;
+ cur_vfactor+=vfactor_eqtb[l].sc;
+ }
+ }
+ }
+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{@+
+ scaled fh=0, fv=0;
+ 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;
+ if (j==1) {fh=cur_hfactor; fv=cur_vfactor;}
+ mem[p+2*j].sc=cur_val; /*width*/
+ }
+ cur_hfactor=fh; cur_vfactor=fv;
+ }
+ 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 (strn_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();
+ if (cur_ext[0]==0) cur_ext=".tex";
+ pack_cur_name;
+ if (a_open_in(&read_file[n])) read_open[n]=just_open;
+ }
+}
+
+@ 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 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);
+@!@:show\_lists\_}{\.{\\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: 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: {@+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...@>=
+{@+p=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;
+
+@ @<Set init...@>=
+format_ident=0;
+
+@ @<Initialize table entries...@>=
+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@>;
+eqtb[dimen_base+hsize_code].i=hhsize;
+eqtb[dimen_base+vsize_code].i=hvsize;
+@<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 a couple more things and the closing check word@>;
+@<Close the format file@>;
+eqtb[dimen_base+hsize_code].i=0;
+eqtb[dimen_base+vsize_code].i=0;
+}
+#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 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|.
+
+@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_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_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]);
+x=x+mem_end+1-hi_mem_min;
+p=avail;
+while (p!=null)
+ {@+decr(dyn_used);p=link(p);
+ }
+dump_int(var_used);dump_int(dyn_used);
+print_ln();print_int(x);
+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 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 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('=');
+printn_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);
+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
+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);
+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); /* k\TeX\ */
+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)
+{ 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!*/
+hhsize=hsize; hvsize=vsize;@/
+hint_open(); main_control(); hint_close(); /*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@>
+
+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@>;
+#ifdef @!STAT
+if (tracing_stats > 0) @<Output statistics about this job@>;@;
+#endif
+wake_up_terminal;
+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 (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 (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, 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 (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;
+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 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|*/ @#
+
+@d par_node 6 /*|subtype| that records the change of a parameter*/
+@d par_node_size 3 /* number of memory words in a |par_node| */
+@d par_type(A) type(A+1) /* type of parameter */
+@d int_type 0 /* type of an |int_par| node */
+@d dimen_type 1 /* type of an |dimen_par| node */
+@d glue_type 2 /* type of an |glue_par| node */
+@d par_number(A) subtype(A+1) /* the parameter number */
+@d par_value(A) mem[A+2] /* the parameter value */@#
+
+@d graf_node 7 /*|subtype| that records a paragraph*/
+@d graf_node_size 5 /* number of memory words in a |graf_node| */
+@d graf_penalty(A) mem[A+1].i /* the final penalty */
+@d graf_extent(A) link(A+3) /* the extent */@#
+@d graf_params(A) info(A+4) /* list of parameter nodes */
+@d graf_list(A) link(A+4) /* list of content nodes */
+
+@d disp_node 8 /*|subtype| that records a math display*/
+@d disp_node_size 3 /* number of memory words in a |disp_node| */
+@d display_left(A) type(A+1) /* 1=left 0=right */
+@d display_no_bs(A) subtype(A+1) /* |prev_depth==ignore_depth| */
+@d display_params(A) link(A+1) /* list of parameter nodes */
+@d display_formula(A) link(A+2) /* formula list */
+@d display_eqno(A) info(A+2) /* box with equation number */@#
+
+@d baseline_node 9 /*|subtype| that records a |baseline_skip| */
+@d baseline_node_size small_node_size /* This is 2; we will convert baseline nodes to glue nodes */
+@d baseline_node_no(A) mem[A+1].i /* baseline reference */@#
+
+@d image_node 10 /*|subtype| that records an image */
+@d image_node_size 9 /* width, depth, height, |shift_amount| like a hbox */
+@d image_width(A) width(A) /*width of image */
+@d image_height(A) height(A) /*height of image */
+@d image_depth(A) depth(A) /* depth of image==zero */
+@d image_no(A) mem[A+4].i /* the section number */
+@d image_stretch(A) mem[A+5].sc /*stretchability of image */
+@d image_shrink(A) mem[A+6].sc /*shrinkability of image */
+@d image_stretch_order(A) stretch_order(A+7)
+@d image_shrink_order(A) shrink_order(A+7)
+@d image_name(A) link(A+7) /*string number of file name */
+@d image_area(A) info(A+8) /*string number of file area */
+@d image_ext(A) link(A+8) /*string number of file extension */@#
+
+@d hpack_node 12 /* a hlist that needs to go to hpack */
+@d vpack_node 13 /* a vlist that needs to go to vpackage */
+@d pack_node_size box_node_size /* a box node up to |list_ptr|*/
+@d pack_m(A) type(A+list_offset) /* either additional or exactly */
+@d pack_limit(A) mem[(A)+1+list_offset].sc /* depth limit in |vpack| */
+@d pack_extent(A) link(A+2+list_offset) /* extent */@#
+
+@d hset_node 14 /* represents a hlist that needs |glue_set| */
+@d vset_node 15 /* represents a vlist that needs |glue_set| */
+@d set_node_size box_node_size /* up to |list_ptr| like a box node */
+@d set_stretch_order glue_sign
+@d set_shrink_order glue_order
+@d set_stretch(A) mem[(A)+1+list_offset].sc /* replaces |glue_set| */
+@d set_extent(A) pack_extent(A) /* extent */
+@d set_shrink(A) mem[(A)+3+list_offset].sc @#
+
+@d align_node 16 /* represents an alignment */
+@d align_node_size 4
+@d align_extent(A) link(A+2) /* the extent of the alignment */
+@d align_m(A) type(A+2) /* either additional or exactly */
+@d align_v(A) subtype(A+2) /* true if vertical */
+@d align_preamble(A) info(A+3) /* the preamble */
+@d align_list(A) link(A+3) /* the unset rows/columns */
+
+@d setpage_node 17 /* represents a page template */
+@d setpage_node_size 6
+@d setpage_name(A) link(A+1)
+@d setpage_number(A) type(A+1) /* the \HINT/ number */
+@d setpage_id(A) subtype(A+1) /* the \TeX\ number */
+@d setpage_priority(A) info(A+2)
+@d setpage_topskip(A) link(A+2)
+@d setpage_depth(A) mem[A+3].sc /* maximum depth */
+@d setpage_height(A) info(A+4) /* extended dimension number */
+@d setpage_width(A) link(A+4) /* extended dimension number */
+@d setpage_list(A) info(A+5) /* the template itself */
+@d setpage_streams(A) link(A+5) /* list of stream definitions */
+
+@d setstream_node 18 /* represents a stream definition */
+@d setstream_node_size 6
+@d setstream_number(A) type(A+1)
+@d setstream_insertion(A) subtype(A+1)
+@d setstream_mag(A) link(A+1) /* magnification factor */
+@d setstream_prefered(A) type(A+2)
+@d setstream_next(A) subtype(A+2)
+@d setstream_ratio(A) link(A+2) /* split ratio */
+@d setstream_max(A) info(A+3) /* extended dimension number */
+@d setstream_width(A) link(A+3) /* extended dimension number */
+@d setstream_topskip(A) info(A+4)
+@d setstream_height(A) link(A+4)
+@d setstream_before(A) info(A+5)
+@d setstream_after(A) link(A+5)
+
+@d stream_node 19 /* represents a stream insertion point */
+@d stream_node_size 2
+@d stream_number(A) type(A+1)
+@d stream_insertion(A) subtype(A+1)
+
+@d stream_after_node 20 /* never allocated */
+@d stream_before_node 21 /* never allocated */
+
+@d xdimen_node 22
+@d xdimen_node_size 4
+@d xdimen_ref_count(A) link(A)
+@d xdimen_width(A) mem[A+1].sc
+@d xdimen_hfactor(A) mem[A+2].sc
+@d xdimen_vfactor(A) mem[A+3].sc
+
+@d ignore_node 23 /* ignored used to attach extra information */
+@d ignore_node_size small_node_size /* same as |disc_node| */
+@d ignore_info(A) type(A+1)
+@d ignore_list(A) link(A+1)
+
+@d label_node 24 /* represents a link to a another location */
+@d label_node_size 2
+@d label_has_name(A) type(A+1) /* 1 for a name , 0 for a number */
+@d label_where(A) subtype(A+1) /* 1 for top, 2 for bot, 3 for mid */
+@d label_ptr(A) link(A+1) /* for a name the token list or the number */
+@d label_ref(A) link(A+1) /*alternatively the label number */
+
+@d start_link_node 25 /* represents a link to a another location */
+@d end_link_node 26 /* represents a link to a another location */
+@d link_node_size 2 /* second word like a |label_node| */
+
+@d outline_node 27 /* represents an outline item */
+@d outline_node_size 4 /* second word like a |label_node| */
+@d outline_ptr(A) link(A+2) /* text to be displayed */
+@d outline_depth(A) mem[A+3].i /* depth of sub items */
+
+
+@ 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 set_language_code 5 /*command modifier for \.{\\setlanguage}*/
+
+@<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@>
+
+primitive("@@HINT", relax, 256); /*cf.\ |relax|*/
+@!@:@@HINT\_}{\.{\\@@HINT} primitive@>
+
+primitive("HINTdest", extension, label_node);@/
+@!@:HINTdest\_}{\.{\\HINTdest} primitive@>
+
+primitive("HINTstartlink", extension, start_link_node);@/
+@!@:startlink\_}{\.{\\HINTstartlink} primitive@>
+
+primitive("HINTendlink", extension, end_link_node);@/
+@!@:HINTendlink\_}{\.{\\HINTendlink} primitive@>
+
+primitive("HINToutline", extension, outline_node);@/
+@!@:HINToutline\_}{\.{\\HINToutline} primitive@>
+
+primitive("image", extension, image_node);@/
+@!@:image\_}{\.{\\image} primitive@>
+
+primitive("setpage", extension, setpage_node);@/
+@!@:setpage\_}{\.{\\setpage} primitive@>
+
+primitive("stream", extension, stream_node);@/
+@!@:stream\_}{\.{\\stream} primitive@>
+
+primitive("setstream", extension, setstream_node);@/
+@!@:setstream\_}{\.{\\setstream} primitive@>
+
+primitive("before", extension, stream_before_node);@/
+@!@:before\_}{\.{\\before} primitive@>
+
+primitive("after", extension, stream_after_node);@/
+@!@:after\_}{\.{\\after} 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 par_node: print("parameter");@+break;
+ case graf_node: print("paragraf");@+break;
+ case disp_node: print("display");@+break;
+ case baseline_node: print("baselineskip");@+break;
+ case hpack_node: print("hpack");@+break;
+ case vpack_node: print("vpack");@+break;
+ case hset_node: print("hset");@+break;
+ case vset_node: print("vset");@+break;
+ case image_node: print("image");@+break;
+ case start_link_node: print("startlink");@+break;
+ case end_link_node: print("endlink");@+break;
+ case label_node: print("dest");@+break;
+ case outline_node: print("outline");@+break;
+ case align_node: print("align");@+break;
+ case setpage_node: print("setpage");@+break;
+ case setstream_node: print("setstream");@+break;
+ case stream_node: print("stream");@+break;
+ case xdimen_node: print("xdimen");@+break;
+ case ignore_node: print("ignore");@+break;
+ case immediate_code: print_esc("immediate");@+break;
+ case set_language_code: print_esc("setlanguage");@+break;
+ 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 par_node:
+case graf_node:
+case disp_node:
+case baseline_node:
+case hpack_node:
+case vpack_node:
+case hset_node:
+case vset_node:
+case align_node: @+break;@#
+case image_node:@/
+{@+ pointer p;
+ scan_optional_equals();
+ scan_file_name();
+ p=new_image_node(cur_name,cur_area,cur_ext);
+ loop {
+ if (scan_keyword("width"))
+ {@+scan_normal_dimen;@+image_width(p)=cur_val;}
+ else if (scan_keyword("height"))
+ {@+scan_normal_dimen;image_height(p)=cur_val; }
+ else if (scan_keyword("plus"))
+ { scan_dimen(false, true, false);
+ image_stretch(p)=cur_val;image_stretch_order(p)=cur_order;
+ }
+ else if (scan_keyword("minus"))
+ { scan_dimen(false, true, false);
+ image_shrink(p)=cur_val;image_shrink_order(p)=cur_order;
+ }
+ else
+ break;
+ }
+ hget_image_information(p);
+ if (abs(mode)==vmode)
+ append_to_vlist(p); /* image nodes have height, width, and depth like boxes */
+ else
+ tail_append(p);
+ break;
+}
+case start_link_node:
+ if (abs(mode) == vmode)
+ fatal_error("HiTeX startlink cannot be used in vertical mode");
+ else
+ { new_whatsit(start_link_node,link_node_size);
+ scan_label(tail);
+ }
+ break;
+case end_link_node:
+ if (abs(mode) == vmode)
+ fatal_error("HiTeX endlink cannot be used in vertical mode");
+ else
+ new_whatsit(end_link_node,link_node_size);
+ break;
+case label_node:
+ new_whatsit(label_node,label_node_size);
+ scan_destination(tail);
+ if (scan_keyword("top")) label_where(tail)=1;
+ else if (scan_keyword("bot")) label_where(tail)=2;
+ else label_where(tail)=3;
+ scan_spaces();
+ break;
+case outline_node:
+ new_whatsit(outline_node,outline_node_size);
+ scan_label(tail);
+ if (scan_keyword("depth"))
+ { scan_int();
+ outline_depth(tail)= cur_val;
+ }
+ else outline_depth(tail)=0;
+ outline_ptr(tail)=null;
+ new_save_level(outline_group);scan_left_brace();
+ push_nest();mode=-hmode;prev_depth=ignore_depth;space_factor=1000;
+ break;
+case setpage_node:
+{ uint8_t n;@+
+ pointer t;
+ scan_eight_bit_int(); n=cur_val;
+ if (n==0)
+ { print_err("Illegal redefinition of page template 0"); print_int(n); error(); break;}
+ scan_optional_equals();
+ scan_file_name(); /* this should be improved to use |scan_name|*/
+ t=new_setpage_node(n,cur_name);
+ loop {
+ if (scan_keyword("priority"))
+ {@+scan_eight_bit_int();setpage_priority(t)=cur_val; }
+ else if (scan_keyword("width"))
+ {@+scan_normal_dimen; setpage_width(t)=new_xdimen(cur_val,cur_hfactor,cur_vfactor); }
+ else if (scan_keyword("height"))
+ {@+scan_normal_dimen; setpage_height(t)=new_xdimen(cur_val,cur_hfactor,cur_vfactor); }
+ else
+ break;
+ }
+ new_save_level(page_group);scan_left_brace();normal_paragraph();
+ push_nest();mode=-vmode;prev_depth=ignore_depth;
+ break;
+}
+case stream_node:
+{ uint8_t n;
+ scan_eight_bit_int(); n=cur_val;
+ new_whatsit(stream_node,stream_node_size);
+ stream_insertion(tail)=n;
+ stream_number(tail)=hget_stream_no(n);
+ break;
+}
+case setstream_node:
+{ uint8_t n;
+ pointer t, s;
+ scan_eight_bit_int(); n=cur_val;
+ scan_optional_equals();
+ t=link(setpage_head);
+ if (t==null) { print_err("\\setstream without \\setpage"); error(); break;}
+ s=new_setstream_node(n);
+ link(s)=setpage_streams(t); setpage_streams(t)=s;
+ loop {
+ if (scan_keyword("prefered"))
+ {@+scan_eight_bit_int();
+ if (cur_val!=255)
+ setstream_prefered(s)=hget_stream_no(cur_val); }
+ else if (scan_keyword("next"))
+ {@+scan_eight_bit_int();
+ if (cur_val!=255)
+ setstream_next(s)=hget_stream_no(cur_val); }
+ else if (scan_keyword("ratio"))
+ {@+scan_int();setstream_ratio(s)=cur_val; }
+ else
+ break;
+ }
+ new_save_level(stream_group);scan_left_brace();normal_paragraph();
+ push_nest();mode=-vmode;prev_depth=ignore_depth;
+ break;
+}
+case stream_before_node:
+ scan_optional_equals();
+ new_save_level(stream_before_group);scan_left_brace();normal_paragraph();
+ push_nest();mode=-vmode;prev_depth=ignore_depth;
+ break;
+case stream_after_node:
+ scan_optional_equals();
+ new_save_level(stream_after_group);scan_left_brace();normal_paragraph();
+ push_nest();mode=-vmode;prev_depth=ignore_depth;
+ break;
+case xdimen_node:
+case ignore_node: @+break;
+case immediate_code: @<Implement \.{\\immediate}@>@;@+break;
+case set_language_code: @<Implement \.{\\setlanguage}@>@;@+break;
+default:confusion("ext1");
+@:this can't happen ext1}{\quad ext1@>
+}
+}
+
+@ @<Declare procedures needed in |do_extension|@>=
+void scan_spaces(void)
+{ @<Get the next non-blank non-call token@>;
+ back_input();
+}
+void scan_destination(pointer p)
+{@+if (scan_keyword("name"))
+ { label_has_name(p)=1;
+ scan_toks(false,true); label_ptr(p)=def_ref;
+ }
+ else if (scan_keyword("num"))
+ { label_has_name(p)=0; scan_int(); label_ptr(p)=cur_val;
+ }
+ else
+ { print_err("`name {...}' or `num 000' expected. Inserted `num 0'.");
+ label_has_name(p)=0; label_ptr(p)=0;
+ error();
+ return;
+ }
+ scan_spaces();
+}
+void scan_label(pointer p)
+{ if (!scan_keyword("goto"))
+ print_err("keyword `goto' inserted");
+ scan_destination(p);
+}
+
+@ 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)=s_no(cur_area);open_ext(tail)=s_no(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}.
+
+@<Implement \.{\\special}@>=
+{@+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_mark(int @!p);
+static void print_label(pointer @!p)
+{ print("goto ");
+ if (label_has_name(p)) { print("name "); print_mark(label_ptr(p));}
+ else { print("num "); print_int(label_ptr(p));}
+}
+
+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('=');printn_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 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;
+case par_node: print_esc("parameter ");
+ print_int(par_type(p));print_char(',');print_int(par_number(p));
+ print_char(':');print_int(par_value(p).i);
+ break;
+case graf_node: print_esc("paragraf(");
+ print_int(graf_penalty(p));
+ print_char(')');
+ node_list_display(graf_params(p));
+ node_list_display(graf_list(p));
+ print_xdimen(graf_extent(p));
+ break;
+case disp_node: print_esc("display ");
+ node_list_display(display_eqno(p));
+ if (display_left(p)) print("left "); else print("right ");
+ node_list_display(display_formula(p));
+ node_list_display(display_params(p));
+ break;
+case baseline_node: print_esc("baselineskip ");
+ print_baseline_skip(baseline_node_no(p));
+ break;
+case hset_node: case vset_node:
+ print_char('\\');
+ print_char(subtype(p)==hset_node?'h':'v');
+ print("set(");
+ print_scaled(height(p)); print_char('+');
+ print_scaled(depth(p)); print(")x"); print_scaled(width(p));
+ if (shift_amount(p)!=0)
+ {@+print(", shifted ");print_scaled(shift_amount(p));
+ }
+ if (set_stretch(p)!=0)
+ {@+print(", stretch ");print_glue(set_stretch(p),set_stretch_order(p),"pt");
+ }
+ if (set_shrink(p)!=0)
+ {@+print(", shrink ");print_glue(set_shrink(p),set_shrink_order(p),"pt");
+ }
+ print(", extent "); print_xdimen(set_extent(p));
+ node_list_display(list_ptr(p)); /*recursive call*/
+ break;
+case hpack_node: case vpack_node:
+ print_char('\\');
+ print_char(subtype(p)==hpack_node?'h':'v');
+ print("pack(");
+ print(pack_m(p)==exactly?"exactly ":"additional ");
+ print_xdimen(pack_extent(p));
+ if (subtype(p)==vpack_node && pack_limit(p)!=max_dimen) { print(", limit "); print_scaled(pack_limit(p)); }
+ print_char(')');
+ node_list_display(list_ptr(p));
+ break;
+case image_node:
+ print_esc("image(");
+ print_char('(');print_scaled(image_height(p));
+ print_char('+'); print_scaled(image_depth(p));
+ print(")x"); print_scaled(image_width(p));
+ if (image_stretch(p)!=0) { print(" plus ");print_glue(image_stretch(p), image_stretch_order(p),"pt"); }
+ if (image_shrink(p)!=0) { print(" minus ");print_glue(image_shrink(p), image_shrink_order(p), "pt"); }
+ print(", section ");print_int(image_no(p));
+ if (image_name(p)!=0) {print(", "); printn(image_name(p));}
+ break;
+case align_node:
+ print_esc("align(");
+ print(align_m(p)==exactly?"exactly ":"additional ");
+ print_xdimen(align_extent(p));print_char(')');
+ node_list_display(align_preamble(p));
+ print_char(':');
+ node_list_display(align_list(p));
+ break;
+case setpage_node:
+ print_esc("setpage");print_int(setpage_number(p));print_char(' '); printn(setpage_name(p));
+ print(" priority ");print_int(setpage_priority(p));
+ print(" width ");print_xdimen(setpage_width(p));
+ print(" height ");print_xdimen(setpage_height(p));
+ print_ln();print_current_string();print(".\\topskip=");print_spec(setpage_topskip(p),0);
+ print_ln();print_current_string();print(".\\maxdepth=");print_scaled(setpage_depth(p));
+ node_list_display(setpage_list(p));
+ node_list_display(setpage_streams(p));
+ break;
+case setstream_node:
+ print_esc("setstream");print_int(setstream_insertion(p));
+ print_char('(');print_int(setstream_number(p));print_char(')');
+ if (setstream_prefered(p)!=255) { print(" prefered ");print_int(setstream_prefered(p)); }
+ if (setstream_ratio(p)>0) { print(" ratio ");print_int(setstream_ratio(p)); }
+ if (setstream_next(p)!=255) { print(" next ");print_int(setstream_next(p)); }
+ append_char('.');
+ print_ln();print_current_string();print_esc("count");print_int(setstream_insertion(p));print_char('=');
+ print_int(setstream_mag(p));
+ print_ln();print_current_string();print_esc("dimen");print_int(setstream_insertion(p));print_char('=');
+ print_xdimen(setstream_max(p));
+ print_ln();print_current_string();print_esc("skip");print_int(setstream_insertion(p));print_char('=');
+ print_spec(setstream_height(p),0);
+ print_ln();print_current_string();print_esc("hsize=");print_xdimen(setstream_width(p));
+ print_ln();print_current_string();print_esc("topskip=");print_spec(setstream_topskip(p),0);
+ if (setstream_before(p)!=null) { print_ln();print_current_string();print_esc("before");node_list_display(setstream_before(p));}
+ if (setstream_after(p)!=null) { print_ln();print_current_string();print_esc("after");node_list_display(setstream_after(p));}
+ flush_char;
+ break;
+case ignore_node:
+ print_esc("ignore ");print_int(ignore_info(p));print_char(':');
+ node_list_display(ignore_list(p));
+ break;
+case start_link_node:
+ print_esc("startlink ");
+ print_label(p);
+ break;
+case end_link_node:
+ print_esc("endlink ");
+ break;
+case label_node:
+ print_esc("dest ");
+ print_label(p);
+ if (label_where(p)==1) print("top");
+ else if (label_where(p)==2) print("bot");
+ else if (label_where(p)==3) print("mid");
+ else print("undefined");
+ break;
+case outline_node:
+ print_esc("outline");
+ print_label(p);
+ print(" depth "); print_int(outline_depth(p));
+ if (outline_ptr(p)==null) print("{}"); else
+ { print_ln();print_current_string();node_list_display(outline_ptr(p));}
+ break;
+case stream_node:
+ print_esc("stream");print_int(stream_insertion(p));
+ print_char('(');print_int(stream_number(p));print_char(')');
+ break;
+case xdimen_node:
+ print_esc("xdimen ");print_xdimen(p);
+ break;
+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: {@+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;
+case par_node:
+{@+r=get_node(par_node_size);
+ if (par_type(p)==glue_type) add_glue_ref(par_value(p).i);
+ words=par_node_size;
+ } @+break;
+case graf_node:
+{@+r=get_node(graf_node_size);
+ add_xdimen_ref(graf_extent(p));
+ graf_params(r)=copy_node_list(graf_params(p));
+ graf_list(r)=copy_node_list(graf_list(p));
+ words=graf_node_size-1;
+ } @+break;
+case disp_node:
+{@+r=get_node(disp_node_size);
+ display_left(r)=display_left(p);
+ display_no_bs(r)=display_no_bs(p);
+ display_eqno(r)=copy_node_list(display_eqno(p));
+ display_formula(r)=copy_node_list(display_formula(p));
+ display_params(r)=copy_node_list(display_params(p));
+ words=disp_node_size-2;
+ } @+break;
+case baseline_node:
+{@+r=get_node(baseline_node_size);
+ words=baseline_node_size;
+ } @+break;
+case hpack_node: case vpack_node:
+{@+r=get_node(pack_node_size);
+ pack_m(r)=pack_m(p);
+ list_ptr(r)=copy_node_list(list_ptr(p));
+ add_xdimen_ref(pack_extent(p));
+ pack_limit(r)=pack_limit(p);
+ words=pack_node_size-3;
+ } @+break;
+case hset_node: case vset_node:
+{@+r=get_node(set_node_size);
+ mem[r+8]=mem[p+8];mem[r+7]=mem[p+7];mem[r+6]=mem[p+6];mem[r+5]=mem[p+5]; /*copy the last four words*/
+ add_xdimen_ref(set_extent(p));
+ list_ptr(r)=copy_node_list(list_ptr(p)); /*this affects |mem[r+5]|*/
+ words=5;
+ } @+break;
+case image_node:
+ r=get_node(image_node_size);
+ words=image_node_size;
+ break;
+case align_node:
+ {@+r=get_node(align_node_size);
+ align_preamble(r)=copy_node_list(align_preamble(p));
+ align_list(r)=copy_node_list(align_list(p));
+ add_xdimen_ref(align_extent(p));
+ words=align_node_size-1;
+ } @+break;
+case setpage_node:
+ {@+r=get_node(setpage_node_size);
+ add_glue_ref(setpage_topskip(p));
+ add_xdimen_ref(setpage_height(p));
+ add_xdimen_ref(setpage_width(p));
+ setpage_list(r)=copy_node_list(setpage_list(p));
+ setpage_streams(r)=copy_node_list(setpage_streams(p));
+ words=setpage_node_size-1;
+ } @+break;
+case setstream_node:
+ {@+r=get_node(setstream_node_size);
+ add_xdimen_ref(setstream_max(p));
+ add_xdimen_ref(setstream_width(p));
+ add_glue_ref(setstream_topskip(p));
+ add_glue_ref(setstream_height(p));
+ setstream_before(r)=copy_node_list(setstream_before(p));
+ setstream_after(r)=copy_node_list(setstream_after(p));
+ words=setstream_node_size-1;
+ } @+break;
+case ignore_node:
+ r=get_node(ignore_node_size);
+ ignore_info(r)=ignore_info(p);
+ ignore_list(r)=copy_node_list(ignore_list(p));
+ words=ignore_node_size-1;
+ break;
+case start_link_node:
+ r=get_node(link_node_size);
+ if (label_has_name(p)) add_token_ref(label_ptr(p));
+ words=link_node_size;
+ break;
+case end_link_node:
+ r=get_node(link_node_size);
+ words=link_node_size;
+ break;
+case label_node:
+ r=get_node(label_node_size);
+ if (label_has_name(p)) add_token_ref(label_ptr(p));
+ words=label_node_size;
+ break;
+case outline_node:
+ r=get_node(outline_node_size);
+ if (label_has_name(p)) add_token_ref(label_ptr(p));
+ outline_ptr(r)=copy_node_list(outline_ptr(p));
+ words=outline_node_size-1;
+ break;
+case stream_node:
+ r=get_node(stream_node_size);
+ words=stream_node_size;
+ break;
+case xdimen_node:
+ r=get_node(xdimen_node_size);
+ words=xdimen_node_size;
+ break;
+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: {@+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;
+case par_node:
+ if (par_type(p)==glue_type) fast_delete_glue_ref(par_value(p).i);
+ free_node(p, par_node_size);@+break;
+case graf_node:
+ delete_xdimen_ref(graf_extent(p));
+ flush_node_list(graf_params(p));
+ flush_node_list(graf_list(p));
+ free_node(p, graf_node_size);@+break;
+case disp_node:
+ flush_node_list(display_eqno(p));
+ flush_node_list(display_formula(p));
+ flush_node_list(display_params(p));
+ free_node(p, disp_node_size);@+break;
+case baseline_node:
+ free_node(p, baseline_node_size);@+break;
+case hpack_node: case vpack_node:
+ delete_xdimen_ref(pack_extent(p));
+ flush_node_list(list_ptr(p));
+ free_node(p, pack_node_size);@+break;
+case hset_node: case vset_node:
+ delete_xdimen_ref(set_extent(p));
+ flush_node_list(list_ptr(p));
+ free_node(p, set_node_size);@+break;
+case image_node:
+ free_node(p,image_node_size);@+break;
+case align_node:
+ delete_xdimen_ref(align_extent(p));
+ flush_node_list(align_preamble(p));
+ flush_node_list(align_list(p));
+ free_node(p, align_node_size);@+break;
+case setpage_node:
+ delete_glue_ref(setpage_topskip(p));
+ delete_xdimen_ref(setpage_height(p));
+ delete_xdimen_ref(setpage_width(p));
+ flush_node_list(setpage_list(p));
+ flush_node_list(setpage_streams(p));
+ free_node(p, setpage_node_size);@+break;
+case setstream_node:
+ delete_xdimen_ref(setstream_max(p));
+ delete_xdimen_ref(setstream_width(p));
+ delete_glue_ref(setstream_topskip(p));
+ delete_glue_ref(setstream_height(p));
+ flush_node_list(setstream_before(p));
+ flush_node_list(setstream_after(p));
+ free_node(p,setstream_node_size); @+break;
+case ignore_node:
+ flush_node_list(ignore_list(p));
+ free_node(p,ignore_node_size); @+break;
+case start_link_node:
+ if (label_has_name(p)) delete_token_ref(label_ptr(p));
+ free_node(p,link_node_size);@+break;
+case end_link_node:
+ free_node(p,link_node_size);@+break;
+case label_node:
+ if (label_has_name(p)) delete_token_ref(label_ptr(p));
+ free_node(p,label_node_size);@+break;
+case outline_node:
+ if (label_has_name(p)) delete_token_ref(label_ptr(p));
+ flush_node_list(outline_ptr(p));
+ free_node(p,outline_node_size);@+break;
+case stream_node:
+ free_node(p,stream_node_size); @+break;
+case xdimen_node:
+ free_node(p,xdimen_node_size);
+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);@+}
+
+@<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)
+{@+int old_setting; /*holds print |selector|*/
+int @!k; /*index into |str_pool|*/
+synch_h;synch_v;@/
+old_setting=selector;selector=new_string;
+show_token_list(link(write_tokens(p)), null, pool_size-pool_ptr);
+selector=old_setting;
+str_room(1);
+if (cur_length < 256)
+ {@+dvi_out(xxx1);dvi_out(cur_length);
+ }
+else{@+dvi_out(xxx4);dvi_four(cur_length);
+ }
+for (k=str_start[str_ptr]; k<=pool_ptr-1; k++) dvi_out(so(str_pool[k]));
+pool_ptr=str_start[str_ptr]; /*erase the string*/
+}
+
+@ 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+'}';@/
+r=get_avail();link(q)=r;info(r)=end_write_token;ins_list(q);@/
+begin_token_list(write_tokens(p), write_text);@/
+q=get_avail();info(q)=left_brace_token+'{';ins_list(q);
+ /*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|@>=
+void out_what(pointer @!p)
+{@+small_number j; /*write stream number*/
+if (subtype(p)==open_node||subtype(p)==write_node||subtype(p)==close_node)
+@<Do some work that has been queued up for \.{\\write}@>@;
+}
+
+@ 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);
+ set_cur_area_ext(open_area(p),open_ext(p));
+ if (cur_ext[0]==0) cur_ext=".tex";
+ pack_cur_name;
+ 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 two 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.
+
+The distinction between these two 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 `$|init|\ldots|tini|$'
+is a run-time switch.
+
+@<Enable \eTeX, if requested@>=
+#ifdef @!INIT
+if (iniversion && (buffer[loc]=='*'||etexp)&&str_eq_str(format_ident," (INITEX)"))
+ {@+no_new_control_sequence=false;
+ @<Generate all \eTeX\ primitives@>@;
+ if (buffer[loc]=='*') incr(loc);
+ eTeX_mode=1; /*enter extended mode*/
+ @<Initialize variables for \eTeX\ extended 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@>
+
+@ @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;
+
+@ 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}' */
+
+@<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|@>=;
+else print_esc("unless")
+
+@ @<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).
+
+
+@ 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
+
+
+
+@ 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));
+}
+
+
+@ 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);
+ }
+}
+
+@* 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
+\def\LaTeX{L\kern-.36em\raise.3ex\hbox{\sc A}\kern-.15em\TeX}%
+\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 k\TeX\ must come before
+the definition of \TeX's macros because
+it uses include files containing identifiers that are in conflict
+with \TeX's macros. The remaining two parts are first auxiliar functions and then
+those functions that are called from the ``classic'' \TeX\ code.
+
+@p @<k\TeX\ auxiliar functions@>@;
+ @<k\TeX\ functions@>@;
+
+@ 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.
+
+@<k\TeX\ auxiliar functions@>=
+static void usage_help(void)
+{@+@<explain the command line@>@;
+ @<explain the options@>@;
+ fprintf(stderr,"\nEmail bug reports to ruckert@@cs.hm.edu.\n");
+ exit(0);
+}
+
+@ The command line commes in three slightly different versions:
+
+@<explain the command line@>=
+ fprintf(stderr,@/
+ "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(stderr,@/
+ " 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");
+
+@ Here is the list of possible options and their explanation:
+
+@<explain the options@>=
+ fprintf(stderr,
+ "Options:\n"@/
+ " -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"@/
+ " -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/pk)\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"@/
+ " [-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"@/
+ " -compress "@/
+ @t\qquad@>"\t enable compression of section 1 and 2\n"@/
+ " [-no]-empty-page "@/
+ @t\qquad@>"\t Disable/Enable empty pages\n"@/
+ " -hyphenate-first-word "@/
+ @t\qquad@>"\t hyphenate the first word of a paragraph\n"@/
+ " -resolution=NUMBER "@/
+ @t\qquad@>"\t set the resolution to NUMBER dpi\n"@/
+ " -mfmode=MODE "@/
+ @t\qquad@>"\t set the METAFONT mode to MODE\n"@/
+#ifdef DEBUG
+ " -debug=FLAGS "@/
+ @t\qquad@>"\t set flags to controll hint debug output\n"@/
+ " -hint-debug-help "@/
+ @t\qquad@>"\t give help on hint debugging\n"@/
+#endif
+);
+
+
+@ 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,
+then the (optional) address of a flag variable that gets set
+by this option, and finally the value to store in the flag variable.
+
+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---except
+for the the |interaction| variable of \TeX.
+Flag variables are initialized with |-1| to indicate an undefined value;
+string variables are initialized with |NULL|.
+
+@<Global...@>=
+static int iniversion=0;
+static int etexp=0;
+static int parsefirstlinep=-1;
+static int filelineerrorstylep=-1;
+static const char *user_progname=NULL, *output_directory=NULL, *c_job_name=NULL;
+static char *dump_name=NULL;@#
+int option_no_empty_page=true, option_hyphen_first=false;
+int option_dpi=600;
+const char *option_mfmode="ljfour", *option_dpi_str="600";
+extern int option_compress;
+extern unsigned int debugflags;
+
+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 },@/
+ { "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 },@/
+ { "compress", 0, &option_compress, 1 },@/
+ { "no-empty-page", 0, &option_no_empty_page, 1 },@/
+ { "empty-page", 0, &option_no_empty_page, 0 },@/
+ { "hyphenate-first-word", 0, &option_hyphen_first, 1 },@/
+ { "resolution", 1, 0, 0 },@/
+ { "mfmode", 1, 0, 0 },@/
+#ifdef DEBUG
+ { "debug", 1, 0, 0 },@/
+ { "hint-debug-help", 0, 0, 0 },@/
+#endif
+ { 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.
+@<k\TeX\ 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 option at |option_index|@>@;@+ }
+ else if (g == -1) return;
+ }
+}
+
+@ 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 auxiliar 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 option at |option_index|@>=
+if (ARGUMENT_IS("help")) usage_help();
+else if (ARGUMENT_IS("version")){@+
+ printf("Version 0.0\n");
+ exit(0);@+
+}
+
+
+@ The ``interaction'' option sets \TeX's |interaction| variable
+based on its string argument contained in the |optarg| variable.
+
+@<handle the option at |option_index|@>=
+else @+if (ARGUMENT_IS ("interaction"))@t\2@> {
+ if (STREQ (optarg, "batchmode")) interaction = batch_mode;
+ else if (STREQ (optarg, "nonstopmode")) interaction = nonstop_mode;
+ else if (STREQ (optarg, "scrollmode")) interaction = scroll_mode;
+ else if (STREQ (optarg, "errorstopmode")) interaction = 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 option at |option_index|@>=
+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 otion can be used.
+The argument value 3 is a good choice to start with.
+
+@<handle the option at |option_index|@>=
+else @+if (ARGUMENT_IS ("kpathsea-debug")) @t\2@>
+ kpathsea_debug |= atoi (optarg);
+
+
+@ The remaining options either set a flag and are handled by
+the |getopt_long_only| function or they take a string argument
+and assign it to the corresponding string variable.
+
+@<handle the option at |option_index|@>=
+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");
+else @+if (ARGUMENT_IS ("cnf-line"))
+ add_cnf_line(optarg);@t\2@>
+else if (ARGUMENT_IS("resolution")) @t\2@>
+{ option_dpi_str=optarg;
+ option_dpi=strtol(option_dpi_str,NULL,10);
+}
+else if (ARGUMENT_IS("mfmode"))
+ option_mfmode=optarg;
+#ifdef DEBUG@t\1@>
+else @+if (ARGUMENT_IS("debug"))
+ debugflags=strtol(optarg,NULL,16);
+else @+if (ARGUMENT_IS("hint-debug-help"))
+ hint_debug_help();
+#endif
+
+@ When string arguments specify files or directories,
+special care is needed if arguments are quoted and/or contain spaces.
+The function |normalize_quotes| makes shure that arguments containing
+spaces get quotes around them and it checks for unballanced quotes.
+
+
+@<k\TeX\ auxiliar 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;
+}
+
+@ 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...@>=
+char **cnf_lines=NULL;
+int cnf_count=0;
+
+@ The function |add_cnf_line| stores the
+given command line argument in the variable |cnf_lines|.
+
+@<k\TeX\ auxiliar 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 0 /* this function does not exists allways */
+{@+ int i;
+ for (i=0;i<cnf_count;i++)
+ kpathsea_cnf_line_env_progname (kpse_def, cnf_lines[i]);
+ free(cnf_lines);
+}
+#endif
+
+@ 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 behaviour 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", "hitex");@/
+
+
+@ 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,
+and we will have the first opportunity to use the \.{kpathsearch} library.
+
+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 afterwards.
+
+@<k\TeX\ auxiliar 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 (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:
+@<k\TeX\ auxiliar 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;
+we postpone k\TeX\'s |full_source_filename_stack| to a later time.
+
+@<Global...@>=
+int @!source_filename_stack0[max_in_open], *const @!source_filename_stack = @!source_filename_stack0-1;
+
+@ The function |print_file_line|
+prints ``file:line:error'' style messages. It looks for a
+filename in |source_filename_stack|, and if it fails to find
+one falls back on the ``non-file:line:error'' style.
+
+@<Basic printing...@>=
+static void print_file_line(void)
+{@+int level=in_open;
+ while (level>0 && source_filename_stack[level]==0) level--;
+ if (level==0) print_nl("! ");
+ else
+ { print_nl(""); printn(source_filename_stack[level]); print_char(':');
+ if (level==in_open) print_int(line);
+ else print_int(line_stack[level]);
+ print(": ");
+ }
+}
+
+@ Most of the time \TeX\ is not running as \.{initex} or \.{virtex},
+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 or virtex program.
+
+If we still don't have a format,
+we use a plain format if running as a virtex, otherwise 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, "hinitex")==0) iniversion = true;
+else if (strcmp(kpse_program_name, "hvirtex")==0 && !dump_name)
+ dump_name = "hitex";
+if (!dump_name)
+ dump_name = kpse_program_name;
+if (!dump_name)
+{ fprintf(stderr,"Unable to determine format name\n");
+ exit(1);
+}
+if (etexp && !iniversion)
+{ fprintf(stderr,"-etex requires -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.
+
+@<k\TeX\ 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, ".fmt");
+ 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);
+ }
+}
+
+
+@ 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);@/
+ kpse_set_program_enabled (kpse_pk_format, MAKE_TEX_PK_BY_DEFAULT,
+ kpse_src_compile);@/
+ xputenv("MAKETEX_BASE_DPI", option_dpi_str);
+ xputenv("MAKETEX_MODE", option_mfmode);
+
+
+@ 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 behaviour
+of a program using command line arguments---especially when writing
+scripts---that k\TeX\ 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.
+
+@<k\TeX\ auxiliar 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);
+@+ }
+
+
+
+@ 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|.
+
+@<k\TeX\ functions@>=
+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)
+ return fopen(file_name,file_mode);
+ 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 (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@>
+
+@<k\TeX\ auxiliar functions@>=
+static void update_name_of_file(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 pathes for
+different types of files and therefore different functions are needed to
+open these files. The common code is in the function |open_in|.
+
+
+@<k\TeX\ auxiliar 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);
+ free(fname);@+
+ }
+ return f;
+}
+
+@ @<k\TeX\ functions@>=
+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= kpse_open_file((char *)name_of_file+1,kpse_tfm_format);
+ 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|.
+
+
+
+@<k\TeX\ functions@>=
+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;
+}
+
+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;
+}
+
+
+
+@ All the code that we have seen so far commes 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;
+
+@ @<k\TeX\ functions@>=
+static void main_init(int ac, char *av[])
+{@+
+ char* main_input_file;
+ argc = ac;
+ argv = av;
+ interaction = error_stop_mode;@/
+ @<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@>@;
+}
+
+
+@ 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);
+}
+#else
+ parse_options (ac, av);
+#endif
+
+@ Adding prototypes for the functions just defined allows \TeX\
+to call them.
+
+@<Header files and function declarations@>=
+
+static int input_command_line(void);
+static void main_init(int ac, char *av[]);
+
+@ 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| that wraps the call to |time|
+(and |gmtime|) for later use in |fix_date_and_time|.
+
+@<Header files and function declarations@>=
+#include <time.h>
+static struct tm *tl_now(void)
+{@+ time_t t=time(NULL);
+ return gmtime(&t);
+}
+
+
+@* 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.
+
+@ Generating the header file.
+@(htex.h@>=
+@h
+enum {@+@<Constants in the outer block@>@+};
+@<Types in the outer block@>@;
+extern void display_node(pointer p);
+extern void hyphenate_word(void);
+extern memory_word *const hfactor_eqtb, *const vfactor_eqtb;
+extern scaled par_shape_hfactor, par_shape_vfactor;
+extern scaled hhsize, hvsize;
+extern int option_no_empty_page, option_hyphen_first;
+extern int option_dpi;
+extern FILE *open_out(const char *file_name, const char *file_mode);
+extern void update_last_values(pointer p); /* see section~\secref{pagebuilder} */
+extern void print_err(char *s);
+extern int s_no(const char *str);
+extern two_halves *const hash;
+extern scaled *const font_dsize;
+extern scaled *const font_size;
+extern void end_diagnostic(bool @!blank_line);
+extern void show_box(pointer @!p);
+extern void begin_diagnostic(void);
+extern void short_display(int @!p);
+extern int font_in_short_display;
+extern int line;
+extern int pack_begin_line;
+extern void print_scaled(scaled @!s);
+extern pointer new_null_box(void);
+extern pointer new_rule(void);
+extern void print_int(int @!n);
+extern halfword badness(scaled @!t, scaled @!s);
+extern str_number job_name;
+extern pointer adjust_tail;
+extern int *const depth_base;
+extern int *const height_base;
+extern int *const width_base;
+extern int *const char_base;
+extern scaled *const total_shrink;
+extern scaled *const total_stretch;
+extern int last_badness;
+extern int dyn_used;
+extern list_state_record nest[];
+extern void append_to_vlist(pointer @!b);
+extern int nest_ptr;
+extern void freeze_page_specs(small_number @!s);
+extern pointer cur_p;
+extern int r_hyf;
+extern int l_hyf;
+extern ASCII_code cur_lang;
+extern void init_trie(void);
+extern bool trie_not_ready;
+extern void ship_out(pointer @!p);
+extern void scan_left_brace(void);
+extern void normal_paragraph(void);
+extern void new_save_level(group_code @!c);
+extern void begin_token_list(pointer @!p, quarterword @!t);
+extern void push_nest(void);
+extern void pop_nest(void);
+extern int init_r_hyf;
+extern int init_l_hyf;
+extern ASCII_code init_cur_lang;
+extern void delete_glue_ref(pointer @!p);
+extern pointer new_param_glue(small_number @!n);
+extern pointer new_penalty(int @!m);
+extern scaled second_indent;
+extern scaled second_width;
+extern halfword last_special_line;
+extern void line_break(int @!final_widow_penalty);
+extern list_state_record cur_list;
+extern void show_node_list(int @!p);
+extern int depth_threshold;
+extern int breadth_max;
+extern void print_ln(void);
+extern void free_node(pointer @!p, halfword @!s);
+extern void flush_node_list(pointer @!p);
+extern packed_ASCII_code str_pool[];
+extern pool_pointer str_start[];
+extern str_number *const font_name;
+extern pointer new_character(internal_font_number @!f, eight_bits @!c);
+extern int *const hyphen_char;
+extern void delete_token_ref(pointer @!p);
+extern pointer *const cur_mark;
+extern memory_word *const eqtb;
+extern memory_word font_info[];
+extern int *const param_base;
+extern pointer new_spec(pointer @!p);
+extern pointer new_glue(pointer @!q);
+extern unsigned char *const name_of_file;
+extern pointer new_disc(void);
+extern pointer *const font_glue;
+extern pointer copy_node_list(pointer @!p);
+extern pointer get_node(int @!s);
+extern pointer hi_mem_min;
+extern int name_length;
+extern void out_what(pointer @!p);
+extern bool doing_leaders;
+extern bool output_active;
+extern int interaction;
+extern memory_word *const mem;
+extern int page_contents;
+extern scaled page_so_far[];
+extern void end_graf(void);
+extern void unsave(void);
+extern void open_log_file(void);
+extern void print_char(ASCII_code @!s);
+extern void prompt_file_name(char *@!s, char *@!e);
+extern void pack_job_name(char *@!s);
+extern void print_spec(int @!p, char *@!s);
+extern void pack_file_name(str_number @!n, char *@!a, char *@!e);
+extern void print(char *@!s);
+extern void print_nl(char *@!s);
+extern void fatal_error(char *@!s);
+extern void overflow(char *@!s, int @!n);
+extern void confusion(char *@!s);
+extern int main(int argc, char *argv[]);
diff --git a/Build/source/texk/web2c/hitexdir/lex.sed b/Build/source/texk/web2c/hitexdir/lex.sed
new file mode 100644
index 00000000000..51ad4d6467a
--- /dev/null
+++ b/Build/source/texk/web2c/hitexdir/lex.sed
@@ -0,0 +1,5 @@
+/#line [0-9]\+/d
+s/::\(.*\):<\(.*\)> :/\1\t\2/
+s/\/\*/\t\/\*/g
+s/^<\([A-Z]\+\)> {/<\1>{/
+
diff --git a/Build/source/texk/web2c/hitexdir/yacc.sed b/Build/source/texk/web2c/hitexdir/yacc.sed
new file mode 100644
index 00000000000..4d3f6aac0f1
--- /dev/null
+++ b/Build/source/texk/web2c/hitexdir/yacc.sed
@@ -0,0 +1,3 @@
+/#line [0-9]\+/d
+s/$<\([a-z]\)> \([0-9]\)/$<\1>\2/
+
diff --git a/Build/source/utils/configure b/Build/source/utils/configure
index 0ee0702a931..f401f6b1d2c 100755
--- a/Build/source/utils/configure
+++ b/Build/source/utils/configure
@@ -765,6 +765,7 @@ enable_uptex_synctex
enable_euptex
enable_euptex_synctex
enable_aleph
+enable_hitex
enable_pdftex
enable_pdftex_synctex
enable_luatex
@@ -1560,6 +1561,7 @@ Optional Features:
--disable-euptex do not compile and install e-upTeX
--disable-euptex-synctex build e-upTeX without SyncTeX support
--disable-aleph do not compile and install Aleph
+ --enable-hitex compile and install HiTeX
--disable-pdftex do not compile and install pdfTeX
--disable-pdftex-synctex build pdfTeX without SyncTeX support
--disable-luatex do not compile and install LuaTeX
@@ -4560,6 +4562,21 @@ case $enable_aleph in #(
*) :
enable_aleph=yes ;;
esac
+# Check whether --enable-hitex was given.
+if test ${enable_hitex+y}
+then :
+ enableval=$enable_hitex;
+fi
+case $enable_hitex in #(
+ yes | no) :
+ ;; #(
+ *) :
+ enable_hitex=no ;;
+esac
+
+test "x$enable_web2c:$enable_hitex" = xyes:yes && {
+ need_zlib=yes
+}
# Check whether --enable-pdftex was given.
if test ${enable_pdftex+y}
then :