summaryrefslogtreecommitdiff
path: root/Build/source/texk/web2c/texprofdir/texprof.w
diff options
context:
space:
mode:
Diffstat (limited to 'Build/source/texk/web2c/texprofdir/texprof.w')
-rw-r--r--Build/source/texk/web2c/texprofdir/texprof.w33304
1 files changed, 33304 insertions, 0 deletions
diff --git a/Build/source/texk/web2c/texprofdir/texprof.w b/Build/source/texk/web2c/texprofdir/texprof.w
new file mode 100644
index 00000000000..722106017fa
--- /dev/null
+++ b/Build/source/texk/web2c/texprofdir/texprof.w
@@ -0,0 +1,33304 @@
+% TeXprof is copyright (C) 2024 by Martin Ruckert
+% Hochschule Muenchen, Lothstrasse 64, 80336 Muenchen
+% and put under the MIT/X11 license.
+%
+% Prote is copyright (C) 2021 by Thierry Laronde and put under
+% the MIT/X11 license.
+%
+% As TeX and e-TeX are reserved names for the unchanged (except for the
+% necessary implementation of system dependencies) instances of, resp.,
+% Donald E. Knuth's program and the NTS team's e-TeX implementation, the
+% name Prote is reserved for this kerTeX implementation.
+%
+% Hence, this is neither TeX (even if it is compatible at start) nor
+% e-TeX (even if is compatible when entering the first set of
+% extensions).
+
+% Version 0 was released in September 1982 after it passed a variety of tests.
+% Version 1 was released in November 1983 after thorough testing.
+% Version 1.1 fixed ``disappearing font identifiers'' et alia (July 1984).
+% Version 1.2 allowed `0' in response to an error, et alia (October 1984).
+% Version 1.3 made memory allocation more flexible and local (November 1984).
+% Version 1.4 fixed accents right after line breaks, et alia (April 1985).
+% Version 1.5 fixed \the\toks after other expansion in \edefs (August 1985).
+% Version 2.0 (almost identical to 1.5) corresponds to "Volume B" (April 1986).
+% Version 2.1 corrected anomalies in discretionary breaks (January 1987).
+% Version 2.2 corrected "(Please type...)" with null \endlinechar (April 1987).
+% Version 2.3 avoided incomplete page in premature termination (August 1987).
+% Version 2.4 fixed \noaligned rules in indented displays (August 1987).
+% Version 2.5 saved cur_order when expanding tokens (September 1987).
+% Version 2.6 added 10sp slop when shipping leaders (November 1987).
+% Version 2.7 improved rounding of negative-width characters (November 1987).
+% Version 2.8 fixed weird bug if no \patterns are used (December 1987).
+% Version 2.9 made \csname\endcsname's "relax" local (December 1987).
+% Version 2.91 fixed \outer\def\a0{}\a\a bug (April 1988).
+% Version 2.92 fixed \patterns, also file names with complex macros (May 1988).
+% Version 2.93 fixed negative halving in allocator when mem_min<0 (June 1988).
+% Version 2.94 kept open_log_file from calling fatal_error (November 1988).
+% Version 2.95 solved that problem a better way (December 1988).
+% Version 2.96 corrected bug in "Infinite shrinkage" recovery (January 1989).
+% Version 2.97 corrected blunder in creating 2.95 (February 1989).
+% Version 2.98 omitted save_for_after at outer level (March 1989).
+% Version 2.99 caught $$\begingroup\halign..$$ (June 1989).
+% Version 2.991 caught .5\ifdim.6... (June 1989).
+% Version 2.992 introduced major changes for 8-bit extensions (September 1989).
+% Version 2.993 fixed a save_stack synchronization bug et alia (December 1989).
+% Version 3.0 fixed unusual displays; was more \output robust (March 1990).
+% Version 3.1 fixed nullfont, disabled \write{\the\prevgraf} (September 1990).
+% Version 3.14 fixed unprintable font names and corrected typos (March 1991).
+% Version 3.141 more of same; reconstituted ligatures better (March 1992).
+% Version 3.1415 preserved nonexplicit kerns, tidied up (February 1993).
+% Version 3.14159 allowed fontmemsize to change; bulletproofing (March 1995).
+% Version 3.141592 fixed \xleaders, glueset, weird alignments (December 2002).
+% Version 3.1415926 was a general cleanup with minor fixes (February 2008).
+% Version 3.14159265 was similar (January 2014).
+% Version 3.141592653 was similar but more extensive (January 2021).
+
+% A reward of $327.68 will be paid to the first finder of any remaining bug.
+
+% This is the 1.1 version of Prote, developed during August 2021,
+% and corrected during september/october 2021 and amended in august 2023
+% for file primitives behavior matching input behavior.
+%
+% 1.0: adds primitives needed by LaTeX as listed in ltnews31.
+% - 2022-07-21: tiddying formal fix: a spurious line was a left over
+% of a removed paragraph (pointed by Martin Ruckert). Suppressed.
+%
+% 1.1: 2023-08-01: the new file primitives are used in LaTeX expecting
+% the input behavior that ".tex" be appended if no extension. So
+% modified to provide this.
+%
+% History towards 1.0 release:
+% 0.99.4:
+% - typos and style corrections provided by Martin Ruckert for
+% clean translation in Pascal (forward declarations and underscores
+% omitted);
+% - adjustements against e-TeX 2.6 instead of 2.1 (T. Laronde).
+% 0.99.7:
+% - \expanded was using an e-TeX procedure not doing def handling
+% => using scan_toks() now. Reported by Martin Ruckert and
+% test code narrowed down by Phelype Oleinik.
+% 0.99.8:
+% - \expanded again: scan_toks() sets def_ref. But back_list()
+% has to take not the refernce count, but the first token.
+% Passing def_ref, a next invocation of scan_toks() was inserting
+% a missing left brace and then reading pass the end of the token
+% list to find a matching right one. def_ref => link(def_ref).
+% Test code narrowed down by Phelype Oleinik (a great help!).
+% - The API for file related primitives has been changed: no error
+% is reported on failure to find/open and nothing is returned
+% (matches current other implementations).
+% 0.99.9:
+% - Fix typos in Pascal replacement text; xchg_buffer_length=0 =>
+% xchg_buffer_length:=0 (caught by Martin Ruckert).
+% 0.99.10:
+% - KerTeX Public License -> X11/MIT license.
+% 1.0:
+% - Just naming the official release. No change.
+%
+% This work was done by Thierry Laronde and is under the MIT/X11
+% license.
+%
+%
+
+% Here is TeX material that gets inserted after \input webmac
+\def\hang{\hangindent 3em\noindent\ignorespaces}
+\def\hangg#1 {\hang\hbox{#1 }}
+\def\textindent#1{\hangindent2.5em\noindent\hbox to2.5em{\hss#1 }\ignorespaces}
+\font\ninerm=cmr9
+\let\mc=\ninerm % medium caps for names like SAIL
+\def\Prote{{\tenrm P\kern-0.1em R\kern-0.15em\raise.11ex\hbox{o}%
+ \kern-0.22em T\kern-0.05em E}}
+\def\texprof{{\TeX}prof}
+\ifpdftex
+\sanitizecommand{\texprof}{TeXprof}
+\sanitizecommand{\eTeX}{eTeX}
+\sanitizecommand{\Prote}{PRoTE}
+\fi
+\font\tenlogo=logo10
+\def\MP{{\tenlogo META}\-{\tenlogo POST}}
+\def\eTeX{$\varepsilon$-\TeX}
+\font\revrm=xbmc10 % for right-to-left text
+% to generate xbmc10 (i.e., reflected cmbx10) use a file
+% xbmc10.mf containing:
+%+++++++++++++++++++++++++++++++++++++++++++++++++
+% if unknown cmbase: input cmbase fi
+% extra_endchar := extra_endchar &
+% "currentpicture:=currentpicture " &
+% "reflectedabout((.5[l,r],0),(.5[l,r],1));";
+% input cmbx10
+%+++++++++++++++++++++++++++++++++++++++++++++++++
+\ifx\beginL\undefined % this is TeX
+ \def\XeT{X\kern-.125em\lower.5ex\hbox{E}\kern-.1667emT}
+ \def\TeXeT{\TeX-\hbox{\revrm \XeT}} % for TeX-XeT
+ \def\TeXXeT{\TeX-\hbox{\revrm -\XeT}} % for TeX--XeT
+\else
+ \ifx\eTeXversion\undefined % this is \TeXeT
+ \def\TeXeT{\TeX-{\revrm\beginR\TeX\endR}} % for TeX-XeT
+ \def\TeXXeT{\TeX-{\revrm\beginR\TeX-\endR}} % for TeX--XeT
+ \else % this is \eTeX
+ \def\TeXeT{\TeX-{\TeXXeTstate=1\revrm\beginR\TeX\endR}} % for TeX-XeT
+ \def\TeXXeT{\TeX-{\TeXXeTstate=1\revrm\beginR\TeX-\endR}} % for TeX--XeT
+ \fi
+\fi
+\def\PASCAL{Pascal}
+\def\pdfTeX{pdf\TeX}
+\def\ph{\hbox{Pascal-H}}
+\def\pct!{{\char`\%}} % percent sign in ordinary text
+\def\grp{\.{\char'173...\char'175}}
+\font\logo=logo10 % font used for the METAFONT logo
+\def\MF{{\logo META}\-{\logo FONT}}
+\def\<#1>{$\langle#1\rangle$}
+\def\section{\mathhexbox278}
+
+\def\(#1){} % this is used to make section names sort themselves better
+\def\9#1{} % this is used for sort keys in the index via @@:sort key}{entry@@>
+
+\let\@@=\relax % we want to be able to \write a \?
+
+\def\title{\texprof}
+\def\LaTeX{L\kern-.36em\raise.3ex\hbox{\sc A}\kern-.15em\TeX}%
+% system dependent redefinitions of \title should come later
+% and should use:
+% \toks0=\expandafter{\title}
+% \edef\title{...\the\toks0...}
+%\let\maybe=\iffalse % print only changed modules
+\def\topofcontents{\hsize 5.5in
+ \vglue 0pt plus 1fil minus 1.5in
+ \def\@@##1]{\hbox to 1in{\hfil##1.\ }}
+ }
+\def\botofcontents{\vskip 0pt plus 1fil minus 1.5in}
+\pageno=3
+\def\glob{13} % this should be the section number of "<Global...>"
+\def\gglob{20, 26} % this should be the next two sections of "<Global...>"
+
+\def\.#1{\leavevmode\hbox{\tentex % typewriter type for strings
+ \let\\=\BS % backslash in a string
+ \let\'=\RQ % right quote in a string
+ \let\`=\LQ % left quote in a string
+ \let\{=\LB % left brace in a string
+ \let\}=\RB % right brace in a string
+ \let\~=\TL % tilde in a string
+ \let\ =\SP % space in a string
+ \let\_=\UL % underline in a string
+ \let\&=\AM % ampersand in a string
+ \let\^=\CF % circumflex in a string
+ #1\kern.05em}}
+\def\&#1{\leavevmode\hbox{\bf\def\_{\UL}%
+ #1\/\kern.05em}} % boldface type for reserved words
+\def\\#1{\leavevmode\hbox{\it\def\_{\UL}%
+ #1\/\kern.05em}} % italic type for identifiers
+\def\vb#1{{\rm #1}}
+%\def\^{\ifmmode\mathchar"222 \else\char`^ \fi} % pointer or hat
+\def\LQ{{\tt\char'22}} % left quote in a string
+\def\RQ{{\tt\char'23}} % right quote in a string
+\def\UL{{\tt\char`\_}} % underline character in a C identifier
+\def\CF{{\tt\char`^}}
+\def\dotdot{\mathrel{.\,.}} % double dot, used only in math mode
+\setbox\MGbox=\hbox{\kern1pt$\rightarrow$\kern1pt} % C pointer to field
+\def\MG{\copy\MGbox}
+@s dotdot TeX
+@s alpha_file int
+@s byte_file int
+@s word_file int
+@s uint8_t int
+@s int16_t int
+@s uint16_t int
+@s int32_t int
+@s uint32_t int
+@s halfword int
+@s nonnegative_integer int
+@s small_number int
+@s glue_ratio double
+@s Xdimen int
+@s Kern int
+@s Font int
+@s Glue int
+@s Disc int
+@s Lig int
+@s Rule int
+@s List int
+@s Kind int
+@s Info int
+@s kpse_file_format_type int
+@s Stretch int
+@s in TeX
+@s line normal
+@s to do
+
+@* Introduction.
+This is \texprof, an implementation of \TeX\ with an extension providing profiling
+capabilities. To be useful nowadays, the profiling capability is not the only
+extension of Donald Knuth's original implementation of \TeX.
+
+To obtain the present \CEE/ program, Donald Knuth's \PASCAL\ program was
+modified in a first step using most of the \eTeX\ extensions by
+Peter Breitenlohner and Phil Taylor.
+@^Breitenlohner, Peter@>
+@^Taylor, Phil@>
+Then further extensions were added using the \Prote\ change file
+of Thierry Laronde.
+@^Laronde, Thierry@>
+Then the constants defining the size of \TeX's data structures,
+notably the size of the |mem| array, were increased considerable. All these
+changes to the \PASCAL\ program are necessary to run a current \LaTeX\ on the
+resulting \TeX\ engine. The next important step was then to translate this
+literate \PASCAL\ program to a literate \CEE/ program using the
+{\tt web2w} source code translator. The use of the \CEE/ based \.{cweb} system
+instead of the \PASCAL\ based \.{WEB} system greatly simplifies the tool chain
+necessary for producing an executable program and allows true source level
+debugging of the resulting program. The latter is indispensable to carry out
+large scale modifications of the \TeX\ program and is of enormous help even
+for smaller modifications.
+The first modification of the resulting \CEE/ program was then the addition
+of \CEE/ routines from the \TeX\ Live project to enable the use of the
+kpath search library by Karl Berry.
+@^Berry, Karl@>
+The last and final step was the addition of profiling capabilities
+which includes, as the latest addition, the definition of numerous
+primitives that enable the \TeX\ profiler to pretend to be \pdfTeX.
+The code used there reuses parts of the \CEE/ version of \pdfTeX\ by
+H\`an Th\^e\llap{\raise 0.5ex\hbox{\'{}}} Th\`anh.
+@^Th\`anh, H\`an Th\^e\llap{\raise 0.5ex\hbox{\'{}}}@>
+
+All these changes left their traces in the text that follows.
+No attempt was made to streamline the text and rid it from the traces
+of its exciting history.
+So with the next paragraph you can start reading Donald Knuth's
+introduction to his \TeX\ program. Enjoy!
+\medskip
+
+This is \TeX, a document compiler intended to produce typesetting of high
+quality.
+The \PASCAL\ program that follows is the definition of \TeX82, a standard
+@:PASCAL}{\PASCAL@>
+@!@:TeX82}{\TeX82@>
+version of \TeX\ that is designed to be highly portable so that identical output
+will be obtainable on a great variety of computers.
+
+The main purpose of the following program is to explain the algorithms of \TeX\
+as clearly as possible. As a result, the program will not necessarily be very
+efficient when a particular \PASCAL\ compiler has translated it into a
+particular machine language. However, the program has been written so that it
+can be tuned to run efficiently in a wide variety of operating environments
+by making comparatively few changes. Such flexibility is possible because
+the documentation that follows is written in the \.{WEB} language, which is
+at a higher level than \PASCAL; the preprocessing step that converts \.{WEB}
+to \PASCAL\ is able to introduce most of the necessary refinements.
+Semi-automatic translation to other languages is also feasible, because the
+program below does not make extensive use of features that are peculiar to
+\PASCAL.
+
+A large piece of software like \TeX\ has inherent complexity that cannot
+be reduced below a certain level of difficulty, although each individual
+part is fairly simple by itself. The \.{WEB} language is intended to make
+the algorithms as readable as possible, by reflecting the way the
+individual program pieces fit together and by providing the
+cross-references that connect different parts. Detailed comments about
+what is going on, and about why things were done in certain ways, have
+been liberally sprinkled throughout the program. These comments explain
+features of the implementation, but they rarely attempt to explain the
+\TeX\ language itself, since the reader is supposed to be familiar with
+{\sl The \TeX book}.
+@.WEB@>
+@:TeXbook}{\sl The \TeX book@>
+
+@ The present implementation has a long ancestry, beginning in the summer
+of~1977, when Michael~F. Plass and Frank~M. Liang designed and coded
+a prototype
+@^Plass, Michael Frederick@>
+@^Liang, Franklin Mark@>
+@^Knuth, Donald Ervin@>
+based on some specifications that the author (in the following, unless
+specified, ``the author'' refers to D.E.~Knuth) had made in May of that year.
+This original proto\TeX\ included macro definitions and elementary
+manipulations on boxes and glue, but it did not have line-breaking,
+page-breaking, mathematical formulas, alignment routines, error recovery,
+or the present semantic nest; furthermore,
+it used character lists instead of token lists, so that a control sequence
+like \.{\\halign} was represented by a list of seven characters. A
+complete version of \TeX\ was designed and coded by the author in late
+1977 and early 1978; that program, like its prototype, was written in the
+{\mc SAIL} language, for which an excellent debugging system was
+available. Preliminary plans to convert the {\mc SAIL} code into a form
+somewhat like the present ``web'' were developed by Luis Trabb~Pardo and
+@^Trabb Pardo, Luis Isidoro@>
+the author at the beginning of 1979, and a complete implementation was
+created by Ignacio~A. Zabala in 1979 and 1980. The \TeX82 program, which
+@^Zabala Salelles, Ignacio Andr\'es@>
+was written by the author during the latter part of 1981 and the early
+part of 1982, also incorporates ideas from the 1979 implementation of
+@^Guibas, Leonidas Ioannis@>
+@^Sedgewick, Robert@>
+@^Wyatt, Douglas Kirk@>
+\TeX\ in {\mc MESA} that was written by Leonidas Guibas, Robert Sedgewick,
+and Douglas Wyatt at the Xerox Palo Alto Research Center. Several hundred
+refinements were introduced into \TeX82 based on the experiences gained with
+the original implementations, so that essentially every part of the system
+has been substantially improved. After the appearance of ``Version 0'' in
+September 1982, this program benefited greatly from the comments of
+many other people, notably David~R. Fuchs and Howard~W. Trickey.
+A final revision in September 1989 extended the input character set to
+eight-bit codes and introduced the ability to hyphenate words from
+different languages, based on some ideas of Michael~J. Ferguson.
+@^Fuchs, David Raymond@>
+@^Trickey, Howard Wellington@>
+@^Ferguson, Michael John@>
+
+No doubt there still is plenty of room for improvement, but the author
+is firmly committed to keeping \TeX82 ``frozen'' from now on; stability
+and reliability are to be its main virtues.
+
+On the other hand, the \.{WEB} description can be extended without changing
+the core of \TeX82 itself, and the program has been designed so that such
+extensions are not extremely difficult to make.
+The |banner| string defined here should be changed whenever \TeX\
+undergoes any modifications, so that it will be clear which version of
+\TeX\ might be the guilty party when a problem arises.
+@^extensions to \TeX@>
+@^system dependencies@>
+
+This program contains code for various features extending \TeX,
+therefore this program is called `\Prote' and not
+`\TeX'; the official name `\TeX' by itself is reserved
+for software systems that are fully compatible with each other.
+A special test suite called the ``\.{TRIP} test'' is available for
+helping to determine whether a particular implementation deserves to be
+known as `\TeX' [cf.~Stanford Computer Science report CS1027,
+November 1984].
+
+A similar test suite called the ``\.{SELLETTE} test'' is available for
+helping to determine whether a particular implementation deserves to be
+known as `\Prote'.
+
+@d eTeX_version 2 /* \.{\\eTeXversion} */
+@d eTeX_revision ".6" /* \.{\\eTeXrevision} */
+@d eTeX_version_string "-2.6" /*current \eTeX\ version*/
+@#
+@d TeX_banner "This is TeX, Version 3.141592653" /*printed when \TeX\ starts*/
+@#
+@#
+@d TEX ETEX /*change program name into |ETEX|*/
+@#
+@d eTeX_states 1 /*number of \eTeX\ state variables in |eqtb|*/
+@#
+@d Prote_version_string "3.141592653-2.6-1.1.0" /*current \Prote\ version*/
+@d Prote_version 1 /* \.{\\Proteversion} */
+@d Prote_revision ".1.0" /* \.{\\Proterevision} */
+@#
+@d Prote_banner "This is Prote, Version " Prote_version_string
+ /*printed when \Prote\ starts*/
+@#
+@d banner Prote_banner
+
+@ Different \PASCAL s have slightly different conventions, and the present
+@!@:PASCAL H}{\ph@>
+program expresses \TeX\ in terms of the \PASCAL\ that was
+available to the author in 1982. Constructions that apply to
+this particular compiler, which we shall call \ph, should help the
+reader see how to make an appropriate interface for other systems
+if necessary. (\ph\ is Charles Hedrick's modification of a compiler
+@^Hedrick, Charles Locke@>
+for the DECsystem-10 that was originally developed at the University of
+Hamburg; cf.\ {\sl Software---Practice and Experience \bf6} (1976),
+29--42. The \TeX\ program below is intended to be adaptable, without
+extensive changes, to most other versions of \PASCAL, so it does not fully
+use the admirable features of \ph. Indeed, a conscious effort has been
+made here to avoid using several idiosyncratic features of standard
+\PASCAL\ itself, so that most of the code can be translated mechanically
+into other high-level languages. For example, the `\&{with}' and `\\{new}'
+features are not used, nor are pointer types, set types, or enumerated
+scalar types; there are no `\&{var}' parameters, except in the case of files
+--- \eTeX, however, does use `\&{var}' parameters for the |reverse| function;
+there are no tag fields on variant records; there are no assignments
+|double=int|; no procedures are declared local to other procedures.)
+
+The portions of this program that involve system-dependent code, where
+changes might be necessary because of differences between \PASCAL\ compilers
+and/or differences between
+operating systems, can be identified by looking at the sections whose
+numbers are listed under `system dependencies' in the index. Furthermore,
+the index entries for `dirty \PASCAL' list all places where the restrictions
+of \PASCAL\ have not been followed perfectly, for one reason or another.
+@!@^system dependencies@>
+@!@^dirty \PASCAL@>
+
+Incidentally, \PASCAL's standard |round| function can be problematical,
+because it disagrees with the IEEE floating-point standard.
+Many implementors have
+therefore chosen to substitute their own home-grown rounding procedure.
+
+@ The following is an outline of the program, whose
+components will be filled in later, using the conventions of \.{cweb}.
+@.WEB@>
+For example, the portion of the program called `\X\glob:Global
+variables\X' below will be replaced by a sequence of variable declarations
+that starts in $\section\glob$ of this documentation. In this way, we are able
+to define each individual global variable when we are prepared to
+understand what it means; we do not have to define all of the globals at
+once. Cross references in $\section\glob$, where it says ``See also
+sections \gglob, \dots,'' also make it possible to look at the set of
+all global variables, if desired. Similar remarks apply to the other
+portions of the program.
+
+The program starts with inserting header files and occassionaly a function
+must be placed before declaring \TeX's macros, because the function
+uses identifiers that \TeX will declare as macros.
+
+@p @<Header files and function declarations@>@;
+@h
+enum {@<Constants in the outer block@>@;
+ @!empty_string=256 /*the empty string follows after 256 characters*/
+};
+@<Types in the outer block@>@;
+@<Forward declarations@>@;
+@<Global variables@>@;
+@#
+static void initialize(void) /*this procedure gets things started properly*/
+ {@+@<Local variables for initialization@>@;
+ @<Initialize whatever \TeX\ might access@>;
+ } @#
+@<Basic printing procedures@>@;
+@<Error handling procedures@>@;
+
+@ The overall \TeX\ program begins with the heading just shown, after which
+comes a bunch of procedure declarations and function declarations.
+Finally we will get to the main program, which begins with the
+comment `|start_here|'. If you want to skip down to the
+main program now, you can look up `|start_here|' in the index.
+But the author suggests that the best way to understand this program
+is to follow pretty much the order of \TeX's components as they appear in the
+\.{WEB} description you are now reading, since the present ordering is
+intended to combine the advantages of the ``bottom up'' and ``top down''
+approaches to the problem of understanding a somewhat complicated system.
+
+@ There is no need to declare labels in \CEE/.
+
+@ Some of the code below is intended to be used only when diagnosing the
+strange behavior that sometimes occurs when \TeX\ is being installed or
+when system wizards are fooling around with \TeX\ without quite knowing
+what they are doing. Such code will not normally be compiled; it is
+delimited by the codewords `$|@t\#\&{ifdef} \.{DEBUG}@>|\ldots|@t\#\&{endif}@>|$', with apologies
+to people who wish to preserve the purity of English.
+
+Similarly, there is some conditional code delimited by
+`$|@t\#\&{ifdef} \.{STAT}@>|\ldots|@t\#\&{endif}@>|$' that is intended for use when statistics are to be
+kept about \TeX's memory usage. The |@t\#\&{ifdef} \.{STAT}@>| $\ldots$ |@t\#\&{endif}@>| code also
+implements diagnostic information for \.{\\tracingparagraphs},
+\.{\\tracingpages}, and \.{\\tracingrestores}.
+@^debugging@>
+
+@ This program has two important variations: (1) There is a long and slow
+version called \.{INITEX}, which does the extra calculations needed to
+@.INITEX@>
+initialize \TeX's internal tables; and (2)~there is a shorter and faster
+production version, which cuts the initialization to a bare minimum.
+Parts of the program that are needed in (1) but not in (2) are delimited by
+the codewords `$|@t\#\&{ifdef} \.{INIT}@>|\ldots|@t\#\&{endif}@>|$'.
+
+\TeX\ Live has established the common practice
+to select the initialization code at runtime
+using the |iniversion| variable.
+
+@<Initialize whatever...@>=
+@<Set initial values of key variables@>@/
+#ifdef @!INIT
+if (iniversion) /* \TeX\ Live*/
+ {@+@<Initialize table entries (done by \.{INITEX} only)@>@;@+}
+#endif
+
+@ The declaration of the necessary type definitions are
+contained in header files.
+
+
+@<Header files and function declarations@>=
+#include <stdbool.h>
+#include <string.h>
+#include <math.h>
+
+@ Further it is necessary to define some build in primitives of
+\PASCAL\ that are otherwise not available in~\CEE/.
+@:PASCAL H}{\ph@>
+
+@d odd(X) ((X)&1)
+@d chr(X) ((unsigned char)(X))
+@d ord(X) ((unsigned int)(X))
+@d abs(X) ((X)>-(X)?(X):-(X))
+@d round(X) ((int)((X)>=0.0?floor((X)+0.5):ceil((X)-0.5)))
+
+@ The following parameters can be changed at compile time to extend or
+reduce \TeX's capacity. They may have different values in \.{INITEX} and
+in production versions of \TeX.
+@.INITEX@>
+@^system dependencies@>
+
+@<Constants...@>=
+@!mem_max=5000000, /*greatest index in \TeX's internal |mem| array;
+ must be strictly less than |max_halfword|;
+ must be equal to |mem_top| in \.{INITEX}, otherwise | >= mem_top|*/
+@!mem_min=0, /*smallest index in \TeX's internal |mem| array;
+ must be |min_halfword| or more;
+ must be equal to |mem_bot| in \.{INITEX}, otherwise | <= mem_bot|*/
+@!buf_size=2000000, /*maximum number of characters simultaneously present in
+ current lines of open files and in control sequences between
+ \.{\\csname} and \.{\\endcsname}; must not exceed |max_halfword|*/
+@!error_line=79, /*width of context lines on terminal error messages*/
+@!half_error_line=50, /*width of first lines of contexts in terminal
+ error messages; should be between 30 and |error_line-15|*/
+@!max_print_line=79, /*width of longest text lines output; should be at least 60*/
+@!stack_size=5000, /*maximum number of simultaneous input sources*/
+@!max_in_open=15, /*maximum number of input files and error insertions that
+ can be going on simultaneously*/
+@!font_max=255, /*maximum internal font number; must not exceed |max_quarterword|
+ and must be at most |font_base+256|*/
+@!font_mem_size=8000000, /*number of words of |font_info| for all fonts*/
+@!param_size=10000, /*maximum number of simultaneous macro parameters*/
+@!nest_size=500, /*maximum number of semantic levels simultaneously active*/
+@!max_strings=500000, /*maximum number of strings; must not exceed |max_halfword|*/
+@!string_vacancies=90000, /*the minimum number of characters that should be
+ available for the user's control sequences and font names,
+ after \TeX's own error messages are stored*/
+@!pool_size=6250000, /*maximum number of characters in strings, including all
+ error messages and help texts, and the names of all fonts and
+ control sequences; must exceed |string_vacancies| by the total
+ length of \TeX's own strings, which is currently about 23000*/
+@!save_size=100000, /*space for saving values outside of current group; must be
+ at most |max_halfword|*/
+@!trie_size=1000000, /*space for hyphenation patterns; should be larger for
+ \.{INITEX} than it is in production versions of \TeX*/
+@!trie_op_size=35111, /*space for ``opcodes'' in the hyphenation patterns*/
+@!dvi_buf_size=16384, /*size of the output buffer; must be a multiple of 8*/
+@!file_name_size=1024, /*file names shouldn't be longer than this*/
+@!xchg_buffer_size=64, /*must be at least 64*/
+ /*size of |eight_bits| buffer for exchange with system routines*/
+
+@ Like the preceding parameters, the following quantities can be changed
+at compile time to extend or reduce \TeX's capacity. But if they are changed,
+it is necessary to rerun the initialization program \.{INITEX}
+@.INITEX@>
+to generate new tables for the production \TeX\ program.
+One can't simply make helter-skelter changes to the following constants,
+since certain rather complex initialization
+numbers are computed from them. They are defined here using
+\.{WEB} macros, instead of being put into the above |enum| list in order to
+emphasize this distinction.
+
+@d mem_bot 0 /*smallest index in the |mem| array dumped by \.{INITEX};
+ must not be less than |mem_min|*/
+@d mem_top 5000000 /*largest index in the |mem| array dumped by \.{INITEX};
+ must be substantially larger than |mem_bot|
+ and not greater than |mem_max|*/
+@d font_base 0 /*smallest internal font number; must not be less
+ than |min_quarterword|*/
+@d hash_size 45000 /*maximum number of control sequences; it should be at most
+ about |(mem_max-mem_min)/(double)10|*/
+@d hash_prime 35999 /*a prime number equal to about 85\pct! of |hash_size|*/
+@d hyph_size 8191 /*another prime; the number of \.{\\hyphenation} exceptions*/
+@^system dependencies@>
+
+@ In case somebody has inadvertently made bad settings of the ``constants,''
+\TeX\ checks them using a global variable called |bad|.
+
+This is the first of many sections of \TeX\ where global variables are
+defined.
+
+@<Glob...@>=
+static int @!bad; /*is some ``constant'' wrong?*/
+
+@ Later on we will say `\ignorespaces|if (mem_max >= max_halfword) bad=14|',
+or something similar. (We can't do that until |max_halfword| has been defined.)
+
+@<Check the ``constant'' values for consistency@>=
+bad=0;
+if ((half_error_line < 30)||(half_error_line > error_line-15)) bad=1;
+if (max_print_line < 60) bad=2;
+if (dvi_buf_size%8!=0) bad=3;
+if (mem_bot+1100 > mem_top) bad=4;
+if (hash_prime > hash_size) bad=5;
+if (max_in_open >= 128) bad=6;
+if (mem_top < 256+11) bad=7; /*we will want |null_list > 255|*/
+
+@ Labels are given symbolic names by the following definitions, so that
+occasional |goto| statements will be meaningful. We insert the label
+`|end|' just before the `\ignorespaces|} |\unskip' of a procedure in
+which we have used the `|goto end|' statement defined below; the label
+`|restart|' is occasionally used at the very beginning of a procedure; and
+the label `|reswitch|' is occasionally used just prior to a |case|
+statement in which some cases change the conditions and we wish to branch
+to the newly applicable case. Loops that are set up with the |loop|
+construction defined below are commonly exited by going to `|done|' or to
+`|found|' or to `|not_found|', and they are sometimes repeated by going to
+`|resume|'. If two or more parts of a subroutine start differently but
+end up the same, the shared code may be gathered together at
+`|common_ending|'.
+
+Incidentally, this program never declares a label that isn't actually used,
+because some fussy \PASCAL\ compilers will complain about redundant labels.
+
+@ Here are some macros for common programming idioms.
+
+@d incr(A) A=A+1 /*increase a variable by unity*/
+@d decr(A) A=A-1 /*decrease a variable by unity*/
+@d negate(A) A=-A /*change the sign of a variable*/
+@d loop @+while (true) @+ /*repeat over and over until a |goto| happens*/
+@f loop else
+ /*\.{WEB}'s |else| acts like `\ignorespaces|while true do|\unskip'*/
+@d do_nothing /*empty statement*/
+@d empty 0 /*symbolic name for a null constant*/
+
+@* The character set.
+In order to make \TeX\ readily portable to a wide variety of
+computers, all of its input text is converted to an internal eight-bit
+code that includes standard ASCII, the ``American Standard Code for
+Information Interchange.'' This conversion is done immediately when each
+character is read in. Conversely, characters are converted from ASCII to
+the user's external representation just before they are output to a
+text file.
+
+Such an internal code is relevant to users of \TeX\ primarily because it
+governs the positions of characters in the fonts. For example, the
+character `\.A' has ASCII code $65=0101$, and when \TeX\ typesets
+this letter it specifies character number 65 in the current font.
+If that font actually has `\.A' in a different position, \TeX\ doesn't
+know what the real position is; the program that does the actual printing from
+\TeX's device-independent files is responsible for converting from ASCII to
+a particular font encoding.
+@^ASCII code@>
+
+\TeX's internal code also defines the value of constants
+that begin with a reverse apostrophe; and it provides an index to the
+\.{\\catcode}, \.{\\mathcode}, \.{\\uccode}, \.{\\lccode}, and \.{\\delcode}
+tables.
+
+@ Characters of text that have been converted to \TeX's internal form
+are said to be of type |ASCII_code|, which is a subrange of the integers.
+
+@<Types...@>=
+typedef uint8_t ASCII_code; /*eight-bit numbers*/
+
+@ The original \PASCAL\ compiler was designed in the late 60s, when six-bit
+character sets were common, so it did not make provision for lowercase
+letters. Nowadays, of course, we need to deal with both capital and small
+letters in a convenient way, especially in a program for typesetting;
+so the present specification of \TeX\ has been written under the assumption
+that the \PASCAL\ compiler and run-time system permit the use of text files
+with more than 64 distinguishable characters. More precisely, we assume that
+the character set contains at least the letters and symbols associated
+with ASCII codes 040 through 0176; all of these characters are now
+available on most computer terminals.
+
+Since we are dealing with more characters than were present in the first
+\PASCAL\ compilers, we have to decide what to call the associated data
+type. Some \PASCAL s use the original name |unsigned char| for the
+characters in text files, even though there now are more than 64 such
+characters, while other \PASCAL s consider |unsigned char| to be a 64-element
+subrange of a larger data type that has some other name.
+
+In order to accommodate this difference, we shall use the name |text_char|
+to stand for the data type of the characters that are converted to and
+from |ASCII_code| when they are input and output. We shall also assume
+that |text_char| consists of the elements |chr(first_text_char)| through
+|chr(last_text_char)|, inclusive. The following definitions should be
+adjusted if necessary.
+@^system dependencies@>
+
+@s text_char char
+@d text_char unsigned char /*the data type of characters in text files*/
+@d first_text_char 0 /*ordinal number of the smallest element of |text_char|*/
+@d last_text_char 255 /*ordinal number of the largest element of |text_char|*/
+
+@<Local variables for init...@>=
+int @!i;
+
+@ The \TeX\ processor converts between ASCII code and
+the user's external character set by means of arrays |xord| and |xchr|
+that are analogous to \PASCAL's |ord| and |chr| functions.
+
+@<Glob...@>=
+static ASCII_code @!xord[256];
+ /*specifies conversion of input characters*/
+static text_char @!xchr[256];
+ /*specifies conversion of output characters*/
+
+@ Since we are assuming that our \PASCAL\ system is able to read and
+write the visible characters of standard ASCII (although not
+necessarily using the ASCII codes to represent them), the following
+assignment statements initialize the standard part of the |xchr| array
+properly, without needing any system-dependent changes. On the other
+hand, it is possible to implement \TeX\ with less complete character
+sets, and in such cases it will be necessary to change something here.
+@^system dependencies@>
+
+@<Set init...@>=
+xchr[040]=' ';
+xchr[041]='!';
+xchr[042]='"';
+xchr[043]='#';
+xchr[044]='$';
+xchr[045]='%';
+xchr[046]='&';
+xchr[047]='\'';@/
+xchr[050]='(';
+xchr[051]=')';
+xchr[052]='*';
+xchr[053]='+';
+xchr[054]=',';
+xchr[055]='-';
+xchr[056]='.';
+xchr[057]='/';@/
+xchr[060]='0';
+xchr[061]='1';
+xchr[062]='2';
+xchr[063]='3';
+xchr[064]='4';
+xchr[065]='5';
+xchr[066]='6';
+xchr[067]='7';@/
+xchr[070]='8';
+xchr[071]='9';
+xchr[072]=':';
+xchr[073]=';';
+xchr[074]='<';
+xchr[075]='=';
+xchr[076]='>';
+xchr[077]='?';@/
+xchr[0100]='@@';
+xchr[0101]='A';
+xchr[0102]='B';
+xchr[0103]='C';
+xchr[0104]='D';
+xchr[0105]='E';
+xchr[0106]='F';
+xchr[0107]='G';@/
+xchr[0110]='H';
+xchr[0111]='I';
+xchr[0112]='J';
+xchr[0113]='K';
+xchr[0114]='L';
+xchr[0115]='M';
+xchr[0116]='N';
+xchr[0117]='O';@/
+xchr[0120]='P';
+xchr[0121]='Q';
+xchr[0122]='R';
+xchr[0123]='S';
+xchr[0124]='T';
+xchr[0125]='U';
+xchr[0126]='V';
+xchr[0127]='W';@/
+xchr[0130]='X';
+xchr[0131]='Y';
+xchr[0132]='Z';
+xchr[0133]='[';
+xchr[0134]='\\';
+xchr[0135]=']';
+xchr[0136]='^';
+xchr[0137]='_';@/
+xchr[0140]='`';
+xchr[0141]='a';
+xchr[0142]='b';
+xchr[0143]='c';
+xchr[0144]='d';
+xchr[0145]='e';
+xchr[0146]='f';
+xchr[0147]='g';@/
+xchr[0150]='h';
+xchr[0151]='i';
+xchr[0152]='j';
+xchr[0153]='k';
+xchr[0154]='l';
+xchr[0155]='m';
+xchr[0156]='n';
+xchr[0157]='o';@/
+xchr[0160]='p';
+xchr[0161]='q';
+xchr[0162]='r';
+xchr[0163]='s';
+xchr[0164]='t';
+xchr[0165]='u';
+xchr[0166]='v';
+xchr[0167]='w';@/
+xchr[0170]='x';
+xchr[0171]='y';
+xchr[0172]='z';
+xchr[0173]='{';
+xchr[0174]='|';
+xchr[0175]='}';
+xchr[0176]='~';@/
+
+@ Some of the ASCII codes without visible characters have been given symbolic
+names in this program because they are used with a special meaning.
+
+@d null_code 00 /*ASCII code that might disappear*/
+@d carriage_return 015 /*ASCII code used at end of line*/
+@d invalid_code 0177 /*ASCII code that many systems prohibit in text files*/
+
+@ The ASCII code is ``standard'' only to a certain extent, since many
+computer installations have found it advantageous to have ready access
+to more than 94 printing characters. Appendix~C of {\sl The \TeX book\/}
+gives a complete specification of the intended correspondence between
+characters and \TeX's internal representation.
+@:TeXbook}{\sl The \TeX book@>
+
+If \TeX\ is being used
+on a garden-variety \PASCAL\ for which only standard ASCII
+codes will appear in the input and output files, it doesn't really matter
+what codes are specified in |xchr[0 dotdot 037]|, but the safest policy is to
+blank everything out by using the code shown below.
+
+However, other settings of |xchr| will make \TeX\ more friendly on
+computers that have an extended character set, so that users can type things
+like `\.^^Z' instead of `\.{\\ne}'. People with extended character sets can
+assign codes arbitrarily, giving an |xchr| equivalent to whatever
+characters the users of \TeX\ are allowed to have in their input files.
+It is best to make the codes correspond to the intended interpretations as
+shown in Appendix~C whenever possible; but this is not necessary. For
+example, in countries with an alphabet of more than 26 letters, it is
+usually best to map the additional letters into codes less than~040.
+To get the most ``permissive'' character set, change |' '| on the
+right of these assignment statements to |chr(i)|.
+@^character set dependencies@>
+@^system dependencies@>
+
+@<Set init...@>=
+for (i=0; i<=037; i++) xchr[i]=chr(i); /* \TeX\ Live*/
+for (i=0177; i<=0377; i++) xchr[i]=chr(i); /* \TeX\ Live*/
+
+@ The following system-independent code makes the |xord| array contain a
+suitable inverse to the information in |xchr|. Note that if |xchr[i]==xchr[j]|
+where |i < j < 0177|, the value of |xord[xchr[i]]| will turn out to be
+|j| or more; hence, standard ASCII code numbers will be used instead of
+codes below 040 in case there is a coincidence.
+
+@<Set init...@>=
+for (i=first_text_char; i<=last_text_char; i++) xord[chr(i)]=invalid_code;
+for (i=0200; i<=0377; i++) xord[xchr[i]]=i;
+for (i=0; i<=0176; i++) xord[xchr[i]]=i;
+
+@* Input and output.
+The bane of portability is the fact that different operating systems treat
+input and output quite differently, perhaps because computer scientists
+have not given sufficient attention to this problem. People have felt somehow
+that input and output are not part of ``real'' programming. Well, it is true
+that some kinds of programming are more fun than others. With existing
+input/output conventions being so diverse and so messy, the only sources of
+joy in such parts of the code are the rare occasions when one can find a
+way to make the program a little less bad than it might have been. We have
+two choices, either to attack I/O now and get it over with, or to postpone
+I/O until near the end. Neither prospect is very attractive, so let's
+get it over with.
+
+The basic operations we need to do are (1)~inputting and outputting of
+text, to or from a file or the user's terminal; (2)~inputting and
+outputting of eight-bit bytes, to or from a file; (3)~instructing the
+operating system to initiate (``open'') or to terminate (``close'') input or
+output from a specified file; (4)~testing whether the end of an input
+file has been reached.
+
+\TeX\ needs to deal with two kinds of files.
+We shall use the term |alpha_file| for a file that contains textual data,
+and the term |byte_file| for a file that contains eight-bit binary information.
+These two types turn out to be the same on many computers, but
+sometimes there is a significant distinction, so we shall be careful to
+distinguish between them. Standard protocols for transferring
+such files from computer to computer, via high-speed networks, are
+now becoming available to more and more communities of users.
+
+The program actually makes use also of a third kind of file, called a
+|word_file|, when dumping and reloading base information for its own
+initialization. We shall define a word file later; but it will be possible
+for us to specify simple operations on word files before they are defined.
+
+@<Types...@>=
+typedef uint8_t eight_bits; /*unsigned one-byte quantity*/
+typedef struct {@+FILE *f;@+text_char@,d;@+} alpha_file; /*files that contain textual data*/
+typedef struct {@+FILE *f;@+eight_bits@,d;@+} byte_file; /*files that contain binary data*/
+
+@ Most of what we need to do with respect to input and output can be handled
+by the I/O facilities that are standard in \PASCAL, i.e., the routines
+called |get|, |put|, |eof|, and so on. But
+standard \PASCAL\ does not allow file variables to be associated with file
+names that are determined at run time, so it cannot be used to implement
+\TeX; some sort of extension to \PASCAL's ordinary |reset| and |rewrite|
+is crucial for our purposes. We shall assume that |name_of_file| is a variable
+of an appropriate type such that the \PASCAL\ run-time system being used to
+implement \TeX\ can open a file whose external name is specified by
+|name_of_file|.
+@^system dependencies@>
+
+@<Glob...@>=
+static unsigned char @!name_of_file0[file_name_size+1]={0},
+ *const @!name_of_file = @!name_of_file0-1;@;@/
+ /*on some systems this may be a \&{record} variable*/
+static int @!name_length;@/ /*this many characters are actually
+ relevant in |name_of_file| (the rest are blank)*/
+
+@ To open files, \TeX\ used \PASCAL's |reset| function.
+We use the {\tt kpathsearch} library to implement new functions
+in the section on \TeX\ Live Integration.
+Here we give only the function prototypes.
+
+\TeX's file-opening functions do not issue their own
+error messages if something goes wrong. If a file identified by
+|name_of_file| cannot be found,
+or if such a file cannot be opened for some other reason
+(e.g., someone may already be trying to write the same file)
+\TeX's file-opening functions return |false|.
+This allows \TeX\ to undertake appropriate corrective action.
+@^system dependencies@>
+
+@p
+static FILE*open_in(char*filename,kpse_file_format_type t,const char*rwb); /* \TeX\ Live*/
+static bool a_open_in(alpha_file *f); /*open a text file for input*/
+static bool b_open_in(byte_file *f); /*open a binary file for input*/
+static bool w_open_in(word_file *f); /*open a word file for input*/
+static FILE *open_out(const char *file_name, const char *file_mode); /* \TeX\ Live*/
+static bool a_open_out(alpha_file *f); /*open a text file for output*/
+static bool b_open_out(byte_file *f); /*open a binary file for output*/
+#ifdef @!INIT
+static bool w_open_out(word_file *f); /*open a word file for output*/
+#endif
+
+@ Files can be closed with the \ph\ routine `|pascal_close(f)|', which
+@:PASCAL H}{\ph@>
+@^system dependencies@>
+should be used when all input or output with respect to |f| has been completed.
+This makes |f| available to be opened again, if desired; and if |f| was used for
+output, the |pascal_close| operation makes the corresponding external file appear
+on the user's area, ready to be read.
+
+These procedures should not generate error messages if a file is
+being closed before it has been successfully opened.
+
+@p static void a_close(alpha_file *f) /*close a text file*/
+{@+pascal_close((*f));
+}
+@#
+static void b_close(byte_file *f) /*close a binary file*/
+{@+pascal_close((*f));
+}
+@#
+static void w_close(word_file *f) /*close a word file*/
+{@+pascal_close((*f));
+}
+
+@ Binary input and output are done with \PASCAL's ordinary |get| and |put|
+procedures, so we don't have to make any other special arrangements for
+binary~I/O. Text output is also easy to do with standard \PASCAL\ routines.
+The treatment of text input is more difficult, however, because
+of the necessary translation to |ASCII_code| values.
+\TeX's conventions should be efficient, and they should
+blend nicely with the user's operating environment.
+
+@ Input from text files is read one line at a time, using a routine called
+|input_ln|. This function is defined in terms of global variables called
+|buffer|, |first|, and |last| that will be described in detail later; for
+now, it suffices for us to know that |buffer| is an array of |ASCII_code|
+values, and that |first| and |last| are indices into this array
+representing the beginning and ending of a line of text.
+
+@<Glob...@>=
+static ASCII_code @!buffer[buf_size+1]; /*lines of characters being read*/
+static int @!first; /*the first unused position in |buffer|*/
+static int @!last; /*end of the line just input to |buffer|*/
+static int @!max_buf_stack; /*largest index used in |buffer|*/
+
+@ The |input_ln| function brings the next line of input from the specified
+file into available positions of the buffer array and returns the value
+|true|, unless the file has already been entirely read, in which case it
+returns |false| and sets |last=first|. In general, the |ASCII_code|
+numbers that represent the next line of the file are input into
+|buffer[first]|, |buffer[first+1]|, \dots, |buffer[last-1]|; and the
+global variable |last| is set equal to |first| plus the length of the
+line. Trailing blanks are removed from the line; thus, either |last==first|
+(in which case the line was entirely blank) or |buffer[last-1]!=' '|.
+
+An overflow error is given, however, if the normal actions of |input_ln|
+would make |last >= buf_size|; this is done so that other parts of \TeX\
+can safely look at the contents of |buffer[last+1]| without overstepping
+the bounds of the |buffer| array. Upon entry to |input_ln|, the condition
+|first < buf_size| will always hold, so that there is always room for an
+``empty'' line.
+
+The variable |max_buf_stack|, which is used to keep track of how large
+the |buf_size| parameter must be to accommodate the present job, is
+also kept up to date by |input_ln|.
+
+If the |bypass_eoln| parameter is |true|, |input_ln| will do a |get|
+before looking at the first character of the line; this skips over
+an |eoln| that was in |f.d|. The procedure does not do a |get| when it
+reaches the end of the line; therefore it can be used to acquire input
+from the user's terminal as well as from ordinary text files.
+
+Standard \PASCAL\ says that a file should have |eoln| immediately
+before |eof|, but \TeX\ needs only a weaker restriction: If |eof|
+occurs in the middle of a line, the system function |eoln| should return
+a |true| result (even though |f.d| will be undefined).
+
+Since the inner loop of |input_ln| is part of \TeX's ``inner loop''---each
+character of input comes in at this place---it is wise to reduce system
+overhead by making use of special routines that read in an entire array
+of characters at once, if such routines are available. The following
+code uses standard \PASCAL\ to illustrate what needs to be done, but
+finer tuning is often possible at well-developed \PASCAL\ sites.
+@^inner loop@>
+
+@p static bool input_ln(alpha_file *f, bool @!bypass_eoln)
+ /*inputs the next line or returns |false|*/
+{@+int last_nonblank; /*|last| with trailing blanks removed*/
+if (bypass_eoln) if (!eof((*f))) get((*f));
+ /*input the first character of the line into |f.d|*/
+last=first; /*cf.\ Matthew 19\thinspace:\thinspace30*/
+if (eof((*f))) return false;
+else{@+last_nonblank=first;
+ while (!eoln((*f)))
+ {@+if (last >= max_buf_stack)
+ {@+max_buf_stack=last+1;
+ if (max_buf_stack==buf_size)
+ @<Report overflow of the input buffer, and abort@>;
+ }
+ buffer[last]=xord[(*f).d];get((*f));incr(last);
+ if (buffer[last-1]!=' ') last_nonblank=last;
+ }
+ last=last_nonblank;return true;
+ }
+}
+
+@ The user's terminal acts essentially like other files of text, except
+that it is used both for input and for output. When the terminal is
+considered an input file, the file variable is called |term_in|, and when it
+is considered an output file the file variable is |term_out|.
+@^system dependencies@>
+
+@<Glob...@>=
+static alpha_file @!term_in; /*the terminal as an input file*/
+static alpha_file @!term_out; /*the terminal as an output file*/
+
+@ Here is how to open the terminal files
+in \ph. The `\.{/I}' switch suppresses the first |get|.
+@:PASCAL H}{\ph@>
+@^system dependencies@>
+
+@d t_open_in term_in.f=stdin /*open the terminal for text input*/
+@d t_open_out term_out.f=stdout /*open the terminal for text output*/
+
+@ Sometimes it is necessary to synchronize the input/output mixture that
+happens on the user's terminal, and three system-dependent
+procedures are used for this
+purpose. The first of these, |update_terminal|, is called when we want
+to make sure that everything we have output to the terminal so far has
+actually left the computer's internal buffers and been sent.
+The second, |clear_terminal|, is called when we wish to cancel any
+input that the user may have typed ahead (since we are about to
+issue an unexpected error message). The third, |wake_up_terminal|,
+is supposed to revive the terminal if the user has disabled it by
+some instruction to the operating system. The following macros show how
+these operations can be specified in \ph:
+@:PASCAL H}{\ph@>
+@^system dependencies@>
+
+@d update_terminal fflush(term_out.f) /*empty the terminal output buffer*/
+@d clear_terminal fflush(term_in.f) /*clear the terminal input buffer*/
+@d wake_up_terminal do_nothing /*cancel the user's cancellation of output*/
+
+@ We need a special routine to read the first line of \TeX\ input from
+the user's terminal. This line is different because it is read before we
+have opened the transcript file; there is sort of a ``chicken and
+egg'' problem here. If the user types `\.{\\input paper}' on the first
+line, or if some macro invoked by that line does such an \.{\\input},
+the transcript file will be named `\.{paper.log}'; but if no \.{\\input}
+commands are performed during the first line of terminal input, the transcript
+file will acquire its default name `\.{texput.log}'. (The transcript file
+will not contain error messages generated by the first line before the
+first \.{\\input} command.)
+@.texput@>
+
+The first line is even more special if we are lucky enough to have an operating
+system that treats \TeX\ differently from a run-of-the-mill \PASCAL\ object
+program. It's nice to let the user start running a \TeX\ job by typing
+a command line like `\.{tex paper}'; in such a case, \TeX\ will operate
+as if the first line of input were `\.{paper}', i.e., the first line will
+consist of the remainder of the command line, after the part that invoked
+\TeX.
+
+The first line is special also because it may be read before \TeX\ has
+input a format file. In such cases, normal error messages cannot yet
+be given. The following code uses concepts that will be explained later.
+(If the \PASCAL\ compiler does not support non-local |@!goto|\unskip, the
+@^system dependencies@>
+statement `|goto exit(0)|' should be replaced by something that
+quietly terminates the program.)
+
+@<Report overflow of the input buffer, and abort@>=
+if (format_ident==0)
+ {@+write_ln(term_out,"Buffer size exceeded!");exit(0);
+@.Buffer size exceeded@>
+ }
+else{@+cur_input.loc_field=first;cur_input.limit_field=last-1;
+ overflow("buffer size", buf_size);
+@:TeX capacity exceeded buffer size}{\quad buffer size@>
+ }
+
+@ Different systems have different ways to get started. But regardless of
+what conventions are adopted, the routine that initializes the terminal
+should satisfy the following specifications:
+
+\yskip\textindent{1)}It should open file |term_in| for input from the
+ terminal. (The file |term_out| will already be open for output to the
+ terminal.)
+
+\textindent{2)}If the user has given a command line, this line should be
+ considered the first line of terminal input. Otherwise the
+ user should be prompted with `\.{**}', and the first line of input
+ should be whatever is typed in response.
+
+\textindent{3)}The first line of input, which might or might not be a
+ command line, should appear in locations |first| to |last-1| of the
+ |buffer| array.
+
+\textindent{4)}The global variable |loc| should be set so that the
+ character to be read next by \TeX\ is in |buffer[loc]|. This
+ character should not be blank, and we should have |loc < last|.
+
+\yskip\noindent(It may be necessary to prompt the user several times
+before a non-blank line comes in. The prompt is `\.{**}' instead of the
+later `\.*' because the meaning is slightly different: `\.{\\input}' need
+not be typed immediately after~`\.{**}'.)
+
+@d loc cur_input.loc_field /*location of first unread character in |buffer|*/
+
+@ The following routine calls |input_command_line|
+to retrieve a possible command line.
+@^system dependencies@>
+
+@p static bool init_terminal(void) /*gets the terminal input started*/
+{@+
+t_open_in;
+if (input_command_line()) return true; /* \TeX\ Live */
+loop@+{@+wake_up_terminal;pascal_write(term_out,"**");update_terminal;
+@.**@>
+ if (!input_ln(&term_in, true)) /*this shouldn't happen*/
+ {@+write_ln(term_out);
+ pascal_write(term_out,"! End of file on the terminal... why?");
+@.End of file on the terminal@>
+ return false;
+ }
+ loc=first;
+ while ((loc < last)&&(buffer[loc]==' ')) incr(loc);
+ if (loc < last)
+ {@+return true;
+ /*return unless the line was all blank*/
+ }
+ write_ln(term_out,"Please type the name of your input file.");
+ }
+}
+
+@* String handling.
+Control sequence names and diagnostic messages are variable-length strings
+of eight-bit characters. Since \PASCAL\ does not have a well-developed string
+mechanism, \TeX\ does all of its string processing by homegrown methods.
+
+Elaborate facilities for dynamic strings are not needed, so all of the
+necessary operations can be handled with a simple data structure.
+The array |str_pool| contains all of the (eight-bit) ASCII codes in all
+of the strings, and the array |str_start| contains indices of the starting
+points of each string. Strings are referred to by integer numbers, so that
+string number |s| comprises the characters |str_pool[j]| for
+|str_start[s] <= j < str_start[s+1]|. Additional integer variables
+|pool_ptr| and |str_ptr| indicate the number of entries used so far
+in |str_pool| and |str_start|, respectively; locations
+|str_pool[pool_ptr]| and |str_start[str_ptr]| are
+ready for the next string to be allocated.
+
+String numbers 0 to 255 are reserved for strings that correspond to single
+ASCII characters. This is in accordance with the conventions of \.{WEB},
+@.WEB@>
+which converts single-character strings into the ASCII code number of the
+single character involved, while it converts other strings into integers
+and builds a string pool file. Thus, when the string constant \.{"."} appears
+in the program below, \.{WEB} converts it into the integer 46, which is the
+ASCII code for a period, while \.{WEB} will convert a string like \.{"hello"}
+into some integer greater than~255. String number 46 will presumably be the
+single character `\..'; but some ASCII codes have no standard visible
+representation, and \TeX\ sometimes needs to be able to print an arbitrary
+ASCII character, so the first 256 strings are used to specify exactly what
+should be printed for each of the 256 possibilities.
+
+Elements of the |str_pool| array must be ASCII codes that can actually
+be printed; i.e., they must have an |xchr| equivalent in the local
+character set. (This restriction applies only to preloaded strings,
+not to those generated dynamically by the user.)
+
+Some \PASCAL\ compilers won't pack integers into a single byte unless the
+integers lie in the range |-128 dotdot 127|. To accommodate such systems
+we access the string pool only via macros that can easily be redefined.
+@^system dependencies@>
+
+@d si(A) A /*convert from |ASCII_code| to |packed_ASCII_code|*/
+@d so(A) A /*convert from |packed_ASCII_code| to |ASCII_code|*/
+
+@<Types...@>=
+typedef int32_t pool_pointer; /*for variables that point into |str_pool|*/
+typedef int32_t str_number; /*for variables that point into |str_start|*/
+typedef uint8_t packed_ASCII_code; /*elements of |str_pool| array*/
+
+@ @<Glob...@>=
+static packed_ASCII_code @!str_pool[pool_size+1]; /*the characters*/
+static pool_pointer @!str_start[max_strings+1]; /*the starting pointers*/
+static pool_pointer @!pool_ptr; /*first unused position in |str_pool|*/
+static str_number @!str_ptr; /*number of the current string being created*/
+static pool_pointer @!init_pool_ptr; /*the starting value of |pool_ptr|*/
+static str_number @!init_str_ptr; /*the starting value of |str_ptr|*/
+
+@ Several of the elementary string operations are performed using \.{WEB}
+macros instead of \PASCAL\ procedures, because many of the
+operations are done quite frequently and we want to avoid the
+overhead of procedure calls. For example, here is
+a simple macro that computes the length of a string.
+@.WEB@>
+
+@d length(A) (str_start[A+1]-str_start[A]) /*the number of characters
+ in string number \#*/
+
+@ The length of the current string is called |cur_length|:
+
+@d cur_length (pool_ptr-str_start[str_ptr])
+
+@ Strings are created by appending character codes to |str_pool|.
+The |append_char| macro, defined here, does not check to see if the
+value of |pool_ptr| has gotten too high; this test is supposed to be
+made before |append_char| is used. There is also a |flush_char|
+macro, which erases the last character appended.
+
+To test if there is room to append |l| more characters to |str_pool|,
+we shall write |str_room(l)|, which aborts \TeX\ and gives an
+apologetic error message if there isn't enough room.
+
+@d append_char(A) /*put |ASCII_code| \# at the end of |str_pool|*/
+{@+str_pool[pool_ptr]=si(A);incr(pool_ptr);
+}
+@d flush_char decr(pool_ptr) /*forget the last character in the pool*/
+@d str_room(A) /*make sure that the pool hasn't overflowed*/
+ {@+if (pool_ptr+A > pool_size)
+ overflow("pool size", pool_size-init_pool_ptr);
+@:TeX capacity exceeded pool size}{\quad pool size@>
+ }
+
+@ Once a sequence of characters has been appended to |str_pool|, it
+officially becomes a string when the function |make_string| is called.
+This function returns the identification number of the new string as its
+value.
+
+@p static str_number make_string(void) /*current string enters the pool*/
+{@+if (str_ptr==max_strings)
+ overflow("number of strings", max_strings-init_str_ptr);
+@:TeX capacity exceeded number of strings}{\quad number of strings@>
+incr(str_ptr);str_start[str_ptr]=pool_ptr;
+return str_ptr-1;
+}
+
+@ To destroy the most recently made string, we say |flush_string|.
+
+@d flush_string {@+decr(str_ptr);pool_ptr=str_start[str_ptr];
+ }
+
+@ The following subroutine compares string |s| with another string of the
+same length that appears in |buffer| starting at position |k|;
+the result is |true| if and only if the strings are equal.
+Empirical tests indicate that |str_eq_buf| is used in such a way that
+it tends to return |true| about 80 percent of the time.
+
+@p static bool str_eq_buf(str_number @!s, int @!k)
+ /*test equality of strings*/
+{@+ /*loop exit*/
+pool_pointer j; /*running index*/
+bool @!result; /*result of comparison*/
+j=str_start[s];
+while (j < str_start[s+1])
+ {@+if (so(str_pool[j])!=buffer[k])
+ {@+result=false;goto not_found;
+ }
+ incr(j);incr(k);
+ }
+result=true;
+not_found: return result;
+}
+
+@ Here is a similar routine, but it compares two strings in the string pool,
+and it does not assume that they have the same length.
+
+@p static bool str_eq_str(str_number @!s, str_number @!t)
+ /*test equality of strings*/
+{@+ /*loop exit*/
+pool_pointer j, @!k; /*running indices*/
+bool @!result; /*result of comparison*/
+result=false;
+if (length(s)!=length(t)) goto not_found;
+j=str_start[s];k=str_start[t];
+while (j < str_start[s+1])
+ {@+if (str_pool[j]!=str_pool[k]) goto not_found;
+ incr(j);incr(k);
+ }
+result=true;
+not_found: return result;
+}
+@t\4@>@<Declare \Prote\ procedures for strings@>@;
+
+@ The initial values of |str_pool|, |str_start|, |pool_ptr|,
+and |str_ptr| are computed by the \.{INITEX} program, based in part
+on the information that \.{WEB} has output while processing \TeX.
+@.INITEX@>
+@^string pool@>
+
+@p
+static bool get_strings_started(void) /*initializes the string pool*/
+{@+
+int k, @!l; /*small indices or counters*/
+pool_ptr=0;str_ptr=0;str_start[0]=0;
+@<Make the first 256 strings@>;
+@<Add the empty string to the string pool@>;
+return true;
+}
+
+@ @d app_lc_hex(A) l=A;
+ if (l < 10) append_char(l+'0')@;@+else append_char(l-10+'a')
+
+@<Make the first 256...@>=
+for (k=0; k<=255; k++)
+ {@+if ((@<Character |k| cannot be printed@>))
+ {@+append_char('^');append_char('^');
+ if (k < 0100) append_char(k+0100)@;
+ else if (k < 0200) append_char(k-0100)@;
+ else{@+app_lc_hex(k/16);app_lc_hex(k%16);
+ }
+ }
+ else append_char(k);
+ make_string();
+ }
+
+@ The first 128 strings will contain 95 standard ASCII characters, and the
+other 33 characters will be printed in three-symbol form like `\.{\^\^A}'
+unless a system-dependent change is made here. Installations that have
+an extended character set, where for example |xchr[032]==@t\.{\'^^Z\'}@>|,
+would like string 032 to be the single character 032 instead of the
+three characters 0136, 0136, 0132 (\.{\^\^Z}). On the other hand,
+even people with an extended character set will want to represent string
+015 by \.{\^\^M}, since 015 is |carriage_return|; the idea is to
+produce visible strings instead of tabs or line-feeds or carriage-returns
+or bell-rings or characters that are treated anomalously in text files.
+
+Unprintable characters of codes 128--255 are, similarly, rendered
+\.{\^\^80}--\.{\^\^ff}.
+
+The boolean expression defined here should be |true| unless \TeX\
+internal code number~|k| corresponds to a non-troublesome visible
+symbol in the local character set. An appropriate formula for the
+extended character set recommended in {\sl The \TeX book\/} would, for
+example, be `|k in[0, 010 dotdot 012, 014, 015, 033, 0177 dotdot 0377]|'.
+If character |k| cannot be printed, and |k < 0200|, then character |k+0100| or
+|k-0100| must be printable; moreover, ASCII codes |[041 dotdot 046,
+060 dotdot 071, 0136, 0141 dotdot 0146, 0160 dotdot 0171]| must be printable.
+Thus, at least 80 printable characters are needed.
+@:TeXbook}{\sl The \TeX book@>
+@^character set dependencies@>
+@^system dependencies@>
+
+@<Character |k| cannot be printed@>=
+ (k < ' ')||(k > '~')
+
+@ The |pool_file| variable is no longer needed and has been removed.
+
+Instead of reading the other strings from the \.{TEX.POOL} file,
+it is sufficient here to add the empty string.
+@<Add the empty string to the string pool@>=
+make_string();
+
+@ Without a string pool file there is no need for a pool check sum either.
+But this is a convenient place to define the function |s_no| that will
+add literal strings to the string pool at runtime, thereby obtaining their
+string number.
+
+@p static int s_no(const char *str)
+{@+
+ if (str[0]==0) return empty_string;
+ if (str[1]==0) return str[0];
+ str_room(strlen(str));
+ while (*str!=0) append_char(*str++);
+ return make_string();
+}
+
+@ The function |s_no| is used in |initialize| and needs a forward
+declaration.
+
+@<Forward declarations@>=
+static int s_no(const char *str);
+
+@* On-line and off-line printing.
+Messages that are sent to a user's terminal and to the transcript-log file
+are produced by several `|print|' procedures. These procedures will
+direct their output to a variety of places, based on the setting of
+the global variable |selector|, which has the following possible
+values:
+
+\yskip
+\hang |term_and_log|, the normal setting, prints on the terminal and on the
+ transcript file.
+
+\hang |log_only|, prints only on the transcript file.
+
+\hang |term_only|, prints only on the terminal.
+
+\hang |no_print|, doesn't print at all. This is used only in rare cases
+ before the transcript file is open.
+
+\hang |pseudo|, puts output into a cyclic buffer that is used
+ by the |show_context| routine; when we get to that routine we shall discuss
+ the reasoning behind this curious mode.
+
+\hang |new_string|, appends the output to the current string in the
+ string pool.
+
+\hang 0 to 15, prints on one of the sixteen files for \.{\\write} output.
+
+\yskip
+\noindent The symbolic names `|term_and_log|', etc., have been assigned
+numeric codes that satisfy the convenient relations |no_print+1==term_only|,
+|no_print+2==log_only|, |term_only+2==log_only+1==term_and_log|.
+
+Three additional global variables, |tally| and |term_offset| and
+|file_offset|, record the number of characters that have been printed
+since they were most recently cleared to zero. We use |tally| to record
+the length of (possibly very long) stretches of printing; |term_offset|
+and |file_offset|, on the other hand, keep track of how many characters
+have appeared so far on the current line that has been output to the
+terminal or to the transcript file, respectively.
+
+@d no_print 16 /*|selector| setting that makes data disappear*/
+@d term_only 17 /*printing is destined for the terminal only*/
+@d log_only 18 /*printing is destined for the transcript file only*/
+@d term_and_log 19 /*normal |selector| setting*/
+@d pseudo 20 /*special |selector| setting for |show_context|*/
+@d new_string 21 /*printing is deflected to the string pool*/
+@d max_selector 21 /*highest selector setting*/
+
+@<Glob...@>=
+static alpha_file @!log_file; /*transcript of \TeX\ session*/
+static int @!selector; /*where to print a message*/
+static int8_t @!dig[23]; /*digits in a number being output*/
+static int @!tally; /*the number of characters recently printed*/
+static int @!term_offset;
+ /*the number of characters on the current terminal line*/
+static int @!file_offset;
+ /*the number of characters on the current file line*/
+static ASCII_code @!trick_buf[error_line+1]; /*circular buffer for
+ pseudoprinting*/
+static int @!trick_count; /*threshold for pseudoprinting, explained later*/
+static int @!first_count; /*another variable for pseudoprinting*/
+
+@ @<Initialize the output routines@>=
+selector=term_only;tally=0;term_offset=0;file_offset=0;
+
+@ Macro abbreviations for output to the terminal and to the log file are
+defined here for convenience. Some systems need special conventions
+for terminal output, and it is possible to adhere to those conventions
+by changing |wterm|, |wterm_ln|, and |wterm_cr| in this section.
+@^system dependencies@>
+
+@<Basic printing procedures@>=
+#define @[put(F)@] @[fwrite(&((F).d)@],@[sizeof((F).d),1,(F).f)@]@;
+#define @[get(F)@] @[(void)!fread(&((F).d),sizeof((F).d),1,(F).f)@]
+
+#define @[pascal_close(F)@] @[fclose((F).f)@]
+#define @[eof(F)@] @[feof((F).f)@]
+#define @[eoln(F)@] @[((F).d=='\n'||eof(F))@]
+#define @[erstat(F)@] @[((F).f==NULL?-1:ferror((F).f))@]
+
+#define @[pascal_read(F,X)@] @[((X)=(F).d,get(F))@]
+#define @[read_ln(F)@] do get(F); while (!eoln(F))
+
+#define @[pascal_write(F, FMT,...)@] @[fprintf(F.f,FMT,## __VA_ARGS__)@]
+#define @[write_ln(F,...)@] @[pascal_write(F,__VA_ARGS__"\n")@]
+
+#define @[wterm(FMT,...)@] @[pascal_write(term_out,FMT, ## __VA_ARGS__)@]
+#define @[wterm_ln(FMT,...)@] @[wterm(FMT "\n", ## __VA_ARGS__)@]
+#define wterm_cr @[pascal_write(term_out,"\n")@]
+#define @[wlog(FMT, ...)@] @[pascal_write(log_file,FMT, ## __VA_ARGS__)@]
+#define @[wlog_ln(FMT, ...)@] @[wlog(FMT "\n", ## __VA_ARGS__)@]
+#define wlog_cr @[pascal_write(log_file,"\n")@]
+
+@ To end a line of text output, we call |print_ln|.
+
+@<Basic print...@>=
+static void print_ln(void) /*prints an end-of-line*/
+{@+switch (selector) {
+case term_and_log: {@+wterm_cr;wlog_cr;
+ term_offset=0;file_offset=0;
+ } @+break;
+case log_only: {@+wlog_cr;file_offset=0;
+ } @+break;
+case term_only: {@+wterm_cr;term_offset=0;
+ } @+break;
+case no_print: case pseudo: case new_string: do_nothing;@+break;
+default:write_ln(write_file[selector]);
+} @/
+} /*|tally| is not affected*/
+
+@ The |print_char| procedure sends one character to the desired destination,
+using the |xchr| array to map it into an external character compatible with
+|input_ln|. All printing comes through |print_ln| or |print_char|.
+
+@<Basic printing...@>=
+static void print_char(ASCII_code @!s) /*prints a single character*/
+{@+
+if (@<Character |s| is the current new-line character@>)
+ if (selector < pseudo)
+ {@+print_ln();return;
+ }
+switch (selector) {
+case term_and_log: {@+wterm("%c",xchr[s]);wlog("%c",xchr[s]);
+ incr(term_offset);incr(file_offset);
+ if (term_offset==max_print_line)
+ {@+wterm_cr;term_offset=0;
+ }
+ if (file_offset==max_print_line)
+ {@+wlog_cr;file_offset=0;
+ }
+ } @+break;
+case log_only: {@+wlog("%c",xchr[s]);incr(file_offset);
+ if (file_offset==max_print_line) print_ln();
+ } @+break;
+case term_only: {@+wterm("%c",xchr[s]);incr(term_offset);
+ if (term_offset==max_print_line) print_ln();
+ } @+break;
+case no_print: do_nothing;@+break;
+case pseudo: if (tally < trick_count) trick_buf[tally%error_line]=s;@+break;
+case new_string: {@+if (pool_ptr < pool_size) append_char(s);
+ } @+break; /*we drop characters if the string space is full*/
+default:pascal_write(write_file[selector],"%c", xchr[s]);
+} @/
+incr(tally);
+}
+
+@ An entire string is output by calling |print|. Note that if we are outputting
+the single standard ASCII character \.c, we could call |print('c')|, since
+|'c'==99| is the number of a single-character string, as explained above. But
+|print_char('c')| is quicker, so \TeX\ goes directly to the |print_char|
+routine when it knows that this is safe. (The present implementation
+assumes that it is always safe to print a visible ASCII character.)
+@^system dependencies@>
+
+@<Basic print...@>=
+static void print(char *s) /* the simple version */
+{ @+if (s == NULL) s="???"; /*this can't happen*/
+ while (*s!=0) print_char(*s++);@+
+}
+
+static void printn(int @!s) /*prints string |s|*/
+{@+
+pool_pointer j; /*current character code position*/
+int @!nl; /*new-line character to restore*/
+if (s >= str_ptr) {print("???"); return;}/*this can't happen*/
+@.???@>
+else if (s < 256)
+ if (s < 0) { print("???");return; } /*can't happen*/
+ else{@+if (selector > pseudo)
+ {@+print_char(s);return; /*internal strings are not expanded*/
+ }
+ if ((@<Character |s| is the current new-line character@>))
+ if (selector < pseudo)
+ {@+print_ln();return;
+ }
+ nl=new_line_char;new_line_char=-1;
+ /*temporarily disable new-line character*/
+ j=str_start[s];
+ while (j < str_start[s+1])
+ {@+print_char(so(str_pool[j]));incr(j);
+ }
+ new_line_char=nl;return;
+ }
+j=str_start[s];
+while (j < str_start[s+1])
+ {@+print_char(so(str_pool[j]));incr(j);
+ }
+}
+
+@ Control sequence names, file names, and strings constructed with
+\.{\\string} might contain |ASCII_code| values that can't
+be printed using |print_char|. Therefore we use |slow_print| for them:
+
+@<Basic print...@>=
+static void slow_print(int @!s) /*prints string |s|*/
+{@+pool_pointer j; /*current character code position*/
+if ((s >= str_ptr)||(s < 256)) printn(s);
+else{@+j=str_start[s];
+ while (j < str_start[s+1])
+ {@+printn(so(str_pool[j]));incr(j);
+ }
+ }
+}
+
+@ Here is the very first thing that \TeX\ prints: a headline that identifies
+the version number and format package. The |term_offset| variable is temporarily
+incorrect, but the discrepancy is not serious since we assume that this
+part of the program is system dependent.
+@^system dependencies@>
+
+According to the conventions of \TeX\ Live,
+ we print the |dump_name| if no format identifier is known.
+@<Initialize the output...@>=
+wterm("%s",banner);
+if (format_ident==0) wterm_ln(" (preloaded format=%s)", dump_name);
+else{@+slow_print(format_ident);print_ln();
+ }
+update_terminal;
+
+@ The procedure |print_nl| is like |print|, but it makes sure that the
+string appears at the beginning of a new line.
+
+@<Basic print...@>=
+static void print_nl(char *@!s) /*prints string |s| at beginning of line*/
+{@+if (((term_offset > 0)&&(odd(selector)))||@|
+ ((file_offset > 0)&&(selector >= log_only))) print_ln();
+print(s);
+}
+
+@ The procedure |print_esc| prints a string that is preceded by
+the user's escape character (which is usually a backslash).
+
+@<Basic print...@>=
+static void printn_esc(str_number @!s) /*prints escape character, then |s|*/
+{@+int c; /*the escape character code*/
+@<Set variable |c| to the current escape character@>;
+if (c >= 0) if (c < 256) printn(c);
+slow_print(s);
+}
+
+static void print_esc(char *@!s) /*the fast way*/
+{@+int c; /*the escape character code*/
+@<Set variable |c| to the current escape character@>;
+if (c >= 0) if (c < 256) printn(c);
+print(s);
+}
+
+@ An array of digits in the range |0 dotdot 15| is printed by |print_the_digs|.
+
+@<Basic print...@>=
+static void print_the_digs(eight_bits @!k)
+ /*prints |dig[k-1]|$\,\ldots\,$|dig[0]|*/
+{@+while (k > 0)
+ {@+decr(k);
+ if (dig[k] < 10) print_char('0'+dig[k]);
+ else print_char('A'-10+dig[k]);
+ }
+}
+
+@ The following procedure, which prints out the decimal representation of a
+given integer |n|, has been written carefully so that it works properly
+if |n==0| or if |(-n)| would cause overflow. It does not apply |%| or |/|
+to negative arguments, since such operations are not implemented consistently
+by all \PASCAL\ compilers.
+
+@<Basic print...@>=
+static void print_int(int @!n) /*prints an integer in decimal form*/
+{@+int k; /*index to current digit; we assume that $\vert n\vert<10^{23}$*/
+int @!m; /*used to negate |n| in possibly dangerous cases*/
+k=0;
+if (n < 0)
+ {@+print_char('-');
+ if (n > -100000000) negate(n);
+ else{@+m=-1-n;n=m/10;m=(m%10)+1;k=1;
+ if (m < 10) dig[0]=m;
+ else{@+dig[0]=0;incr(n);
+ }
+ }
+ }
+@/do@+{dig[k]=n%10;n=n/10;incr(k);
+}@+ while (!(n==0));
+print_the_digs(k);
+}
+
+@ Here is a trivial procedure to print two digits; it is usually called with
+a parameter in the range |0 <= n <= 99|.
+
+@p static void print_two(int @!n) /*prints two least significant digits*/
+{@+n=abs(n)%100;print_char('0'+(n/10));
+print_char('0'+(n%10));
+}
+
+@ Hexadecimal printing of nonnegative integers is accomplished by |print_hex|.
+
+@p static void print_hex(int @!n)
+ /*prints a positive integer in hexadecimal form*/
+{@+int k; /*index to current digit; we assume that $0\le n<16^{22}$*/
+k=0;print_char('"');
+@/do@+{dig[k]=n%16;n=n/16;incr(k);
+}@+ while (!(n==0));
+print_the_digs(k);
+}
+
+@ Old versions of \TeX\ needed a procedure called |print_ASCII| whose function
+is now subsumed by |print|. We retain the old name here as a possible aid to
+future software arch\ae ologists.
+
+@d print_ASCII printn
+
+@ Roman numerals are produced by the |print_roman_int| routine. Readers
+who like puzzles might enjoy trying to figure out how this tricky code
+works; therefore no explanation will be given. Notice that 1990 yields
+\.{mcmxc}, not \.{mxm}.
+
+@p static void print_roman_int(int @!n)
+{@+
+pool_pointer j, @!k; /*mysterious indices into |mystery|*/
+nonnegative_integer @!u, @!v; /*mysterious numbers*/
+const char mystery[] ="m2d5c2l5x2v5i";
+j=0;v=1000;
+loop@+{@+while (n >= v)
+ {@+print_char(so(mystery[j]));n=n-v;
+ }
+ if (n <= 0) return; /*nonpositive input produces no output*/
+ k=j+2;u=v/(so(mystery[k-1])-'0');
+ if (mystery[k-1]==si('2'))
+ {@+k=k+2;u=u/(so(mystery[k-1])-'0');
+ }
+ if (n+u >= v)
+ {@+print_char(so(mystery[k]));n=n+u;
+ }
+ else{@+j=j+2;v=v/(so(mystery[j-1])-'0');
+ }
+ }
+}
+
+@ The |print| subroutine will not print a string that is still being
+created. The following procedure will.
+
+@p static void print_current_string(void) /*prints a yet-unmade string*/
+{@+pool_pointer j; /*points to current character code*/
+j=str_start[str_ptr];
+while (j < pool_ptr)
+ {@+print_char(so(str_pool[j]));incr(j);
+ }
+}
+
+@ Here is a procedure that asks the user to type a line of input,
+assuming that the |selector| setting is either |term_only| or |term_and_log|.
+The input is placed into locations |first| through |last-1| of the
+|buffer| array, and echoed on the transcript file if appropriate.
+
+This procedure is never called when |interaction < scroll_mode|.
+
+@d prompt_input(A) {@+wake_up_terminal;print(A);term_input();
+ } /*prints a string and gets a line of input*/
+
+@p static void term_input(void) /*gets a line from the terminal*/
+{@+int k; /*index into |buffer|*/
+update_terminal; /*now the user sees the prompt for sure*/
+if (!input_ln(&term_in, true)) fatal_error("End of file on the terminal!");
+@.End of file on the terminal@>
+term_offset=0; /*the user's line ended with \<\rm return>*/
+decr(selector); /*prepare to echo the input*/
+if (last!=first) for (k=first; k<=last-1; k++) printn(buffer[k]);
+print_ln();incr(selector); /*restore previous status*/
+}
+
+@* Reporting errors.
+When something anomalous is detected, \TeX\ typically does something like this:
+$$\vbox{\halign{#\hfil\cr
+|print_err("Something anomalous has been detected");|\cr
+|help3("This is the first line of my offer to help.")|\cr
+|("This is the second line. I'm trying to")|\cr
+|("explain the best way for you to proceed.");|\cr
+|error;|\cr}}$$
+A two-line help message would be given using |help2|, etc.; these informal
+helps should use simple vocabulary that complements the words used in the
+official error message that was printed. (Outside the U.S.A., the help
+messages should preferably be translated into the local vernacular. Each
+line of help is at most 60 characters long, in the present implementation,
+so that |max_print_line| will not be exceeded.)
+
+The |print_err| procedure supplies a `\.!' before the official message,
+and makes sure that the terminal is awake if a stop is going to occur.
+The |error| procedure supplies a `\..' after the official message, then it
+shows the location of the error; and if |interaction==error_stop_mode|,
+it also enters into a dialog with the user, during which time the help
+message may be printed.
+@^system dependencies@>
+
+@<Error handling...@>=
+static void print_err(char *s)
+{@+if (interaction==error_stop_mode) wake_up_terminal;
+ if (filelineerrorstylep) print_file_line(); /* \TeX\ Live */
+ else print_nl("! ");
+ print(s);
+}
+
+@ The global variable |interaction| has four settings, representing increasing
+amounts of user interaction:
+
+@d batch_mode 0 /*omits all stops and omits terminal output*/
+@d nonstop_mode 1 /*omits all stops*/
+@d scroll_mode 2 /*omits error stops*/
+@d error_stop_mode 3 /*stops at every opportunity to interact*/
+
+@<Glob...@>=
+static int @!interaction; /*current level of interaction*/
+
+@ @<Set init...@>=
+if (interaction_option<0) interaction=error_stop_mode;
+else interaction=interaction_option; /* \TeX\ Live */
+
+@ \TeX\ is careful not to call |error| when the print |selector| setting
+might be unusual. The only possible values of |selector| at the time of
+error messages are
+
+\yskip\hang|no_print| (when |interaction==batch_mode|
+ and |log_file| not yet open);
+
+\hang|term_only| (when |interaction > batch_mode| and |log_file| not yet open);
+
+\hang|log_only| (when |interaction==batch_mode| and |log_file| is open);
+
+\hang|term_and_log| (when |interaction > batch_mode| and |log_file| is open).
+
+@<Initialize the print |selector| based on |interaction|@>=
+if (interaction==batch_mode) selector=no_print;@+else selector=term_only
+
+@ A global variable |deletions_allowed| is set |false| if the |get_next|
+routine is active when |error| is called; this ensures that |get_next|
+and related routines like |get_token| will never be called recursively.
+A similar interlock is provided by |set_box_allowed|.
+@^recursion@>
+
+The global variable |history| records the worst level of error that
+has been detected. It has four possible values: |spotless|, |warning_issued|,
+|error_message_issued|, and |fatal_error_stop|.
+
+Another global variable, |error_count|, is increased by one when an
+|error| occurs without an interactive dialog, and it is reset to zero at
+the end of every paragraph. If |error_count| reaches 100, \TeX\ decides
+that there is no point in continuing further.
+
+@d spotless 0 /*|history| value when nothing has been amiss yet*/
+@d warning_issued 1 /*|history| value when |begin_diagnostic| has been called*/
+@d error_message_issued 2 /*|history| value when |error| has been called*/
+@d fatal_error_stop 3 /*|history| value when termination was premature*/
+
+@<Glob...@>=
+static bool @!deletions_allowed; /*is it safe for |error| to call |get_token|?*/
+static bool @!set_box_allowed; /*is it safe to do a \.{\\setbox} assignment?*/
+static int @!history; /*has the source input been clean so far?*/
+static int @!error_count; /*the number of scrolled errors since the
+ last paragraph ended*/
+
+@ The value of |history| is initially |fatal_error_stop|, but it will
+be changed to |spotless| if \TeX\ survives the initialization process.
+
+@<Set init...@>=
+deletions_allowed=true;set_box_allowed=true;
+error_count=0; /*|history| is initialized elsewhere*/
+
+@ Since errors can be detected almost anywhere in \TeX, we want to declare the
+error procedures near the beginning of the program. But the error procedures
+in turn use some other procedures, which need to be declared |forward|
+before we get to |error| itself.
+
+It is possible for |error| to be called recursively if some error arises
+when |get_token| is being used to delete a token, and/or if some fatal error
+occurs while \TeX\ is trying to fix a non-fatal one. But such recursion
+@^recursion@>
+is never more than two levels deep.
+
+@<Error handling...@>=
+static void normalize_selector(void);@/
+static void get_token(void);@/
+static void term_input(void);@/
+static void show_context(void);@/
+static void begin_file_reading(void);@/
+static void open_log_file(void);@/
+static void close_files_and_terminate(void);@/
+static void clear_for_error_prompt(void);@/
+static void give_err_help(void);@/
+#ifdef @!DEBUG
+static void debug_help(void);
+#else
+#define debug_help() do_nothing
+#endif
+
+@ Individual lines of help are recorded in the array |help_line|, which
+contains entries in positions |0 dotdot(help_ptr-1)|. They should be printed
+in reverse order, i.e., with |help_line[0]| appearing last.
+
+@d hlp1(A) help_line[0]=A;@+}
+@d hlp2(A, B) help_line[1]=A;help_line[0]=B;@+}
+@d hlp3(A, B, C) help_line[2]=A;help_line[1]=B;help_line[0]=C;@+}
+@d hlp4(A, B, C, D) help_line[3]=A;help_line[2]=B;help_line[1]=C;help_line[0]=D;@+}
+@d hlp5(A, B, C, D, E) help_line[4]=A;help_line[3]=B;help_line[2]=C;help_line[1]=D;help_line[0]=E;@+}
+@d hlp6(A, B, C, D, E, F) help_line[5]=A;help_line[4]=B;help_line[3]=C;help_line[2]=D;help_line[1]=E;help_line[0]=F;@+}
+@d help0 help_ptr=0 /*sometimes there might be no help*/
+@d help1(A) @+{@+help_ptr=1;hlp1(A) /*use this with one help line*/
+@d help2(A, B) @+{@+help_ptr=2;hlp2(A, B) /*use this with two help lines*/
+@d help3(A, B, C) @+{@+help_ptr=3;hlp3(A, B, C) /*use this with three help lines*/
+@d help4(A, B, C, D) @+{@+help_ptr=4;hlp4(A, B, C, D) /*use this with four help lines*/
+@d help5(A, B, C, D, E) @+{@+help_ptr=5;hlp5(A, B, C, D, E) /*use this with five help lines*/
+@d help6(A, B, C, D, E, F) @+{@+help_ptr=6;hlp6(A, B, C, D, E, F) /*use this with six help lines*/
+
+@<Glob...@>=
+static char *@!help_line[6]; /*helps for the next |error|*/
+static int @!help_ptr; /*the number of help lines present*/
+static bool @!use_err_help; /*should the |err_help| list be shown?*/
+
+@ @<Set init...@>=
+help_ptr=0;use_err_help=false;
+
+@ The |jump_out| procedure just cuts across all active procedure levels and
+goes to |end_of_TEX|. This is the only nontrivial |@!goto| statement in the
+whole program. It is used when there is no recovery from a particular error.
+
+Some \PASCAL\ compilers do not implement non-local |goto| statements.
+@^system dependencies@>
+In such cases the body of |jump_out| should simply be
+`|close_files_and_terminate|;\thinspace' followed by a call on some system
+procedure that quietly terminates the program.
+
+@<Error hand...@>=
+static void jump_out(void)
+{@+ close_files_and_terminate(); exit(0);
+}
+
+@ Here now is the general |error| routine.
+
+@<Error hand...@>=
+static void error(void) /*completes the job of error reporting*/
+{@+
+ASCII_code c; /*what the user types*/
+int @!s1, @!s2, @!s3, @!s4;
+ /*used to save global variables when deleting tokens*/
+if (history < error_message_issued) history=error_message_issued;
+print_char('.');show_context();
+if (interaction==error_stop_mode)
+ @<Get user's advice and |return|@>;
+incr(error_count);
+if (error_count==100)
+ {@+print_nl("(That makes 100 errors; please try again.)");
+@.That makes 100 errors...@>
+ history=fatal_error_stop;jump_out();
+ }
+@<Put help message on the transcript file@>;
+}
+
+@ @<Get user's advice...@>=
+loop@+{@+resume: if (interaction!=error_stop_mode) return;
+ clear_for_error_prompt();prompt_input("? ");
+@.?\relax@>
+ if (last==first) return;
+ c=buffer[first];
+ if (c >= 'a') c=c+'A'-'a'; /*convert to uppercase*/
+ @<Interpret code |c| and |return| if done@>;
+ }
+
+@ It is desirable to provide an `\.E' option here that gives the user
+an easy way to return from \TeX\ to the system editor, with the offending
+line ready to be edited. But such an extension requires some system
+wizardry, so the present implementation simply types out the name of the
+file that should be
+edited and the relevant line number.
+@^system dependencies@>
+
+There is a secret `\.D' option available when the debugging routines haven't
+been commented~out.
+@^debugging@>
+
+@<Interpret code |c| and |return| if done@>=
+switch (c) {
+case '0': case '1': case '2': case '3':
+ case '4': case '5': case '6': case '7':
+ case '8': case '9': if (deletions_allowed)
+ @<Delete \(c)|c-"0"| tokens and |goto resume|@>@;@+break;
+@t\4\4@>@;
+#ifdef @!DEBUG
+case 'D': {@+debug_help();goto resume;@+}
+#endif
+case 'E': if (base_ptr > 0) if (input_stack[base_ptr].name_field >= 256)
+ {@+print_nl("You want to edit file ");
+@.You want to edit file x@>
+ slow_print(input_stack[base_ptr].name_field);
+ print(" at line ");print_int(line);
+ interaction=scroll_mode;jump_out();
+ } @+break;
+case 'H': @<Print the help information and |goto resume|@>@;
+case 'I': @<Introduce new material from the terminal and |return|@>@;
+case 'Q': case 'R': case 'S': @<Change the interaction level and |return|@>@;
+case 'X': {@+interaction=scroll_mode;jump_out();
+ } @+break;
+default:do_nothing;
+} @/
+@<Print the menu of available options@>@;
+
+@ @<Print the menu...@>=
+{@+print("Type <return> to proceed, S to scroll future error messages,");@/
+@.Type <return> to proceed...@>
+print_nl("R to run without stopping, Q to run quietly,");@/
+print_nl("I to insert something, ");
+if (base_ptr > 0) if (input_stack[base_ptr].name_field >= 256)
+ print("E to edit your file,");
+if (deletions_allowed)
+ print_nl("1 or ... or 9 to ignore the next 1 to 9 tokens of input,");
+print_nl("H for help, X to quit.");
+}
+
+@ Here the author of \TeX\ apologizes for making use of the numerical
+relation between |'Q'|, |'R'|, |'S'|, and the desired interaction settings
+|batch_mode|, |nonstop_mode|, |scroll_mode|.
+@^Knuth, Donald Ervin@>
+
+@<Change the interaction...@>=
+{@+error_count=0;interaction=batch_mode+c-'Q';
+print("OK, entering ");
+switch (c) {
+case 'Q': {@+print_esc("batchmode");decr(selector);
+ } @+break;
+case 'R': print_esc("nonstopmode");@+break;
+case 'S': print_esc("scrollmode");
+} /*there are no other cases*/
+print("...");print_ln();update_terminal;return;
+}
+
+@ When the following code is executed, |buffer[(first+1)dotdot(last-1)]| may
+contain the material inserted by the user; otherwise another prompt will
+be given. In order to understand this part of the program fully, you need
+to be familiar with \TeX's input stacks.
+
+@<Introduce new material...@>=
+{@+begin_file_reading(); /*enter a new syntactic level for terminal input*/
+ /*now |state==mid_line|, so an initial blank space will count as a blank*/
+if (last > first+1)
+ {@+loc=first+1;buffer[first]=' ';
+ }
+else{@+prompt_input("insert>");loc=first;
+@.insert>@>
+ }
+first=last;
+cur_input.limit_field=last-1; /*no |end_line_char| ends this line*/
+return;
+}
+
+@ We allow deletion of up to 99 tokens at a time.
+
+@<Delete \(c)|c-"0"| tokens...@>=
+{@+s1=cur_tok;s2=cur_cmd;s3=cur_chr;s4=align_state;
+align_state=1000000;OK_to_interrupt=false;
+if ((last > first+1)&&(buffer[first+1] >= '0')&&(buffer[first+1] <= '9'))
+ c=c*10+buffer[first+1]-'0'*11;
+else c=c-'0';
+while (c > 0)
+ {@+get_token(); /*one-level recursive call of |error| is possible*/
+ decr(c);
+ }
+cur_tok=s1;cur_cmd=s2;cur_chr=s3;align_state=s4;OK_to_interrupt=true;
+help2("I have just deleted some text, as you asked.",@/
+"You can now delete more, or insert, or whatever.");
+show_context();goto resume;
+}
+
+@ @<Print the help info...@>=
+{@+if (use_err_help)
+ {@+give_err_help();use_err_help=false;
+ }
+else{@+if (help_ptr==0)
+ help2("Sorry, I don't know how to help in this situation.",@/
+ @t\kern1em@>"Maybe you should try asking a human?");
+ @/do@+{decr(help_ptr);print(help_line[help_ptr]);print_ln();
+ }@+ while (!(help_ptr==0));
+ }
+help4("Sorry, I already gave what help I could...",@/
+ "Maybe you should try asking a human?",@/
+ "An error might have occurred before I noticed any problems.",@/
+ "``If all else fails, read the instructions.'");@/
+goto resume;
+}
+
+@ @<Put help message on the transcript file@>=
+if (interaction > batch_mode) decr(selector); /*avoid terminal output*/
+if (use_err_help)
+ {@+print_ln();give_err_help();
+ }
+else while (help_ptr > 0)
+ {@+decr(help_ptr);print_nl(help_line[help_ptr]);
+ }
+print_ln();
+if (interaction > batch_mode) incr(selector); /*re-enable terminal output*/
+print_ln()
+
+@ A dozen or so error messages end with a parenthesized integer, so we
+save a teeny bit of program space by declaring the following procedure:
+
+@p static void int_error(int @!n)
+{@+print(" (");print_int(n);print_char(')');error();
+}
+
+@ In anomalous cases, the print selector might be in an unknown state;
+the following subroutine is called to fix things just enough to keep
+running a bit longer.
+
+@p static void normalize_selector(void)
+{@+if (log_opened) selector=term_and_log;
+else selector=term_only;
+if (job_name==0) open_log_file();
+if (interaction==batch_mode) decr(selector);
+}
+
+@ The following procedure prints \TeX's last words before dying.
+
+@d succumb {@+if (interaction==error_stop_mode)
+ interaction=scroll_mode; /*no more interaction*/
+ if (log_opened) error();
+ if (interaction > batch_mode) debug_help();
+ history=fatal_error_stop;jump_out(); /*irrecoverable error*/
+ }
+
+@<Error hand...@>=
+static void fatal_error(char *@!s) /*prints |s|, and that's it*/
+{@+normalize_selector();@/
+print_err("Emergency stop");help1(s);succumb;
+@.Emergency stop@>
+}
+
+@ Here is the most dreaded error message.
+
+@<Error hand...@>=
+static void overflow(char *@!s, int @!n) /*stop due to finiteness*/
+{@+normalize_selector();
+print_err("TeX capacity exceeded, sorry [");
+@.TeX capacity exceeded ...@>
+print(s);print_char('=');print_int(n);print_char(']');
+help2("If you really absolutely need more capacity,",@/
+ "you can ask a wizard to enlarge me.");
+succumb;
+}
+
+@ The program might sometime run completely amok, at which point there is
+no choice but to stop. If no previous error has been detected, that's bad
+news; a message is printed that is really intended for the \TeX\
+maintenance person instead of the user (unless the user has been
+particularly diabolical). The index entries for `this can't happen' may
+help to pinpoint the problem.
+@^dry rot@>
+
+@<Error hand...@>=
+static void confusion(char *@!s)
+ /*consistency check violated; |s| tells where*/
+{@+normalize_selector();
+if (history < error_message_issued)
+ {@+print_err("This can't happen (");print(s);print_char(')');
+@.This can't happen@>
+ help1("I'm broken. Please show this to someone who can fix can fix");
+ }
+else{@+print_err("I can't go on meeting you like this");
+@.I can't go on...@>
+ help2("One of your faux pas seems to have wounded me deeply...",@/
+ "in fact, I'm barely conscious. Please fix it and try again.");
+ }
+succumb;
+}
+
+@ Users occasionally want to interrupt \TeX\ while it's running.
+If the \PASCAL\ runtime system allows this, one can implement
+a routine that sets the global variable |interrupt| to some nonzero value
+when such an interrupt is signalled. Otherwise there is probably at least
+a way to make |interrupt| nonzero using the \PASCAL\ debugger.
+@^system dependencies@>
+@^debugging@>
+
+@d check_interrupt {@+if (interrupt!=0) pause_for_instructions();
+ }
+
+@<Global...@>=
+static int @!interrupt; /*should \TeX\ pause for instructions?*/
+static bool @!OK_to_interrupt; /*should interrupts be observed?*/
+
+@ @<Set init...@>=
+interrupt=0;OK_to_interrupt=true;
+
+@ When an interrupt has been detected, the program goes into its
+highest interaction level and lets the user have nearly the full flexibility of
+the |error| routine. \TeX\ checks for interrupts only at times when it is
+safe to do this.
+
+@p static void pause_for_instructions(void)
+{@+if (OK_to_interrupt)
+ {@+interaction=error_stop_mode;
+ if ((selector==log_only)||(selector==no_print))
+ incr(selector);
+ print_err("Interruption");
+@.Interruption@>
+ help3("You rang?",@/
+ "Try to insert an instruction for me (e.g., `I\\showlists'),",@/
+ "unless you just want to quit by typing `X'.");
+ deletions_allowed=false;error();deletions_allowed=true;
+ interrupt=0;
+ }
+}
+
+@* Arithmetic with scaled dimensions.
+The principal computations performed by \TeX\ are done entirely in terms of
+integers less than $2^{31}$ in magnitude; and divisions are done only when both
+dividend and divisor are nonnegative. Thus, the arithmetic specified in this
+program can be carried out in exactly the same way on a wide variety of
+computers, including some small ones. Why? Because the arithmetic
+calculations need to be spelled out precisely in order to guarantee that
+\TeX\ will produce identical output on different machines. If some
+quantities were rounded differently in different implementations, we would
+find that line breaks and even page breaks might occur in different places.
+Hence the arithmetic of \TeX\ has been designed with care, and systems that
+claim to be implementations of \TeX82 should follow precisely the
+@:TeX82}{\TeX82@>
+calculations as they appear in the present program.
+
+(Actually there are three places where \TeX\ uses |/| with a possibly negative
+numerator. These are harmless; see |/| in the index. Also if the user
+sets the \.{\\time} or the \.{\\year} to a negative value, some diagnostic
+information will involve negative-numerator division. The same remarks
+apply for |%| as well as for |/|.)
+
+@ Here is a routine that calculates half of an integer, using an
+unambiguous convention with respect to signed odd numbers.
+
+@p static int half(int @!x)
+{@+if (odd(x)) return(x+1)/2;
+else return x/2;
+}
+
+@ Fixed-point arithmetic is done on {\sl scaled integers\/} that are multiples
+of $2^{-16}$. In other words, a binary point is assumed to be sixteen bit
+positions from the right end of a binary computer word.
+
+@d unity 0200000 /*$2^{16}$, represents 1.00000*/
+@d two 0400000 /*$2^{17}$, represents 2.00000*/
+
+@<Types...@>=
+typedef int scaled; /*this type is used for scaled integers*/
+typedef int32_t nonnegative_integer; /*$0\le x<2^{31}$*/
+typedef int8_t small_number; /*this type is self-explanatory*/
+
+@ The following function is used to create a scaled integer from a given decimal
+fraction $(.d_0d_1\ldots d_{k-1})$, where |0 <= k <= 17|. The digit $d_i$ is
+given in |dig[i]|, and the calculation produces a correctly rounded result.
+
+@p static scaled round_decimals(small_number @!k)
+ /*converts a decimal fraction*/
+{@+int a; /*the accumulator*/
+a=0;
+while (k > 0)
+ {@+decr(k);a=(a+dig[k]*two)/10;
+ }
+return(a+1)/2;
+}
+
+@ Conversely, here is a procedure analogous to |print_int|. If the output
+of this procedure is subsequently read by \TeX\ and converted by the
+|round_decimals| routine above, it turns out that the original value will
+be reproduced exactly; the ``simplest'' such decimal number is output,
+but there is always at least one digit following the decimal point.
+
+The invariant relation in the \&{repeat} loop is that a sequence of
+decimal digits yet to be printed will yield the original number if and only if
+they form a fraction~$f$ in the range $s-\delta\le 10\cdot2^{16}f<s$.
+We can stop if and only if $f=0$ satisfies this condition; the loop will
+terminate before $s$ can possibly become zero.
+
+@p static void print_scaled(scaled @!s) /*prints scaled real, rounded to five
+ digits*/
+{@+scaled delta; /*amount of allowable inaccuracy*/
+if (s < 0)
+ {@+print_char('-');negate(s); /*print the sign, if negative*/
+ }
+print_int(s/unity); /*print the integer part*/
+print_char('.');
+s=10*(s%unity)+5;delta=10;
+@/do@+{if (delta > unity) s=s+0100000-50000; /*round the last digit*/
+print_char('0'+(s/unity));s=10*(s%unity);delta=delta*10;
+}@+ while (!(s <= delta));
+}
+
+@ Physical sizes that a \TeX\ user specifies for portions of documents are
+represented internally as scaled points. Thus, if we define an `sp' (scaled
+@^sp@>
+point) as a unit equal to $2^{-16}$ printer's points, every dimension
+inside of \TeX\ is an integer number of sp. There are exactly
+4,736,286.72 sp per inch. Users are not allowed to specify dimensions
+larger than $2^{30}-1$ sp, which is a distance of about 18.892 feet (5.7583
+meters); two such quantities can be added without overflow on a 32-bit
+computer.
+
+The present implementation of \TeX\ does not check for overflow when
+@^overflow in arithmetic@>
+dimensions are added or subtracted. This could be done by inserting a
+few dozen tests of the form `\ignorespaces|if (x >= 010000000000)|
+\\{report\_overflow}', but the chance of overflow is so remote that
+such tests do not seem worthwhile.
+
+\TeX\ needs to do only a few arithmetic operations on scaled quantities,
+other than addition and subtraction, and the following subroutines do most of
+the work. A single computation might use several subroutine calls, and it is
+desirable to avoid producing multiple error messages in case of arithmetic
+overflow; so the routines set the global variable |arith_error| to |true|
+instead of reporting errors directly to the user. Another global variable,
+|rem|, holds the remainder after a division.
+
+@<Glob...@>=
+static bool @!arith_error; /*has arithmetic overflow occurred recently?*/
+static scaled @!rem; /*amount subtracted to get an exact division*/
+
+@ The first arithmetical subroutine we need computes $nx+y$, where |x|
+and~|y| are |scaled| and |n| is an integer. We will also use it to
+multiply integers.
+
+@d nx_plus_y(A, B, C) mult_and_add(A, B, C, 07777777777)
+@d mult_integers(A, B) mult_and_add(A, B, 0, 017777777777)
+
+@p static scaled mult_and_add(int @!n, scaled @!x, scaled @!y, scaled @!max_answer)
+{@+if (n < 0)
+ {@+negate(x);negate(n);
+ }
+if (n==0) return y;
+else if (((x <= (max_answer-y)/n)&&(-x <= (max_answer+y)/n)))
+ return n*x+y;
+else{@+arith_error=true;return 0;
+ }
+}
+
+@ We also need to divide scaled dimensions by integers.
+
+@p static scaled x_over_n(scaled @!x, int @!n)
+{@+bool negative; /*should |rem| be negated?*/
+scaled x_over_n;
+negative=false;
+if (n==0)
+ {@+arith_error=true;x_over_n=0;rem=x;
+ }
+else{@+if (n < 0)
+ {@+negate(x);negate(n);negative=true;
+ }
+ if (x >= 0)
+ {@+x_over_n=x/n;rem=x%n;
+ }
+ else{@+x_over_n=-((-x)/n);rem=-((-x)%n);
+ }
+ }
+if (negative) negate(rem);
+return x_over_n;}
+
+@ Then comes the multiplication of a scaled number by a fraction |n/(double)d|,
+where |n| and |d| are nonnegative integers | <= @t$2^{16}$@>| and |d| is
+positive. It would be too dangerous to multiply by~|n| and then divide
+by~|d|, in separate operations, since overflow might well occur; and it
+would be too inaccurate to divide by |d| and then multiply by |n|. Hence
+this subroutine simulates 1.5-precision arithmetic.
+
+@p static scaled xn_over_d(scaled @!x, int @!n, int @!d)
+{@+bool positive; /*was |x >= 0|?*/
+nonnegative_integer @!t, @!u, @!v; /*intermediate quantities*/
+scaled xn_over_d;
+if (x >= 0) positive=true;
+else{@+negate(x);positive=false;
+ }
+t=(x%0100000)*n;
+u=(x/0100000)*n+(t/0100000);
+v=(u%d)*0100000+(t%0100000);
+if (u/d >= 0100000) arith_error=true;
+else u=0100000*(u/d)+(v/d);
+if (positive)
+ {@+xn_over_d=u;rem=v%d;
+ }
+else{@+xn_over_d=-u;rem=-(v%d);
+ }
+return xn_over_d;}
+
+@ The next subroutine is used to compute the ``badness'' of glue, when a
+total~|t| is supposed to be made from amounts that sum to~|s|. According
+to {\sl The \TeX book}, the badness of this situation is $100(t/s)^3$;
+however, badness is simply a heuristic, so we need not squeeze out the
+last drop of accuracy when computing it. All we really want is an
+approximation that has similar properties.
+@:TeXbook}{\sl The \TeX book@>
+
+The actual method used to compute the badness is easier to read from the
+program than to describe in words. It produces an integer value that is a
+reasonably close approximation to $100(t/s)^3$, and all implementations
+of \TeX\ should use precisely this method. Any badness of $2^{13}$ or more is
+treated as infinitely bad, and represented by 10000.
+
+It is not difficult to prove that $$\hbox{|badness(t+1, s) >= badness(t, s)
+ >= badness(t, s+1)|}.$$ The badness function defined here is capable of
+computing at most 1095 distinct values, but that is plenty.
+
+@d inf_bad 10000 /*infinitely bad value*/
+
+@p @<Declare \Prote\ arithmetic routines@>@/
+static halfword badness(scaled @!t, scaled @!s) /*compute badness, given |t >= 0|*/
+{@+int r; /*approximation to $\alpha t/s$, where $\alpha^3\approx
+ 100\cdot2^{18}$*/
+if (t==0) return 0;
+else if (s <= 0) return inf_bad;
+else{@+if (t <= 7230584) r=(t*297)/s; /*$297^3=99.94\times2^{18}$*/
+ else if (s >= 1663497) r=t/(s/297);
+ else r=t;
+ if (r > 1290) return inf_bad; /*$1290^3<2^{31}<1291^3$*/
+ else return(r*r*r+0400000)/01000000;
+ } /*that was $r^3/2^{18}$, rounded to the nearest integer*/
+}
+
+@ When \TeX\ ``packages'' a list into a box, it needs to calculate the
+proportionality ratio by which the glue inside the box should stretch
+or shrink. This calculation does not affect \TeX's decision making,
+so the precise details of rounding, etc., in the glue calculation are not
+of critical importance for the consistency of results on different computers.
+
+We shall use the type |glue_ratio| for such proportionality ratios.
+A glue ratio should take the same amount of memory as an
+|int| (usually 32 bits) if it is to blend smoothly with \TeX's
+other data structures. Thus |glue_ratio| should be equivalent to
+|short_real| in some implementations of \PASCAL. Alternatively,
+it is possible to deal with glue ratios using nothing but fixed-point
+arithmetic; see {\sl TUGboat \bf3},1 (March 1982), 10--27. (But the
+routines cited there must be modified to allow negative glue ratios.)
+@^system dependencies@>
+
+@d set_glue_ratio_zero(A) A=0.0 /*store the representation of zero ratio*/
+@d set_glue_ratio_one(A) A=1.0 /*store the representation of unit ratio*/
+@d unfix(A) ((double)(A)) /*convert from |glue_ratio| to type |double|*/
+@d fix(A) ((glue_ratio)(A)) /*convert from |double| to type |glue_ratio|*/
+@d float_constant(A) ((double)(A)) /*convert |int| constant to |double|*/
+
+@<Types...@>=
+#if __SIZEOF_FLOAT__==4
+typedef float float32_t;
+#else
+#error @=float type must have size 4@>
+#endif
+typedef float @!glue_ratio; /*one-word representation of a glue expansion factor*/
+
+@* Packed data.
+In order to make efficient use of storage space, \TeX\ bases its major data
+structures on a |memory_word|, which contains either a (signed) integer,
+possibly scaled, or a (signed) |glue_ratio|, or a small number of
+fields that are one half or one quarter of the size used for storing
+integers.
+
+If |x| is a variable of type |memory_word|, it contains up to four
+fields that can be referred to as follows:
+$$\vbox{\halign{\hfil#&#\hfil&#\hfil\cr
+|x|&.|i|&(an |int|)\cr
+|x|&.|sc|\qquad&(a |scaled| integer)\cr
+|x|&.|gr|&(a |glue_ratio|)\cr
+|x.hh.lh|, |x.hh|&.|rh|&(two halfword fields)\cr
+|x.hh.b0|, |x.hh.b1|, |x.hh|&.|rh|&(two quarterword fields, one halfword
+ field)\cr
+|x.qqqq.b0|, |x.qqqq.b1|, |x.qqqq|&.|b2|, |x.qqqq.b3|\hskip-100pt
+ &\qquad\qquad\qquad(four quarterword fields)\cr}}$$
+This is somewhat cumbersome to write, and not very readable either, but
+macros will be used to make the notation shorter and more transparent.
+The \PASCAL\ code below gives a formal definition of |memory_word| and
+its subsidiary types, using packed variant records. \TeX\ makes no
+assumptions about the relative positions of the fields within a word.
+
+Since we are assuming 32-bit integers, a halfword must contain at least
+16 bits, and a quarterword must contain at least 8 bits.
+@^system dependencies@>
+But it doesn't hurt to have more bits; for example, with enough 36-bit
+words you might be able to have |mem_max| as large as 262142, which is
+eight times as much memory as anybody had during the first four years of
+\TeX's existence.
+
+N.B.: Valuable memory space will be dreadfully wasted unless \TeX\ is compiled
+by a \PASCAL\ that packs all of the |memory_word| variants into
+the space of a single integer. This means, for example, that |glue_ratio|
+words should be |short_real| instead of |double| on some computers. Some
+\PASCAL\ compilers will pack an integer whose subrange is `|0 dotdot 255|' into
+an eight-bit field, but others insist on allocating space for an additional
+sign bit; on such systems you can get 256 values into a quarterword only
+if the subrange is `|-128 dotdot 127|'.
+
+The present implementation tries to accommodate as many variations as possible,
+so it makes few assumptions. If integers having the subrange
+`|min_quarterword dotdot max_quarterword|' can be packed into a quarterword,
+and if integers having the subrange `|min_halfword dotdot max_halfword|'
+can be packed into a halfword, everything should work satisfactorily.
+
+It is usually most efficient to have |min_quarterword==min_halfword==0|,
+so one should try to achieve this unless it causes a severe problem.
+The values defined here are recommended for most 32-bit computers.
+
+@d min_quarterword 0 /*smallest allowable value in a |quarterword|*/
+@d max_quarterword 65535 /*largest allowable value in a |quarterword|*/
+@d min_halfword 0 /*smallest allowable value in a |halfword|*/
+@d max_halfword 0x3FFFFFFF /*largest allowable value in a |halfword|*/
+
+@ Here are the inequalities that the quarterword and halfword values
+must satisfy (or rather, the inequalities that they mustn't satisfy):
+
+@<Check the ``constant''...@>=
+#ifdef @!INIT
+if ((mem_min!=mem_bot)||(mem_max!=mem_top)) bad=10;
+#endif
+@;@/
+if ((mem_min > mem_bot)||(mem_max < mem_top)) bad=10;
+if ((min_quarterword > 0)||(max_quarterword < 127)) bad=11;
+if ((min_halfword > 0)||(max_halfword < 32767)) bad=12;
+if ((min_quarterword < min_halfword)||@|
+ (max_quarterword > max_halfword)) bad=13;
+if ((mem_min < min_halfword)||(mem_max >= max_halfword)||@|
+ (mem_bot-mem_min > max_halfword+1)) bad=14;
+if ((font_base < min_quarterword)||(font_max > max_quarterword)) bad=15;
+if (font_max > font_base+256) bad=16;
+if ((save_size > max_halfword)||(max_strings > max_halfword)) bad=17;
+if (buf_size > max_halfword) bad=18;
+if (max_quarterword-min_quarterword < 255) bad=19;
+
+@ The operation of adding or subtracting |min_quarterword| occurs quite
+frequently in \TeX, so it is convenient to abbreviate this operation
+by using the macros |qi| and |qo| for input and output to and from
+quarterword format.
+
+The inner loop of \TeX\ will run faster with respect to compilers
+that don't optimize expressions like `|x+0|' and `|x-0|', if these
+macros are simplified in the obvious way when |min_quarterword==0|.
+@^inner loop@>@^system dependencies@>
+
+@d qi(A) A+min_quarterword
+ /*to put an |eight_bits| item into a quarterword*/
+@d qo(A) A-min_quarterword
+ /*to take an |eight_bits| item out of a quarterword*/
+@d hi(A) A+min_halfword
+ /*to put a sixteen-bit item into a halfword*/
+@d ho(A) A-min_halfword
+ /*to take a sixteen-bit item from a halfword*/
+
+@ The reader should study the following definitions closely:
+@^system dependencies@>
+
+@d sc i /*|scaled| data is equivalent to |int|*/
+
+@<Types...@>=
+typedef uint16_t quarterword; /*1/4 of a word*/
+typedef int32_t halfword; /*1/2 of a word*/
+typedef int8_t two_choices; /*used when there are two variants in a record*/
+typedef int8_t four_choices; /*used when there are four variants in a record*/
+typedef uint64_t fullword;
+typedef struct { @;@/
+ halfword @!rh;
+ union {
+ halfword @!lh;
+ struct { quarterword @!b0;quarterword @!b1;} ;
+ };} two_halves;
+typedef struct { @;@/
+ quarterword @!b0;
+ quarterword @!b1;
+ quarterword @!b2;
+ quarterword @!b3;
+ } four_quarters;
+typedef struct { @;@/
+ union {
+ fullword @!w;
+ int @!i;
+ glue_ratio @!gr;
+ two_halves @!hh;
+ four_quarters @!qqqq;
+ };} memory_word;
+typedef struct {@+FILE *f;@+memory_word@,d;@+} word_file;
+
+@ When debugging, we may want to print a |memory_word| without knowing
+what type it is; so we print it in all modes.
+@^dirty \PASCAL@>@^debugging@>
+
+@p
+#ifdef @!DEBUG
+static void print_word(memory_word @!w)
+ /*prints |w| in all ways*/
+{@+print_int(w.i);print_char(' ');@/
+print_scaled(w.sc);print_char(' ');@/
+print_scaled(round(unity*unfix(w.gr)));print_ln();@/
+@^real multiplication@>
+print_int(w.hh.lh);print_char('=');print_int(w.hh.b0);print_char(':');
+print_int(w.hh.b1);print_char(';');print_int(w.hh.rh);print_char(' ');@/
+print_int(w.qqqq.b0);print_char(':');print_int(w.qqqq.b1);print_char(':');
+print_int(w.qqqq.b2);print_char(':');print_int(w.qqqq.b3);
+}
+#endif
+
+@* Dynamic memory allocation.
+The \TeX\ system does nearly all of its own memory allocation, so that it
+can readily be transported into environments that do not have automatic
+facilities for strings, garbage collection, etc., and so that it can be in
+control of what error messages the user receives. The dynamic storage
+requirements of \TeX\ are handled by providing a large array |mem| in
+which consecutive blocks of words are used as nodes by the \TeX\ routines.
+
+Pointer variables are indices into this array, or into another array
+called |eqtb| that will be explained later. A pointer variable might
+also be a special flag that lies outside the bounds of |mem|, so we
+allow pointers to assume any |halfword| value. The minimum halfword
+value represents a null pointer. \TeX\ does not assume that |mem[null]| exists.
+
+@s pointer int
+@d pointer halfword /*a flag or a location in |mem| or |eqtb|*/
+@d null min_halfword /*the null pointer*/
+
+@<Glob...@>=
+static pointer @!temp_ptr; /*a pointer variable for occasional emergency use*/
+
+@ The |mem| array is divided into two regions that are allocated separately,
+but the dividing line between these two regions is not fixed; they grow
+together until finding their ``natural'' size in a particular job.
+Locations less than or equal to |lo_mem_max| are used for storing
+variable-length records consisting of two or more words each. This region
+is maintained using an algorithm similar to the one described in exercise
+2.5--19 of {\sl The Art of Computer Programming}. However, no size field
+appears in the allocated nodes; the program is responsible for knowing the
+relevant size when a node is freed. Locations greater than or equal to
+|hi_mem_min| are used for storing one-word records; a conventional
+\.{AVAIL} stack is used for allocation in this region.
+
+Locations of |mem| between |mem_bot| and |mem_top| may be dumped as part
+of preloaded format files, by the \.{INITEX} preprocessor.
+@.INITEX@>
+Production versions of \TeX\ may extend the memory at both ends in order to
+provide more space; locations between |mem_min| and |mem_bot| are always
+used for variable-size nodes, and locations between |mem_top| and |mem_max|
+are always used for single-word nodes.
+
+The key pointers that govern |mem| allocation have a prescribed order:
+$$\advance\thickmuskip-2mu
+\hbox{|null <= mem_min <= mem_bot < lo_mem_max <
+ hi_mem_min < mem_top <= mem_end <= mem_max|.}$$
+
+Empirical tests show that the present implementation of \TeX\ tends to
+spend about 9\pct! of its running time allocating nodes, and about 6\pct!
+deallocating them after their use.
+
+@<Glob...@>=
+static memory_word @!mem0[mem_max-mem_min+1],
+ *const @!mem = @!mem0-mem_min; /*the big dynamic storage area*/
+static pointer @!lo_mem_max; /*the largest location of variable-size memory in use*/
+static pointer @!hi_mem_min; /*the smallest location of one-word memory in use*/
+
+@ In order to study the memory requirements of particular applications, it
+is possible to prepare a version of \TeX\ that keeps track of current and
+maximum memory usage. When code between the delimiters |
+#ifdef @!STAT
+| $\ldots$
+|@t\#\&{endif}@>| is not ``commented out,'' \TeX\ will run a bit slower but it will
+report these statistics when |tracing_stats| is sufficiently large.
+
+@<Glob...@>=
+static int @!var_used, @!dyn_used; /*how much memory is in use*/
+#ifdef @!STAT
+#define incr_dyn_used @[incr(dyn_used)@]
+#define decr_dyn_used @[decr(dyn_used)@]
+#else
+#define incr_dyn_used
+#define decr_dyn_used
+#endif
+
+@ Let's consider the one-word memory region first, since it's the
+simplest. The pointer variable |mem_end| holds the highest-numbered location
+of |mem| that has ever been used. The free locations of |mem| that
+occur between |hi_mem_min| and |mem_end|, inclusive, are of type
+|two_halves|, and we write |info(p)| and |link(p)| for the |lh|
+and |rh| fields of |mem[p]| when it is of this type. The single-word
+free locations form a linked list
+$$|avail|,\;\hbox{|link(avail)|},\;\hbox{|link(link(avail))|},\;\ldots$$
+terminated by |null|.
+
+@d link(A) mem[A].hh.rh /*the |link| field of a memory word*/
+@d info(A) mem[A].hh.lh /*the |info| field of a memory word*/
+
+@<Glob...@>=
+static pointer @!avail; /*head of the list of available one-word nodes*/
+static pointer @!mem_end; /*the last one-word node used in |mem|*/
+
+@ If memory is exhausted, it might mean that the user has forgotten
+a right brace. We will define some procedures later that try to help
+pinpoint the trouble.
+
+@p @<Declare the procedure called |show_token_list|@>@/
+@<Declare the procedure called |runaway|@>@;
+
+@ The function |get_avail| returns a pointer to a new one-word node whose
+|link| field is null. However, \TeX\ will halt if there is no more room left.
+@^inner loop@>
+
+If the available-space list is empty, i.e., if |avail==null|,
+we try first to increase |mem_end|. If that cannot be done, i.e., if
+|mem_end==mem_max|, we try to decrease |hi_mem_min|. If that cannot be
+done, i.e., if |hi_mem_min==lo_mem_max+1|, we have to quit.
+
+@p static pointer get_avail(void) /*single-word node allocation*/
+{@+pointer p; /*the new node being got*/
+p=avail; /*get top location in the |avail| stack*/
+if (p!=null) avail=link(avail); /*and pop it off*/
+else if (mem_end < mem_max) /*or go into virgin territory*/
+ {@+incr(mem_end);p=mem_end;
+ }
+else{@+decr(hi_mem_min);p=hi_mem_min;
+ if (hi_mem_min <= lo_mem_max)
+ {@+runaway(); /*if memory is exhausted, display possible runaway text*/
+ overflow("main memory size", mem_max+1-mem_min);
+ /*quit; all one-word nodes are busy*/
+@:TeX capacity exceeded main memory size}{\quad main memory size@>
+ }
+ }
+link(p)=null; /*provide an oft-desired initialization of the new node*/
+#ifdef @!STAT
+incr(dyn_used);
+#endif
+@; /*maintain statistics*/
+return p;
+}
+
+@ Conversely, a one-word node is recycled by calling |free_avail|.
+This routine is part of \TeX's ``inner loop,'' so we want it to be fast.
+@^inner loop@>
+
+@d free_avail(A) /*single-word node liberation*/
+ {@+link(A)=avail;avail=A;
+ decr_dyn_used;
+ }
+
+@ There's also a |fast_get_avail| routine, which saves the procedure-call
+overhead at the expense of extra programming. This routine is used in
+the places that would otherwise account for the most calls of |get_avail|.
+@^inner loop@>
+
+@d fast_get_avail(A) @t@>@;@/
+ {@+A=avail; /*avoid |get_avail| if possible, to save time*/
+ if (A==null) A=get_avail();
+ else{@+avail=link(A);link(A)=null;
+ incr_dyn_used;
+ }
+ }
+
+@ The procedure |flush_list(p)| frees an entire linked list of
+one-word nodes that starts at position |p|.
+@^inner loop@>
+
+@p static void flush_list(pointer @!p) /*makes list of single-word nodes
+ available*/
+{@+pointer @!q, @!r; /*list traversers*/
+if (p!=null)
+ {@+r=p;
+ @/do@+{q=r;r=link(r);
+#ifdef @!STAT
+decr(dyn_used);
+#endif
+ }@+ while (!(r==null)); /*now |q| is the last node on the list*/
+ link(q)=avail;avail=p;
+ }
+}
+
+@ The available-space list that keeps track of the variable-size portion
+of |mem| is a nonempty, doubly-linked circular list of empty nodes,
+pointed to by the roving pointer |rover|.
+
+Each empty node has size 2 or more; the first word contains the special
+value |max_halfword| in its |link| field and the size in its |info| field;
+the second word contains the two pointers for double linking.
+
+Each nonempty node also has size 2 or more. Its first word is of type
+|two_halves|\kern-1pt, and its |link| field is never equal to |max_halfword|.
+Otherwise there is complete flexibility with respect to the contents
+of its other fields and its other words.
+
+(We require |mem_max < max_halfword| because terrible things can happen
+when |max_halfword| appears in the |link| field of a nonempty node.)
+
+@d empty_flag max_halfword /*the |link| of an empty variable-size node*/
+@d is_empty(A) (link(A)==empty_flag) /*tests for empty node*/
+@d node_size(A) info(A) /*the size field in empty variable-size nodes*/
+@d llink(A) info(A+1) /*left link in doubly-linked list of empty nodes*/
+@d rlink(A) link(A+1) /*right link in doubly-linked list of empty nodes*/
+
+@<Glob...@>=
+static pointer @!rover; /*points to some node in the list of empties*/
+
+@ A call to |get_node| with argument |s| returns a pointer to a new node
+of size~|s|, which must be 2~or more. The |link| field of the first word
+of this new node is set to null. An overflow stop occurs if no suitable
+space exists.
+
+If |get_node| is called with $s=2^{30}$, it simply merges adjacent free
+areas and returns the value |max_halfword|.
+
+@p static pointer get_node(int @!s) /*variable-size node allocation*/
+{@+
+pointer p; /*the node currently under inspection*/
+pointer @!q; /*the node physically after node |p|*/
+int @!r; /*the newly allocated node, or a candidate for this honor*/
+int @!t; /*temporary register*/
+restart: p=rover; /*start at some free node in the ring*/
+@/do@+{@<Try to allocate within node |p| and its physical successors, and
+|goto found| if allocation was possible@>;
+@^inner loop@>
+p=rlink(p); /*move to the next node in the ring*/
+}@+ while (!(p==rover)); /*repeat until the whole list has been traversed*/
+if (s==010000000000)
+ {@+return max_halfword;
+ }
+if (lo_mem_max+2 < hi_mem_min) if (lo_mem_max+2 <= mem_bot+max_halfword)
+ @<Grow more variable-size memory and |goto restart|@>;
+overflow("main memory size", mem_max+1-mem_min);
+ /*sorry, nothing satisfactory is left*/
+@:TeX capacity exceeded main memory size}{\quad main memory size@>
+found: link(r)=null; /*this node is now nonempty*/
+#ifdef @!STAT
+var_used=var_used+s; /*maintain usage statistics*/
+#endif
+@;@/
+return r;
+}
+
+@ The lower part of |mem| grows by 1000 words at a time, unless
+we are very close to going under. When it grows, we simply link
+a new node into the available-space list. This method of controlled
+growth helps to keep the |mem| usage consecutive when \TeX\ is
+implemented on ``virtual memory'' systems.
+@^virtual memory@>
+
+@<Grow more variable-size memory and |goto restart|@>=
+{@+if (hi_mem_min-lo_mem_max >= 1998) t=lo_mem_max+1000;
+else t=lo_mem_max+1+(hi_mem_min-lo_mem_max)/2;
+ /*|lo_mem_max+2 <= t < hi_mem_min|*/
+p=llink(rover);q=lo_mem_max;rlink(p)=q;llink(rover)=q;@/
+if (t > mem_bot+max_halfword) t=mem_bot+max_halfword;
+rlink(q)=rover;llink(q)=p;link(q)=empty_flag;node_size(q)=t-lo_mem_max;@/
+lo_mem_max=t;link(lo_mem_max)=null;info(lo_mem_max)=null;
+rover=q;goto restart;
+}
+
+@ Empirical tests show that the routine in this section performs a
+node-merging operation about 0.75 times per allocation, on the average,
+after which it finds that |r > p+1| about 95\pct! of the time.
+
+@<Try to allocate...@>=
+q=p+node_size(p); /*find the physical successor*/
+@^inner loop@>
+while (is_empty(q)) /*merge node |p| with node |q|*/
+ {@+t=rlink(q);
+ if (q==rover) rover=t;
+ llink(t)=llink(q);rlink(llink(q))=t;@/
+ q=q+node_size(q);
+ }
+r=q-s;
+if (r > p+1) @<Allocate from the top of node |p| and |goto found|@>;
+if (r==p) if (rlink(p)!=p)
+ @<Allocate entire node |p| and |goto found|@>;
+node_size(p)=q-p /*reset the size in case it grew*/
+
+@ @<Allocate from the top...@>=
+{@+node_size(p)=r-p; /*store the remaining size*/
+@^inner loop@>
+rover=p; /*start searching here next time*/
+goto found;
+}
+
+@ Here we delete node |p| from the ring, and let |rover| rove around.
+
+@<Allocate entire...@>=
+{@+rover=rlink(p);t=llink(p);
+llink(rover)=t;rlink(t)=rover;
+goto found;
+}
+
+@ Conversely, when some variable-size node |p| of size |s| is no longer needed,
+the operation |free_node(p, s)| will make its words available, by inserting
+|p| as a new empty node just before where |rover| now points.
+@^inner loop@>
+
+@p static void free_node(pointer @!p, halfword @!s) /*variable-size node
+ liberation*/
+{@+pointer q; /*|llink(rover)|*/
+node_size(p)=s;link(p)=empty_flag;
+q=llink(rover);llink(p)=q;rlink(p)=rover; /*set both links*/
+llink(rover)=p;rlink(q)=p; /*insert |p| into the ring*/
+#ifdef @!STAT
+var_used=var_used-s;
+#endif
+@; /*maintain statistics*/
+}
+
+@ Just before \.{INITEX} writes out the memory, it sorts the doubly linked
+available space list. The list is probably very short at such times, so a
+simple insertion sort is used. The smallest available location will be
+pointed to by |rover|, the next-smallest by |rlink(rover)|, etc.
+
+@p
+#ifdef @!INIT
+static void sort_avail(void) /*sorts the available variable-size nodes
+ by location*/
+{@+pointer p, @!q, @!r; /*indices into |mem|*/
+pointer @!old_rover; /*initial |rover| setting*/
+p=get_node(010000000000); /*merge adjacent free areas*/
+p=rlink(rover);rlink(rover)=max_halfword;old_rover=rover;
+while (p!=old_rover) @<Sort \(p)|p| into the list starting at |rover| and
+advance |p| to |rlink(p)|@>;
+p=rover;
+while (rlink(p)!=max_halfword)
+ {@+llink(rlink(p))=p;p=rlink(p);
+ }
+rlink(p)=rover;llink(rover)=p;
+}
+#endif
+
+@ The following |while | loop is guaranteed to
+terminate, since the list that starts at
+|rover| ends with |max_halfword| during the sorting procedure.
+
+@<Sort \(p)|p|...@>=
+if (p < rover)
+ {@+q=p;p=rlink(q);rlink(q)=rover;rover=q;
+ }
+else{@+q=rover;
+ while (rlink(q) < p) q=rlink(q);
+ r=rlink(p);rlink(p)=rlink(q);rlink(q)=p;p=r;
+ }
+
+@* Data structures for boxes and their friends.
+From the computer's standpoint, \TeX's chief mission is to create
+horizontal and vertical lists. We shall now investigate how the elements
+of these lists are represented internally as nodes in the dynamic memory.
+
+A horizontal or vertical list is linked together by |link| fields in
+the first word of each node. Individual nodes represent boxes, glue,
+penalties, or special things like discretionary hyphens; because of this
+variety, some nodes are longer than others, and we must distinguish different
+kinds of nodes. We do this by putting a `|type|' field in the first word,
+together with the link and an optional `|subtype|'.
+
+@d type(A) mem[A].hh.b0 /*identifies what kind of node this is*/
+@d subtype(A) mem[A].hh.b1 /*secondary identification in some cases*/
+
+@ A |@!char_node|, which represents a single character, is the most important
+kind of node because it accounts for the vast majority of all boxes.
+Special precautions are therefore taken to ensure that a |char_node| does
+not take up much memory space. Every such node is one word long, and in fact
+it is identifiable by this property, since other kinds of nodes have at least
+two words, and they appear in |mem| locations less than |hi_mem_min|.
+This makes it possible to omit the |type| field in a |char_node|, leaving
+us room for two bytes that identify a |font| and a |character| within
+that font.
+
+Note that the format of a |char_node| allows for up to 256 different
+fonts and up to 256 characters per font; but most implementations will
+probably limit the total number of fonts to fewer than 75 per job,
+and most fonts will stick to characters whose codes are
+less than 128 (since higher codes
+are more difficult to access on most keyboards).
+
+Extensions of \TeX\ intended for oriental languages will need even more
+than $256\times256$ possible characters, when we consider different sizes
+@^oriental characters@>@^Chinese characters@>@^Japanese characters@>
+and styles of type. It is suggested that Chinese and Japanese fonts be
+handled by representing such characters in two consecutive |char_node|
+entries: The first of these has |font==font_base|, and its |link| points
+to the second;
+the second identifies the font and the character dimensions.
+The saving feature about oriental characters is that most of them have
+the same box dimensions. The |character| field of the first |char_node|
+is a ``\\{charext}'' that distinguishes between graphic symbols whose
+dimensions are identical for typesetting purposes. (See the \MF\ manual.)
+Such an extension of \TeX\ would not be difficult; further details are
+left to the reader.
+
+In order to make sure that the |character| code fits in a quarterword,
+\TeX\ adds the quantity |min_quarterword| to the actual code.
+
+Character nodes appear only in horizontal lists, never in vertical lists.
+
+@d is_char_node(A) (A >= hi_mem_min)
+ /*does the argument point to a |char_node|?*/
+@d font(A) type(A) /*the font code in a |char_node|*/
+@d character(A) subtype(A) /*the character code in a |char_node|*/
+
+@ An |hlist_node| stands for a box that was made from a horizontal list.
+Each |hlist_node| is seven words long, and contains the following fields
+(in addition to the mandatory |type| and |link|, which we shall not
+mention explicitly when discussing the other node types): The |height| and
+|width| and |depth| are scaled integers denoting the dimensions of the
+box. There is also a |shift_amount| field, a scaled integer indicating
+how much this box should be lowered (if it appears in a horizontal list),
+or how much it should be moved to the right (if it appears in a vertical
+list). There is a |list_ptr| field, which points to the beginning of the
+list from which this box was fabricated; if |list_ptr| is |null|, the box
+is empty. Finally, there are three fields that represent the setting of
+the glue: |glue_set(p)| is a word of type |glue_ratio| that represents
+the proportionality constant for glue setting; |glue_sign(p)| is
+|stretching| or |shrinking| or |normal| depending on whether or not the
+glue should stretch or shrink or remain rigid; and |glue_order(p)|
+specifies the order of infinity to which glue setting applies (|normal|,
+|fil|, |fill|, or |filll|). The |subtype| field is not used.
+
+@d hlist_node 0 /*|type| of hlist nodes*/
+@d box_node_size 7 /*number of words to allocate for a box node*/
+@d width_offset 1 /*position of |width| field in a box node*/
+@d depth_offset 2 /*position of |depth| field in a box node*/
+@d height_offset 3 /*position of |height| field in a box node*/
+@d width(A) mem[A+width_offset].sc /*width of the box, in sp*/
+@d depth(A) mem[A+depth_offset].sc /*depth of the box, in sp*/
+@d height(A) mem[A+height_offset].sc /*height of the box, in sp*/
+@d shift_amount(A) mem[A+4].sc /*repositioning distance, in sp*/
+@d list_offset 5 /*position of |list_ptr| field in a box node*/
+@d list_ptr(A) link(A+list_offset) /*beginning of the list inside the box*/
+@d glue_order(A) subtype(A+list_offset) /*applicable order of infinity*/
+@d glue_sign(A) type(A+list_offset) /*stretching or shrinking*/
+@d normal 0 /*the most common case when several cases are named*/
+@d stretching 1 /*glue setting applies to the stretch components*/
+@d shrinking 2 /*glue setting applies to the shrink components*/
+@d glue_offset 6 /*position of |glue_set| in a box node*/
+@d glue_set(A) mem[A+glue_offset].gr
+ /*a word of type |glue_ratio| for glue setting*/
+
+@ The |new_null_box| function returns a pointer to an |hlist_node| in
+which all subfields have the values corresponding to `\.{\\hbox\{\}}'.
+(The |subtype| field is set to |min_quarterword|, for historic reasons
+that are no longer relevant.)
+
+@p static pointer new_null_box(void) /*creates a new box node*/
+{@+pointer p; /*the new node*/
+p=get_node(box_node_size);type(p)=hlist_node;
+subtype(p)=min_quarterword;
+width(p)=0;depth(p)=0;height(p)=0;shift_amount(p)=0;list_ptr(p)=null;
+glue_sign(p)=normal;glue_order(p)=normal;set_glue_ratio_zero(glue_set(p));
+return p;
+}
+
+@ A |vlist_node| is like an |hlist_node| in all respects except that it
+contains a vertical list.
+
+@d vlist_node 1 /*|type| of vlist nodes*/
+
+@ A |rule_node| stands for a solid black rectangle; it has |width|,
+|depth|, and |height| fields just as in an |hlist_node|. However, if
+any of these dimensions is $-2^{30}$, the actual value will be determined
+by running the rule up to the boundary of the innermost enclosing box.
+This is called a ``running dimension.'' The |width| is never running in
+an hlist; the |height| and |depth| are never running in a~vlist.
+
+@d rule_node 2 /*|type| of rule nodes*/
+@d rule_node_size 4 /*number of words to allocate for a rule node*/
+@d null_flag -010000000000 /*$-2^{30}$, signifies a missing item*/
+@d is_running(A) (A==null_flag) /*tests for a running dimension*/
+
+@ A new rule node is delivered by the |new_rule| function. It
+makes all the dimensions ``running,'' so you have to change the
+ones that are not allowed to run.
+
+@p static pointer new_rule(void)
+{@+pointer p; /*the new node*/
+p=get_node(rule_node_size);type(p)=rule_node;
+subtype(p)=0; /*the |subtype| is not used*/
+width(p)=null_flag;depth(p)=null_flag;height(p)=null_flag;
+return p;
+}
+
+@ Insertions are represented by |ins_node| records, where the |subtype|
+indicates the corresponding box number. For example, `\.{\\insert 250}'
+leads to an |ins_node| whose |subtype| is |250+min_quarterword|.
+The |height| field of an |ins_node| is slightly misnamed; it actually holds
+the natural height plus depth of the vertical list being inserted.
+The |depth| field holds the |split_max_depth| to be used in case this
+insertion is split, and the |split_top_ptr| points to the corresponding
+|split_top_skip|. The |float_cost| field holds the |floating_penalty| that
+will be used if this insertion floats to a subsequent page after a
+split insertion of the same class. There is one more field, the
+|ins_ptr|, which points to the beginning of the vlist for the insertion.
+
+@d ins_node 3 /*|type| of insertion nodes*/
+@d ins_node_size 5 /*number of words to allocate for an insertion*/
+@d float_cost(A) mem[A+1].i /*the |floating_penalty| to be used*/
+@d ins_ptr(A) info(A+4) /*the vertical list to be inserted*/
+@d split_top_ptr(A) link(A+4) /*the |split_top_skip| to be used*/
+
+@ A |mark_node| has a |mark_ptr| field that points to the reference count
+of a token list that contains the user's \.{\\mark} text.
+In addition there is a |mark_class| field that contains the mark class.
+
+@d mark_node 4 /*|type| of a mark node*/
+@d small_node_size 2 /*number of words to allocate for most node types*/
+@d mark_ptr(A) link(A+1) /*head of the token list for a mark*/
+@d mark_class(A) info(A+1) /*the mark class*/
+
+@ An |adjust_node|, which occurs only in horizontal lists,
+specifies material that will be moved out into the surrounding
+vertical list; i.e., it is used to implement \TeX's `\.{\\vadjust}'
+operation. The |adjust_ptr| field points to the vlist containing this
+material.
+
+@d adjust_node 5 /*|type| of an adjust node*/
+@d adjust_ptr(A) mem[A+1].i
+ /*vertical list to be moved out of horizontal list*/
+
+@ A |ligature_node|, which occurs only in horizontal lists, specifies
+a character that was fabricated from the interaction of two or more
+actual characters. The second word of the node, which is called the
+|lig_char| word, contains |font| and |character| fields just as in a
+|char_node|. The characters that generated the ligature have not been
+forgotten, since they are needed for diagnostic messages and for
+hyphenation; the |lig_ptr| field points to a linked list of character
+nodes for all original characters that have been deleted. (This list
+might be empty if the characters that generated the ligature were
+retained in other nodes.)
+
+The |subtype| field is 0, plus 2 and/or 1 if the original source of the
+ligature included implicit left and/or right boundaries.
+
+@d ligature_node 6 /*|type| of a ligature node*/
+@d lig_char(A) A+1 /*the word where the ligature is to be found*/
+@d lig_ptr(A) link(lig_char(A)) /*the list of characters*/
+
+@ The |new_ligature| function creates a ligature node having given
+contents of the |font|, |character|, and |lig_ptr| fields. We also have
+a |new_lig_item| function, which returns a two-word node having a given
+|character| field. Such nodes are used for temporary processing as ligatures
+are being created.
+
+@p static pointer new_ligature(quarterword @!f, quarterword @!c, pointer @!q)
+{@+pointer p; /*the new node*/
+p=get_node(small_node_size);type(p)=ligature_node;
+font(lig_char(p))=f;character(lig_char(p))=c;lig_ptr(p)=q;
+subtype(p)=0;return p;
+}
+@#
+static pointer new_lig_item(quarterword @!c)
+{@+pointer p; /*the new node*/
+p=get_node(small_node_size);character(p)=c;lig_ptr(p)=null;
+return p;
+}
+
+@ A |disc_node|, which occurs only in horizontal lists, specifies a
+``dis\-cretion\-ary'' line break. If such a break occurs at node |p|, the text
+that starts at |pre_break(p)| will precede the break, the text that starts at
+|post_break(p)| will follow the break, and text that appears in the next
+|replace_count(p)| nodes will be ignored. For example, an ordinary
+discretionary hyphen, indicated by `\.{\\-}', yields a |disc_node| with
+|pre_break| pointing to a |char_node| containing a hyphen, |post_break==null|,
+and |replace_count==0|. All three of the discretionary texts must be
+lists that consist entirely of character, kern, box, rule, and ligature nodes.
+
+If |pre_break(p)==null|, the |ex_hyphen_penalty| will be charged for this
+break. Otherwise the |hyphen_penalty| will be charged. The texts will
+actually be substituted into the list by the line-breaking algorithm if it
+decides to make the break, and the discretionary node will disappear at
+that time; thus, the output routine sees only discretionaries that were
+not chosen.
+
+@d disc_node 7 /*|type| of a discretionary node*/
+@d replace_count(A) subtype(A) /*how many subsequent nodes to replace*/
+@d pre_break(A) llink(A) /*text that precedes a discretionary break*/
+@d post_break(A) rlink(A) /*text that follows a discretionary break*/
+
+@p static pointer new_disc(void) /*creates an empty |disc_node|*/
+{@+pointer p; /*the new node*/
+p=get_node(small_node_size);type(p)=disc_node;
+replace_count(p)=0;pre_break(p)=null;post_break(p)=null;
+return p;
+}
+
+@ A |whatsit_node| is a wild card reserved for extensions to \TeX. The
+|subtype| field in its first word says what `\\{whatsit}' it is, and
+implicitly determines the node size (which must be 2 or more) and the
+format of the remaining words. When a |whatsit_node| is encountered
+in a list, special actions are invoked; knowledgeable people who are
+careful not to mess up the rest of \TeX\ are able to make \TeX\ do new
+things by adding code at the end of the program. For example, there
+might be a `\TeX nicolor' extension to specify different colors of ink,
+@^extensions to \TeX@>
+and the whatsit node might contain the desired parameters.
+
+The present implementation of \TeX\ treats the features associated with
+`\.{\\write}' and `\.{\\special}' as if they were extensions, in order to
+illustrate how such routines might be coded. We shall defer further
+discussion of extensions until the end of this program.
+
+@d whatsit_node 8 /*|type| of special extension nodes*/
+
+@ A |math_node|, which occurs only in horizontal lists, appears before and
+after mathematical formulas. The |subtype| field is |before| before the
+formula and |after| after it. There is a |width| field, which represents
+the amount of surrounding space inserted by \.{\\mathsurround}.
+
+@d math_node 9 /*|type| of a math node*/
+@d before 0 /*|subtype| for math node that introduces a formula*/
+@d after 1 /*|subtype| for math node that winds up a formula*/
+
+@p static pointer new_math(scaled @!w, small_number @!s)
+{@+pointer p; /*the new node*/
+p=get_node(small_node_size);type(p)=math_node;
+subtype(p)=s;width(p)=w;return p;
+}
+
+@ \TeX\ makes use of the fact that |hlist_node|, |vlist_node|,
+|rule_node|, |ins_node|, |mark_node|, |adjust_node|, |ligature_node|,
+|disc_node|, |whatsit_node|, and |math_node| are at the low end of the
+type codes, by permitting a break at glue in a list if and only if the
+|type| of the previous node is less than |math_node|. Furthermore, a
+node is discarded after a break if its type is |math_node| or~more.
+
+@d precedes_break(A) (type(A) < math_node)
+@d non_discardable(A) (type(A) < math_node)
+
+@ A |glue_node| represents glue in a list. However, it is really only
+a pointer to a separate glue specification, since \TeX\ makes use of the
+fact that many essentially identical nodes of glue are usually present.
+If |p| points to a |glue_node|, |glue_ptr(p)| points to
+another packet of words that specify the stretch and shrink components, etc.
+
+Glue nodes also serve to represent leaders; the |subtype| is used to
+distinguish between ordinary glue (which is called |normal|) and the three
+kinds of leaders (which are called |a_leaders|, |c_leaders|, and |x_leaders|).
+The |leader_ptr| field points to a rule node or to a box node containing the
+leaders; it is set to |null| in ordinary glue nodes.
+
+Many kinds of glue are computed from \TeX's ``skip'' parameters, and
+it is helpful to know which parameter has led to a particular glue node.
+Therefore the |subtype| is set to indicate the source of glue, whenever
+it originated as a parameter. We will be defining symbolic names for the
+parameter numbers later (e.g., |line_skip_code==0|, |baseline_skip_code==1|,
+etc.); it suffices for now to say that the |subtype| of parametric glue
+will be the same as the parameter number, plus~one.
+
+In math formulas there are two more possibilities for the |subtype| in a
+glue node: |mu_glue| denotes an \.{\\mskip} (where the units are scaled \.{mu}
+instead of scaled \.{pt}); and |cond_math_glue| denotes the `\.{\\nonscript}'
+feature that cancels the glue node immediately following if it appears
+in a subscript.
+
+@d glue_node 10 /*|type| of node that points to a glue specification*/
+@d cond_math_glue 98 /*special |subtype| to suppress glue in the next node*/
+@d mu_glue 99 /*|subtype| for math glue*/
+@d a_leaders 100 /*|subtype| for aligned leaders*/
+@d c_leaders 101 /*|subtype| for centered leaders*/
+@d x_leaders 102 /*|subtype| for expanded leaders*/
+@d glue_ptr(A) llink(A) /*pointer to a glue specification*/
+@d leader_ptr(A) rlink(A) /*pointer to box or rule node for leaders*/
+
+@ A glue specification has a halfword reference count in its first word,
+@^reference counts@>
+representing |null| plus the number of glue nodes that point to it (less one).
+Note that the reference count appears in the same position as
+the |link| field in list nodes; this is the field that is initialized
+to |null| when a node is allocated, and it is also the field that is flagged
+by |empty_flag| in empty nodes.
+
+Glue specifications also contain three |scaled| fields, for the |width|,
+|stretch|, and |shrink| dimensions. Finally, there are two one-byte
+fields called |stretch_order| and |shrink_order|; these contain the
+orders of infinity (|normal|, |fil|, |fill|, or |filll|)
+corresponding to the stretch and shrink values.
+
+@d glue_spec_size 4 /*number of words to allocate for a glue specification*/
+@d glue_ref_count(A) link(A) /*reference count of a glue specification*/
+@d stretch(A) mem[A+2].sc /*the stretchability of this glob of glue*/
+@d shrink(A) mem[A+3].sc /*the shrinkability of this glob of glue*/
+@d stretch_order(A) type(A) /*order of infinity for stretching*/
+@d shrink_order(A) subtype(A) /*order of infinity for shrinking*/
+@d fil 1 /*first-order infinity*/
+@d fill 2 /*second-order infinity*/
+@d filll 3 /*third-order infinity*/
+
+@<Types...@>=
+typedef int8_t glue_ord; /*infinity to the 0, 1, 2, or 3 power*/
+
+@ Here is a function that returns a pointer to a copy of a glue spec.
+The reference count in the copy is |null|, because there is assumed
+to be exactly one reference to the new specification.
+
+@p static pointer new_spec(pointer @!p) /*duplicates a glue specification*/
+{@+pointer q; /*the new spec*/
+q=get_node(glue_spec_size);@/
+mem[q]=mem[p];glue_ref_count(q)=null;@/
+width(q)=width(p);stretch(q)=stretch(p);shrink(q)=shrink(p);
+return q;
+}
+
+@ And here's a function that creates a glue node for a given parameter
+identified by its code number; for example,
+|new_param_glue(line_skip_code)| returns a pointer to a glue node for the
+current \.{\\lineskip}.
+
+@p static pointer new_param_glue(small_number @!n)
+{@+pointer p; /*the new node*/
+pointer @!q; /*the glue specification*/
+p=get_node(small_node_size);type(p)=glue_node;subtype(p)=n+1;
+leader_ptr(p)=null;@/
+q=@<Current |mem| equivalent of glue parameter number |n|@>@t@>;
+glue_ptr(p)=q;incr(glue_ref_count(q));
+return p;
+}
+
+@ Glue nodes that are more or less anonymous are created by |new_glue|,
+whose argument points to a glue specification.
+
+@p static pointer new_glue(pointer @!q)
+{@+pointer p; /*the new node*/
+p=get_node(small_node_size);type(p)=glue_node;subtype(p)=normal;
+leader_ptr(p)=null;glue_ptr(p)=q;incr(glue_ref_count(q));
+return p;
+}
+
+@ Still another subroutine is needed: This one is sort of a combination
+of |new_param_glue| and |new_glue|. It creates a glue node for one of
+the current glue parameters, but it makes a fresh copy of the glue
+specification, since that specification will probably be subject to change,
+while the parameter will stay put. The global variable |temp_ptr| is
+set to the address of the new spec.
+
+@p static pointer new_skip_param(small_number @!n)
+{@+pointer p; /*the new node*/
+temp_ptr=new_spec(@<Current |mem| equivalent of glue parameter...@>);
+p=new_glue(temp_ptr);glue_ref_count(temp_ptr)=null;subtype(p)=n+1;
+return p;
+}
+
+@ A |kern_node| has a |width| field to specify a (normally negative)
+amount of spacing. This spacing correction appears in horizontal lists
+between letters like A and V when the font designer said that it looks
+better to move them closer together or further apart. A kern node can
+also appear in a vertical list, when its `|width|' denotes additional
+spacing in the vertical direction. The |subtype| is either |normal| (for
+kerns inserted from font information or math mode calculations) or |explicit|
+(for kerns inserted from \.{\\kern} and \.{\\/} commands) or |acc_kern|
+(for kerns inserted from non-math accents) or |mu_glue| (for kerns
+inserted from \.{\\mkern} specifications in math formulas).
+
+@d kern_node 11 /*|type| of a kern node*/
+@d explicit 1 /*|subtype| of kern nodes from \.{\\kern} and \.{\\/}*/
+@d acc_kern 2 /*|subtype| of kern nodes from accents*/
+
+@ The |new_kern| function creates a kern node having a given width.
+
+@p static pointer new_kern(scaled @!w)
+{@+pointer p; /*the new node*/
+p=get_node(small_node_size);type(p)=kern_node;
+subtype(p)=normal;
+width(p)=w;
+return p;
+}
+
+@ A |penalty_node| specifies the penalty associated with line or page
+breaking, in its |penalty| field. This field is a fullword integer, but
+the full range of integer values is not used: Any penalty | >= 10000| is
+treated as infinity, and no break will be allowed for such high values.
+Similarly, any penalty | <= -10000| is treated as negative infinity, and a
+break will be forced.
+
+@d penalty_node 12 /*|type| of a penalty node*/
+@d inf_penalty inf_bad /*``infinite'' penalty value*/
+@d eject_penalty (-inf_penalty) /*``negatively infinite'' penalty value*/
+@d penalty(A) mem[A+1].i /*the added cost of breaking a list here*/
+
+@ Anyone who has been reading the last few sections of the program will
+be able to guess what comes next.
+
+@p static pointer new_penalty(int @!m)
+{@+pointer p; /*the new node*/
+p=get_node(small_node_size);type(p)=penalty_node;
+subtype(p)=0; /*the |subtype| is not used*/
+penalty(p)=m;return p;
+}
+
+@ You might think that we have introduced enough node types by now. Well,
+almost, but there is one more: An |unset_node| has nearly the same format
+as an |hlist_node| or |vlist_node|; it is used for entries in \.{\\halign}
+or \.{\\valign} that are not yet in their final form, since the box
+dimensions are their ``natural'' sizes before any glue adjustment has been
+made. The |glue_set| word is not present; instead, we have a |glue_stretch|
+field, which contains the total stretch of order |glue_order| that is
+present in the hlist or vlist being boxed.
+Similarly, the |shift_amount| field is replaced by a |glue_shrink| field,
+containing the total shrink of order |glue_sign| that is present.
+The |subtype| field is called |span_count|; an unset box typically
+contains the data for |qo(span_count)+1| columns.
+Unset nodes will be changed to box nodes when alignment is completed.
+
+@d unset_node 13 /*|type| for an unset node*/
+@d glue_stretch(A) mem[A+glue_offset].sc /*total stretch in an unset node*/
+@d glue_shrink(A) shift_amount(A) /*total shrink in an unset node*/
+@d span_count(A) subtype(A) /*indicates the number of spanned columns*/
+
+@ In fact, there are still more types coming. When we get to math formula
+processing we will see that a |style_node| has |type==14|; and a number
+of larger type codes will also be defined, for use in math mode only.
+
+@ Warning: If any changes are made to these data structure layouts, such as
+changing any of the node sizes or even reordering the words of nodes,
+the |copy_node_list| procedure and the memory initialization code
+below may have to be changed. Such potentially dangerous parts of the
+program are listed in the index under `data structure assumptions'.
+@!@^data structure assumptions@>
+However, other references to the nodes are made symbolically in terms of
+the \.{WEB} macro definitions above, so that format changes will leave
+\TeX's other algorithms intact.
+@^system dependencies@>
+
+@* Memory layout.
+Some areas of |mem| are dedicated to fixed usage, since static allocation is
+more efficient than dynamic allocation when we can get away with it. For
+example, locations |mem_bot| to |mem_bot+3| are always used to store the
+specification for glue that is `\.{0pt plus 0pt minus 0pt}'. The
+following macro definitions accomplish the static allocation by giving
+symbolic names to the fixed positions. Static variable-size nodes appear
+in locations |mem_bot| through |lo_mem_stat_max|, and static single-word nodes
+appear in locations |hi_mem_stat_min| through |mem_top|, inclusive. It is
+harmless to let |lig_trick| and |garbage| share the same location of |mem|.
+
+@d zero_glue mem_bot /*specification for \.{0pt plus 0pt minus 0pt}*/
+@d fil_glue zero_glue+glue_spec_size /*\.{0pt plus 1fil minus 0pt}*/
+@d fill_glue fil_glue+glue_spec_size /*\.{0pt plus 1fill minus 0pt}*/
+@d ss_glue fill_glue+glue_spec_size /*\.{0pt plus 1fil minus 1fil}*/
+@d fil_neg_glue ss_glue+glue_spec_size /*\.{0pt plus -1fil minus 0pt}*/
+@d lo_mem_stat_max fil_neg_glue+glue_spec_size-1 /*largest statically
+ allocated word in the variable-size |mem|*/
+@#
+@d page_ins_head mem_top /*list of insertion data for current page*/
+@d contrib_head mem_top-1 /*vlist of items not yet on current page*/
+@d page_head mem_top-2 /*vlist for current page*/
+@d temp_head mem_top-3 /*head of a temporary list of some kind*/
+@d hold_head mem_top-4 /*head of a temporary list of another kind*/
+@d adjust_head mem_top-5 /*head of adjustment list returned by |hpack|*/
+@d active mem_top-7 /*head of active list in |line_break|, needs two words*/
+@d align_head mem_top-8 /*head of preamble list for alignments*/
+@d end_span mem_top-9 /*tail of spanned-width lists*/
+@d omit_template mem_top-10 /*a constant token list*/
+@d null_list mem_top-11 /*permanently empty list*/
+@d lig_trick mem_top-12 /*a ligature masquerading as a |char_node|*/
+@d garbage mem_top-12 /*used for scrap information*/
+@d backup_head mem_top-13 /*head of token list built by |scan_keyword|*/
+@d hi_mem_stat_min mem_top-13 /*smallest statically allocated word in
+ the one-word |mem|*/
+@d hi_mem_stat_usage 14 /*the number of one-word nodes always present*/
+
+@ The following code gets |mem| off to a good start, when \TeX\ is
+initializing itself the slow~way.
+
+@<Local variables for init...@>=
+int @!k; /*index into |mem|, |eqtb|, etc.*/
+
+@ @<Initialize table entries...@>=
+for (k=mem_bot+1; k<=lo_mem_stat_max; k++) mem[k].sc=0;
+ /*all glue dimensions are zeroed*/
+@^data structure assumptions@>
+k=mem_bot;@+while (k <= lo_mem_stat_max)
+ /*set first words of glue specifications*/
+ {@+glue_ref_count(k)=null+1;
+ stretch_order(k)=normal;shrink_order(k)=normal;
+ k=k+glue_spec_size;
+ }
+stretch(fil_glue)=unity;stretch_order(fil_glue)=fil;@/
+stretch(fill_glue)=unity;stretch_order(fill_glue)=fill;@/
+stretch(ss_glue)=unity;stretch_order(ss_glue)=fil;@/
+shrink(ss_glue)=unity;shrink_order(ss_glue)=fil;@/
+stretch(fil_neg_glue)=-unity;stretch_order(fil_neg_glue)=fil;@/
+rover=lo_mem_stat_max+1;
+link(rover)=empty_flag; /*now initialize the dynamic memory*/
+node_size(rover)=1000; /*which is a 1000-word available node*/
+llink(rover)=rover;rlink(rover)=rover;@/
+lo_mem_max=rover+1000;link(lo_mem_max)=null;info(lo_mem_max)=null;@/
+for (k=hi_mem_stat_min; k<=mem_top; k++)
+ mem[k]=mem[lo_mem_max]; /*clear list heads*/
+@<Initialize the special list heads and constant nodes@>;
+avail=null;mem_end=mem_top;
+hi_mem_min=hi_mem_stat_min; /*initialize the one-word memory*/
+var_used=lo_mem_stat_max+1-mem_bot;dyn_used=hi_mem_stat_usage;
+ /*initialize statistics*/
+
+@ If \TeX\ is extended improperly, the |mem| array might get screwed up.
+For example, some pointers might be wrong, or some ``dead'' nodes might not
+have been freed when the last reference to them disappeared. Procedures
+|check_mem| and |search_mem| are available to help diagnose such
+problems. These procedures make use of two arrays called |is_free| and
+|was_free| that are present only if \TeX's debugging routines have
+been included. (You may want to decrease the size of |mem| while you
+@^debugging@>
+are debugging.)
+
+@<Glob...@>=
+#ifdef @!DEBUG
+static bool @!is_free0[mem_max-mem_min+1],
+ *const @!is_free = @!is_free0-mem_min; /*free cells*/
+@t\hskip10pt@>static bool @!was_free0[mem_max-mem_min+1],
+ *const @!was_free = @!was_free0-mem_min;
+ /*previously free cells*/
+@t\hskip10pt@>static pointer @!was_mem_end, @!was_lo_max, @!was_hi_min;
+ /*previous |mem_end|, |lo_mem_max|, and |hi_mem_min|*/
+@t\hskip10pt@>static bool @!panicking; /*do we want to check memory constantly?*/
+#endif
+
+@ @<Set initial...@>=
+#ifdef @!DEBUG
+was_mem_end=mem_min; /*indicate that everything was previously free*/
+was_lo_max=mem_min;was_hi_min=mem_max;
+panicking=false;
+#endif
+
+@ Procedure |check_mem| makes sure that the available space lists of
+|mem| are well formed, and it optionally prints out all locations
+that are reserved now but were free the last time this procedure was called.
+
+@p
+#ifdef @!DEBUG
+static void check_mem(bool @!print_locs)
+{@+ /*loop exits*/
+int p, @!q; /*current locations of interest in |mem|*/
+bool @!clobbered; /*is something amiss?*/
+for (p=mem_min; p<=lo_mem_max; p++) is_free[p]=false; /*you can probably
+ do this faster*/
+for (p=hi_mem_min; p<=mem_end; p++) is_free[p]=false; /*ditto*/
+@<Check single-word |avail| list@>;
+@<Check variable-size |avail| list@>;
+@<Check flags of unavailable nodes@>;
+if (print_locs) @<Print newly busy locations@>;
+for (p=mem_min; p<=lo_mem_max; p++) was_free[p]=is_free[p];
+for (p=hi_mem_min; p<=mem_end; p++) was_free[p]=is_free[p];
+ /*|was_free=is_free| might be faster*/
+was_mem_end=mem_end;was_lo_max=lo_mem_max;was_hi_min=hi_mem_min;
+}
+#endif
+
+@ @<Check single-word...@>=
+p=avail;q=null;clobbered=false;
+while (p!=null)
+ {@+if ((p > mem_end)||(p < hi_mem_min)) clobbered=true;
+ else if (is_free[p]) clobbered=true;
+ if (clobbered)
+ {@+print_nl("AVAIL list clobbered at ");
+@.AVAIL list clobbered...@>
+ print_int(q);goto done1;
+ }
+ is_free[p]=true;q=p;p=link(q);
+ }
+done1:
+
+@ @<Check variable-size...@>=
+p=rover;q=null;clobbered=false;
+@/do@+{if ((p >= lo_mem_max)||(p < mem_min)) clobbered=true;
+ else if ((rlink(p) >= lo_mem_max)||(rlink(p) < mem_min)) clobbered=true;
+ else if (!(is_empty(p))||(node_size(p) < 2)||@|
+ (p+node_size(p) > lo_mem_max)||@|(llink(rlink(p))!=p)) clobbered=true;
+ if (clobbered)
+ {@+print_nl("Double-AVAIL list clobbered at ");
+ print_int(q);goto done2;
+ }
+for (q=p; q<=p+node_size(p)-1; q++) /*mark all locations free*/
+ {@+if (is_free[q])
+ {@+print_nl("Doubly free location at ");
+@.Doubly free location...@>
+ print_int(q);goto done2;
+ }
+ is_free[q]=true;
+ }
+q=p;p=rlink(p);
+}@+ while (!(p==rover));
+done2:
+
+@ @<Check flags...@>=
+p=mem_min;
+while (p <= lo_mem_max) /*node |p| should not be empty*/
+ {@+if (is_empty(p))
+ {@+print_nl("Bad flag at ");print_int(p);
+@.Bad flag...@>
+ }
+ while ((p <= lo_mem_max)&&!is_free[p]) incr(p);
+ while ((p <= lo_mem_max)&&is_free[p]) incr(p);
+ }
+
+@ @<Print newly busy...@>=
+{@+print_nl("New busy locs:");
+for (p=mem_min; p<=lo_mem_max; p++)
+ if (!is_free[p]&&((p > was_lo_max)||was_free[p]))
+ {@+print_char(' ');print_int(p);
+ }
+for (p=hi_mem_min; p<=mem_end; p++)
+ if (!is_free[p]&&
+ ((p < was_hi_min)||(p > was_mem_end)||was_free[p]))
+ {@+print_char(' ');print_int(p);
+ }
+}
+
+@ The |search_mem| procedure attempts to answer the question ``Who points
+to node~|p|?'' In doing so, it fetches |link| and |info| fields of |mem|
+that might not be of type |two_halves|. Strictly speaking, this is
+@^dirty \PASCAL@>
+undefined in \PASCAL, and it can lead to ``false drops'' (words that seem to
+point to |p| purely by coincidence). But for debugging purposes, we want
+to rule out the places that do {\sl not\/} point to |p|, so a few false
+drops are tolerable.
+
+@p
+#ifdef @!DEBUG
+static void search_mem(pointer @!p) /*look for pointers to |p|*/
+{@+int q; /*current position being searched*/
+for (q=mem_min; q<=lo_mem_max; q++)
+ {@+if (link(q)==p)
+ {@+print_nl("LINK(");print_int(q);print_char(')');
+ }
+ if (info(q)==p)
+ {@+print_nl("INFO(");print_int(q);print_char(')');
+ }
+ }
+for (q=hi_mem_min; q<=mem_end; q++)
+ {@+if (link(q)==p)
+ {@+print_nl("LINK(");print_int(q);print_char(')');
+ }
+ if (info(q)==p)
+ {@+print_nl("INFO(");print_int(q);print_char(')');
+ }
+ }
+@<Search |eqtb| for equivalents equal to |p|@>;
+@<Search |save_stack| for equivalents that point to |p|@>;
+@<Search |hyph_list| for pointers to |p|@>;
+}
+#endif
+
+@* Displaying boxes.
+We can reinforce our knowledge of the data structures just introduced
+by considering two procedures that display a list in symbolic form.
+The first of these, called |short_display|, is used in ``overfull box''
+messages to give the top-level description of a list. The other one,
+called |show_node_list|, prints a detailed description of exactly what
+is in the data structure.
+
+The philosophy of |short_display| is to ignore the fine points about exactly
+what is inside boxes, except that ligatures and discretionary breaks are
+expanded. As a result, |short_display| is a recursive procedure, but the
+recursion is never more than one level deep.
+@^recursion@>
+
+A global variable |font_in_short_display| keeps track of the font code that
+is assumed to be present when |short_display| begins; deviations from this
+font will be printed.
+
+@<Glob...@>=
+static int @!font_in_short_display; /*an internal font number*/
+
+@ Boxes, rules, inserts, whatsits, marks, and things in general that are
+sort of ``complicated'' are indicated only by printing `\.{[]}'.
+
+@p static void short_display(int @!p) /*prints highlights of list |p|*/
+{@+int n; /*for replacement counts*/
+while (p > mem_min)
+ {@+if (is_char_node(p))
+ {@+if (p <= mem_end)
+ {@+if (font(p)!=font_in_short_display)
+ {@+if ((font(p) < font_base)||(font(p) > font_max))
+ print_char('*');
+@.*\relax@>
+ else@<Print the font identifier for |font(p)|@>;
+ print_char(' ');font_in_short_display=font(p);
+ }
+ print_ASCII(qo(character(p)));
+ }
+ }
+ else@<Print a short indication of the contents of node |p|@>;
+ p=link(p);
+ }
+}
+
+@ @<Print a short indication of the contents of node |p|@>=
+switch (type(p)) {
+case hlist_node: case vlist_node: case ins_node:
+ case whatsit_node: case mark_node: case adjust_node:
+ case unset_node: print("[]");@+break;
+case rule_node: print_char('|');@+break;
+case glue_node: if (glue_ptr(p)!=zero_glue) print_char(' ');@+break;
+case math_node: print_char('$');@+break;
+case ligature_node: short_display(lig_ptr(p));@+break;
+case disc_node: {@+short_display(pre_break(p));
+ short_display(post_break(p));@/
+ n=replace_count(p);
+ while (n > 0)
+ {@+if (link(p)!=null) p=link(p);
+ decr(n);
+ }
+ } @+break;
+default:do_nothing;
+}
+
+@ The |show_node_list| routine requires some auxiliary subroutines: one to
+print a font-and-character combination, one to print a token list without
+its reference count, and one to print a rule dimension.
+
+@p static void print_font_and_char(int @!p) /*prints |char_node| data*/
+{@+if (p > mem_end) print_esc("CLOBBERED.");
+else{@+if ((font(p) < font_base)||(font(p) > font_max)) print_char('*');
+@.*\relax@>
+ else@<Print the font identifier for |font(p)|@>;
+ print_char(' ');print_ASCII(qo(character(p)));
+ }
+}
+@#
+static void print_mark(int @!p) /*prints token list data in braces*/
+{@+print_char('{');
+if ((p < hi_mem_min)||(p > mem_end)) print_esc("CLOBBERED.");
+else show_token_list(link(p), null, max_print_line-10);
+print_char('}');
+}
+@#
+static void print_rule_dimen(scaled @!d) /*prints dimension in rule node*/
+{@+if (is_running(d)) print_char('*');else print_scaled(d);
+@.*\relax@>
+}
+
+@ Then there is a subroutine that prints glue stretch and shrink, possibly
+followed by the name of finite units:
+
+@p static void print_glue(scaled @!d, int @!order, char *@!s)
+ /*prints a glue component*/
+{@+print_scaled(d);
+if ((order < normal)||(order > filll)) print("foul");
+else if (order > normal)
+ {@+print("fil");
+ while (order > fil)
+ {@+print_char('l');decr(order);
+ }
+ }
+else if (s!=0) print(s);
+}
+
+@ The next subroutine prints a whole glue specification.
+
+@p static void print_spec(int @!p, char *@!s)
+ /*prints a glue specification*/
+{@+if ((p < mem_min)||(p >= lo_mem_max)) print_char('*');
+@.*\relax@>
+else{@+print_scaled(width(p));
+ if (s!=0) print(s);
+ if (stretch(p)!=0)
+ {@+print(" plus ");print_glue(stretch(p), stretch_order(p), s);
+ }
+ if (shrink(p)!=0)
+ {@+print(" minus ");print_glue(shrink(p), shrink_order(p), s);
+ }
+ }
+}
+
+@ We also need to declare some procedures that appear later in this
+documentation.
+
+@p @<Declare procedures needed for displaying the elements of mlists@>@;
+@<Declare the procedure called |print_skip_param|@>@;
+
+@ Since boxes can be inside of boxes, |show_node_list| is inherently recursive,
+@^recursion@>
+up to a given maximum number of levels. The history of nesting is indicated
+by the current string, which will be printed at the beginning of each line;
+the length of this string, namely |cur_length|, is the depth of nesting.
+
+Recursive calls on |show_node_list| therefore use the following pattern:
+
+@d node_list_display(A)
+ {@+append_char('.');show_node_list(A);flush_char;
+ } /*|str_room| need not be checked; see |show_box| below*/
+
+@ A global variable called |depth_threshold| is used to record the maximum
+depth of nesting for which |show_node_list| will show information. If we
+have |depth_threshold==0|, for example, only the top level information will
+be given and no sublists will be traversed. Another global variable, called
+|breadth_max|, tells the maximum number of items to show at each level;
+|breadth_max| had better be positive, or you won't see anything.
+
+@<Glob...@>=
+static int @!depth_threshold; /*maximum nesting depth in box displays*/
+static int @!breadth_max; /*maximum number of items shown at the same list level*/
+
+@ Now we are ready for |show_node_list| itself. This procedure has been
+written to be ``extra robust'' in the sense that it should not crash or get
+into a loop even if the data structures have been messed up by bugs in
+the rest of the program. You can safely call its parent routine
+|show_box(p)| for arbitrary values of |p| when you are debugging \TeX.
+However, in the presence of bad data, the procedure may
+@^dirty \PASCAL@>@^debugging@>
+fetch a |memory_word| whose variant is different from the way it was stored;
+for example, it might try to read |mem[p].hh| when |mem[p]|
+contains a scaled integer, if |p| is a pointer that has been
+clobbered or chosen at random.
+
+@p static void show_node_list(int @!p) /*prints a node list symbolically*/
+{@+
+int n; /*the number of items already printed at this level*/
+double @!g; /*a glue ratio, as a floating point number*/
+if (cur_length > depth_threshold)
+ {@+if (p > null) print(" []");
+ /*indicate that there's been some truncation*/
+ return;
+ }
+n=0;
+while (p > mem_min)
+ {@+print_ln();print_current_string(); /*display the nesting history*/
+ if (p > mem_end) /*pointer out of range*/
+ {@+print("Bad link, display aborted.");return;
+@.Bad link...@>
+ }
+ incr(n);if (n > breadth_max) /*time to stop*/
+ {@+print("etc.");return;
+@.etc@>
+ }
+ @<Display node |p|@>;
+ p=link(p);
+ }
+
+}
+
+@ @<Display node |p|@>=
+if (is_char_node(p)) print_font_and_char(p);
+else switch (type(p)) {
+ case hlist_node: case vlist_node: case unset_node: @<Display box |p|@>@;@+break;
+ case rule_node: @<Display rule |p|@>@;@+break;
+ case ins_node: @<Display insertion |p|@>@;@+break;
+ case whatsit_node: @<Display the whatsit node |p|@>@;@+break;
+ case glue_node: @<Display glue |p|@>@;@+break;
+ case kern_node: @<Display kern |p|@>@;@+break;
+ case math_node: @<Display math node |p|@>@;@+break;
+ case ligature_node: @<Display ligature |p|@>@;@+break;
+ case penalty_node: @<Display penalty |p|@>@;@+break;
+ case disc_node: @<Display discretionary |p|@>@;@+break;
+ case mark_node: @<Display mark |p|@>@;@+break;
+ case adjust_node: @<Display adjustment |p|@>@;@+break;
+ @t\4@>@<Cases of |show_node_list| that arise in mlists only@>@;
+ default:print("Unknown node type!");
+ }
+
+@ @<Display box |p|@>=
+{@+if (type(p)==hlist_node) print_esc("h");
+else if (type(p)==vlist_node) print_esc("v");
+else print_esc("unset");
+print("box(");print_scaled(height(p));print_char('+');
+print_scaled(depth(p));print(")x");print_scaled(width(p));
+if (type(p)==unset_node)
+ @<Display special fields of the unset node |p|@>@;
+else{@+@<Display the value of |glue_set(p)|@>;
+ if (shift_amount(p)!=0)
+ {@+print(", shifted ");print_scaled(shift_amount(p));
+ }
+ }
+node_list_display(list_ptr(p)); /*recursive call*/
+}
+
+@ @<Display special fields of the unset node |p|@>=
+{@+if (span_count(p)!=min_quarterword)
+ {@+print(" (");print_int(qo(span_count(p))+1);
+ print(" columns)");
+ }
+if (glue_stretch(p)!=0)
+ {@+print(", stretch ");print_glue(glue_stretch(p), glue_order(p), 0);
+ }
+if (glue_shrink(p)!=0)
+ {@+print(", shrink ");print_glue(glue_shrink(p), glue_sign(p), 0);
+ }
+}
+
+@ The code will have to change in this place if |glue_ratio| is
+a structured type instead of an ordinary |double|. Note that this routine
+should avoid arithmetic errors even if the |glue_set| field holds an
+arbitrary random value. The following code assumes that a properly
+formed nonzero |double| number has absolute value $2^{20}$ or more when
+it is regarded as an integer; this precaution was adequate to prevent
+floating point underflow on the author's computer.
+@^system dependencies@>
+@^dirty \PASCAL@>
+
+@<Display the value of |glue_set(p)|@>=
+g=unfix(glue_set(p));
+if ((g!=float_constant(0))&&(glue_sign(p)!=normal))
+ {@+print(", glue set ");
+ if (glue_sign(p)==shrinking) print("- ");
+ if (abs(mem[p+glue_offset].i) < 04000000) print("?.?");
+ else if (abs(g) > float_constant(20000))
+ {@+if (g > float_constant(0)) print_char('>');
+ else print("< -");
+ print_glue(20000*unity, glue_order(p), 0);
+ }
+ else print_glue(round(unity*g), glue_order(p), 0);
+@^real multiplication@>
+ }
+
+@ @<Display rule |p|@>=
+{@+print_esc("rule(");print_rule_dimen(height(p));print_char('+');
+print_rule_dimen(depth(p));print(")x");print_rule_dimen(width(p));
+}
+
+@ @<Display insertion |p|@>=
+{@+print_esc("insert");print_int(qo(subtype(p)));
+print(", natural size ");print_scaled(height(p));
+print("; split(");print_spec(split_top_ptr(p), 0);
+print_char(',');print_scaled(depth(p));
+print("); float cost ");print_int(float_cost(p));
+node_list_display(ins_ptr(p)); /*recursive call*/
+}
+
+@ @<Display glue |p|@>=
+if (subtype(p) >= a_leaders) @<Display leaders |p|@>@;
+else{@+print_esc("glue");
+ if (subtype(p)!=normal)
+ {@+print_char('(');
+ if (subtype(p) < cond_math_glue)
+ print_skip_param(subtype(p)-1);
+ else if (subtype(p)==cond_math_glue) print_esc("nonscript");
+ else print_esc("mskip");
+ print_char(')');
+ }
+ if (subtype(p)!=cond_math_glue)
+ {@+print_char(' ');
+ if (subtype(p) < cond_math_glue) print_spec(glue_ptr(p), 0);
+ else print_spec(glue_ptr(p),"mu");
+ }
+ }
+
+@ @<Display leaders |p|@>=
+{@+print_esc("");
+if (subtype(p)==c_leaders) print_char('c');
+else if (subtype(p)==x_leaders) print_char('x');
+print("leaders ");print_spec(glue_ptr(p), 0);
+node_list_display(leader_ptr(p)); /*recursive call*/
+}
+
+@ An ``explicit'' kern value is indicated implicitly by an explicit space.
+
+@<Display kern |p|@>=
+if (subtype(p)!=mu_glue)
+ {@+print_esc("kern");
+ if (subtype(p)!=normal) print_char(' ');
+ print_scaled(width(p));
+ if (subtype(p)==acc_kern) print(" (for accent)");
+@.for accent@>
+ }
+else{@+print_esc("mkern");print_scaled(width(p));print("mu");
+ }
+
+@ @<Display math node |p|@>=
+{@+print_esc("math");
+if (subtype(p)==before) print("on");
+else print("off");
+if (width(p)!=0)
+ {@+print(", surrounded ");print_scaled(width(p));
+ }
+}
+
+@ @<Display ligature |p|@>=
+{@+print_font_and_char(lig_char(p));print(" (ligature ");
+if (subtype(p) > 1) print_char('|');
+font_in_short_display=font(lig_char(p));short_display(lig_ptr(p));
+if (odd(subtype(p))) print_char('|');
+print_char(')');
+}
+
+@ @<Display penalty |p|@>=
+{@+print_esc("penalty ");print_int(penalty(p));
+}
+
+@ The |post_break| list of a discretionary node is indicated by a prefixed
+`\.{\char'174}' instead of the `\..' before the |pre_break| list.
+
+@<Display discretionary |p|@>=
+{@+print_esc("discretionary");
+if (replace_count(p) > 0)
+ {@+print(" replacing ");print_int(replace_count(p));
+ }
+node_list_display(pre_break(p)); /*recursive call*/
+append_char('|');show_node_list(post_break(p));flush_char; /*recursive call*/
+}
+
+@ @<Display mark |p|@>=
+{@+print_esc("mark");
+if (mark_class(p)!=0)
+ {@+print_char('s');print_int(mark_class(p));
+ }
+print_mark(mark_ptr(p));
+}
+
+@ @<Display adjustment |p|@>=
+{@+print_esc("vadjust");node_list_display(adjust_ptr(p)); /*recursive call*/
+}
+
+@ The recursive machinery is started by calling |show_box|.
+@^recursion@>
+
+@p static void show_box(pointer @!p)
+{@+@<Assign the values |depth_threshold:=show_box_depth| and |breadth_max:=show_box_breadth|@>;
+if (breadth_max <= 0) breadth_max=5;
+if (pool_ptr+depth_threshold >= pool_size)
+ depth_threshold=pool_size-pool_ptr-1;
+ /*now there's enough room for prefix string*/
+show_node_list(p); /*the show starts at |p|*/
+print_ln();
+}
+
+@* Destroying boxes.
+When we are done with a node list, we are obliged to return it to free
+storage, including all of its sublists. The recursive procedure
+|flush_node_list| does this for us.
+
+@ First, however, we shall consider two non-recursive procedures that do
+simpler tasks. The first of these, |delete_token_ref|, is called when
+a pointer to a token list's reference count is being removed. This means
+that the token list should disappear if the reference count was |null|,
+otherwise the count should be decreased by one.
+@^reference counts@>
+
+@d token_ref_count(A) info(A) /*reference count preceding a token list*/
+
+@p static void delete_token_ref(pointer @!p) /*|p| points to the reference count
+ of a token list that is losing one reference*/
+{@+if (token_ref_count(p)==null) flush_list(p);
+else decr(token_ref_count(p));
+}
+
+@ Similarly, |delete_glue_ref| is called when a pointer to a glue
+specification is being withdrawn.
+@^reference counts@>
+@d fast_delete_glue_ref(A) @t@>@;@/
+ {@+if (glue_ref_count(A)==null) free_node(A, glue_spec_size);
+ else decr(glue_ref_count(A));
+ }
+
+@p static void delete_glue_ref(pointer @!p) /*|p| points to a glue specification*/
+fast_delete_glue_ref(p)
+
+@ Now we are ready to delete any node list, recursively.
+In practice, the nodes deleted are usually charnodes (about 2/3 of the time),
+and they are glue nodes in about half of the remaining cases.
+@^recursion@>
+
+@p static void flush_node_list(pointer @!p) /*erase list of nodes starting at |p|*/
+{@+ /*go here when node |p| has been freed*/
+pointer q; /*successor to node |p|*/
+while (p!=null)
+@^inner loop@>
+ {@+q=link(p);
+ if (is_char_node(p)) free_avail(p)@;
+ else{@+switch (type(p)) {
+ case hlist_node: case vlist_node:
+ case unset_node: {@+flush_node_list(list_ptr(p));
+ free_node(p, box_node_size);goto done;
+ }
+ case rule_node: {@+free_node(p, rule_node_size);goto done;
+ }
+ case ins_node: {@+flush_node_list(ins_ptr(p));
+ delete_glue_ref(split_top_ptr(p));
+ free_node(p, ins_node_size);goto done;
+ }
+ case whatsit_node: @<Wipe out the whatsit node |p| and |goto done|@>@;
+ case glue_node: {@+fast_delete_glue_ref(glue_ptr(p));
+ if (leader_ptr(p)!=null) flush_node_list(leader_ptr(p));
+ } @+break;
+ case kern_node: case math_node: case penalty_node: do_nothing;@+break;
+ case ligature_node: flush_node_list(lig_ptr(p));@+break;
+ case mark_node: delete_token_ref(mark_ptr(p));@+break;
+ case disc_node: {@+flush_node_list(pre_break(p));
+ flush_node_list(post_break(p));
+ } @+break;
+ case adjust_node: flush_node_list(adjust_ptr(p));@+break;
+ @t\4@>@<Cases of |flush_node_list| that arise in mlists only@>@;
+ default:confusion("flushing");
+@:this can't happen flushing}{\quad flushing@>
+ } @/
+ free_node(p, small_node_size);
+ done: ;}
+ p=q;
+ }
+}
+
+@* Copying boxes.
+Another recursive operation that acts on boxes is sometimes needed: The
+procedure |copy_node_list| returns a pointer to another node list that has
+the same structure and meaning as the original. Note that since glue
+specifications and token lists have reference counts, we need not make
+copies of them. Reference counts can never get too large to fit in a
+halfword, since each pointer to a node is in a different memory address,
+and the total number of memory addresses fits in a halfword.
+@^recursion@>
+@^reference counts@>
+
+(Well, there actually are also references from outside |mem|; if the
+|save_stack| is made arbitrarily large, it would theoretically be possible
+to break \TeX\ by overflowing a reference count. But who would want to do that?)
+
+@d add_token_ref(A) incr(token_ref_count(A)) /*new reference to a token list*/
+@d add_glue_ref(A) incr(glue_ref_count(A)) /*new reference to a glue spec*/
+
+@ The copying procedure copies words en masse without bothering
+to look at their individual fields. If the node format changes---for
+example, if the size is altered, or if some link field is moved to another
+relative position---then this code may need to be changed too.
+@^data structure assumptions@>
+
+@p static pointer copy_node_list(pointer @!p) /*makes a duplicate of the
+ node list that starts at |p| and returns a pointer to the new list*/
+{@+pointer h; /*temporary head of copied list*/
+pointer @!q; /*previous position in new list*/
+pointer @!r; /*current node being fabricated for new list*/
+int @!words; /*number of words remaining to be copied*/
+h=get_avail();q=h;
+while (p!=null)
+ {@+@<Make a copy of node |p| in node |r|@>;
+ link(q)=r;q=r;p=link(p);
+ }
+link(q)=null;q=link(h);free_avail(h);
+return q;
+}
+
+@ @<Make a copy of node |p|...@>=
+words=1; /*this setting occurs in more branches than any other*/
+if (is_char_node(p)) r=get_avail();
+else@<Case statement to copy different types and set |words| to the number
+of initial words not yet copied@>;
+while (words > 0)
+ {@+decr(words);mem[r+words]=mem[p+words];
+ }
+
+@ @<Case statement to copy...@>=
+switch (type(p)) {
+case hlist_node: case vlist_node: case unset_node: {@+r=get_node(box_node_size);
+ mem[r+6]=mem[p+6];mem[r+5]=mem[p+5]; /*copy the last two words*/
+ list_ptr(r)=copy_node_list(list_ptr(p)); /*this affects |mem[r+5]|*/
+ words=5;
+ } @+break;
+case rule_node: {@+r=get_node(rule_node_size);words=rule_node_size;
+ } @+break;
+case ins_node: {@+r=get_node(ins_node_size);mem[r+4]=mem[p+4];
+ add_glue_ref(split_top_ptr(p));
+ ins_ptr(r)=copy_node_list(ins_ptr(p)); /*this affects |mem[r+4]|*/
+ words=ins_node_size-1;
+ } @+break;
+case whatsit_node: @<Make a partial copy of the whatsit node |p| and make
+|r| point to it; set |words| to the number of initial words not yet copied@>@;@+break;
+case glue_node: {@+r=get_node(small_node_size);add_glue_ref(glue_ptr(p));
+ glue_ptr(r)=glue_ptr(p);leader_ptr(r)=copy_node_list(leader_ptr(p));
+ } @+break;
+case kern_node: case math_node: case penalty_node: {@+r=get_node(small_node_size);
+ words=small_node_size;
+ } @+break;
+case ligature_node: {@+r=get_node(small_node_size);
+ mem[lig_char(r)]=mem[lig_char(p)]; /*copy |font| and |character|*/
+ lig_ptr(r)=copy_node_list(lig_ptr(p));
+ } @+break;
+case disc_node: {@+r=get_node(small_node_size);
+ pre_break(r)=copy_node_list(pre_break(p));
+ post_break(r)=copy_node_list(post_break(p));
+ } @+break;
+case mark_node: {@+r=get_node(small_node_size);add_token_ref(mark_ptr(p));
+ words=small_node_size;
+ } @+break;
+case adjust_node: {@+r=get_node(small_node_size);
+ adjust_ptr(r)=copy_node_list(adjust_ptr(p));
+ } @+break; /*|words==1==small_node_size-1|*/
+default:confusion("copying");
+@:this can't happen copying}{\quad copying@>
+}
+
+@* The command codes.
+Before we can go any further, we need to define symbolic names for the internal
+code numbers that represent the various commands obeyed by \TeX. These codes
+are somewhat arbitrary, but not completely so. For example, the command
+codes for character types are fixed by the language, since a user says,
+e.g., `\.{\\catcode \`\\\${} = 3}' to make \.{\char'44} a math delimiter,
+and the command code |math_shift| is equal to~3. Some other codes have
+been made adjacent so that |case| statements in the program need not consider
+cases that are widely spaced, or so that |case| statements can be replaced
+by |if| statements.
+
+At any rate, here is the list, for future reference. First come the
+``catcode'' commands, several of which share their numeric codes with
+ordinary commands when the catcode cannot emerge from \TeX's scanning routine.
+
+@d escape 0 /*escape delimiter (called \.\\ in {\sl The \TeX book\/})*/
+@:TeXbook}{\sl The \TeX book@>
+@d relax 0 /*do nothing ( \.{\\relax} )*/
+@d left_brace 1 /*beginning of a group ( \.\{ )*/
+@d right_brace 2 /*ending of a group ( \.\} )*/
+@d math_shift 3 /*mathematics shift character ( \.\$ )*/
+@d tab_mark 4 /*alignment delimiter ( \.\&, \.{\\span} )*/
+@d car_ret 5 /*end of line ( |carriage_return|, \.{\\cr}, \.{\\crcr} )*/
+@d out_param 5 /*output a macro parameter*/
+@d mac_param 6 /*macro parameter symbol ( \.\# )*/
+@d sup_mark 7 /*superscript ( \.{\char'136} )*/
+@d sub_mark 8 /*subscript ( \.{\char'137} )*/
+@d ignore 9 /*characters to ignore ( \.{\^\^@@} )*/
+@d endv 9 /*end of \<v_j> list in alignment template*/
+@d spacer 10 /*characters equivalent to blank space ( \.{\ } )*/
+@d letter 11 /*characters regarded as letters ( \.{A..Z}, \.{a..z} )*/
+@d other_char 12 /*none of the special character types*/
+@d active_char 13 /*characters that invoke macros ( \.{\char`\~} )*/
+@d par_end 13 /*end of paragraph ( \.{\\par} )*/
+@d match 13 /*match a macro parameter*/
+@d comment 14 /*characters that introduce comments ( \.\% )*/
+@d end_match 14 /*end of parameters to macro*/
+@d stop 14 /*end of job ( \.{\\end}, \.{\\dump} )*/
+@d invalid_char 15 /*characters that shouldn't appear ( \.{\^\^?} )*/
+@d delim_num 15 /*specify delimiter numerically ( \.{\\delimiter} )*/
+@d max_char_code 15 /*largest catcode for individual characters*/
+
+@ Next are the ordinary run-of-the-mill command codes. Codes that are
+|min_internal| or more represent internal quantities that might be
+expanded by `\.{\\the}'.
+
+@d char_num 16 /*character specified numerically ( \.{\\char} )*/
+@d math_char_num 17 /*explicit math code ( \.{\\mathchar} )*/
+@d mark 18 /*mark definition ( \.{\\mark} )*/
+@d xray 19 /*peek inside of \TeX\ ( \.{\\show}, \.{\\showbox}, etc.~)*/
+@d make_box 20 /*make a box ( \.{\\box}, \.{\\copy}, \.{\\hbox}, etc.~)*/
+@d hmove 21 /*horizontal motion ( \.{\\moveleft}, \.{\\moveright} )*/
+@d vmove 22 /*vertical motion ( \.{\\raise}, \.{\\lower} )*/
+@d un_hbox 23 /*unglue a box ( \.{\\unhbox}, \.{\\unhcopy} )*/
+@d un_vbox 24 /*unglue a box ( \.{\\unvbox}, \.{\\unvcopy} )*/
+ /*( or \.{\\pagediscards}, \.{\\splitdiscards} )*/
+@d remove_item 25 /*nullify last item ( \.{\\unpenalty},
+ \.{\\unkern}, \.{\\unskip} )*/
+@d hskip 26 /*horizontal glue ( \.{\\hskip}, \.{\\hfil}, etc.~)*/
+@d vskip 27 /*vertical glue ( \.{\\vskip}, \.{\\vfil}, etc.~)*/
+@d mskip 28 /*math glue ( \.{\\mskip} )*/
+@d kern 29 /*fixed space ( \.{\\kern} )*/
+@d mkern 30 /*math kern ( \.{\\mkern} )*/
+@d leader_ship 31 /*use a box ( \.{\\shipout}, \.{\\leaders}, etc.~)*/
+@d halign 32 /*horizontal table alignment ( \.{\\halign} )*/
+@d valign 33 /*vertical table alignment ( \.{\\valign} )*/
+@d no_align 34 /*temporary escape from alignment ( \.{\\noalign} )*/
+@d vrule 35 /*vertical rule ( \.{\\vrule} )*/
+@d hrule 36 /*horizontal rule ( \.{\\hrule} )*/
+@d insert 37 /*vlist inserted in box ( \.{\\insert} )*/
+@d vadjust 38 /*vlist inserted in enclosing paragraph ( \.{\\vadjust} )*/
+@d ignore_spaces 39 /*gobble |spacer| tokens ( \.{\\ignorespaces} )*/
+@d after_assignment 40 /*save till assignment is done ( \.{\\afterassignment} )*/
+@d after_group 41 /*save till group is done ( \.{\\aftergroup} )*/
+@d break_penalty 42 /*additional badness ( \.{\\penalty} )*/
+@d start_par 43 /*begin paragraph ( \.{\\indent}, \.{\\noindent} )*/
+@d ital_corr 44 /*italic correction ( \.{\\/} )*/
+@d accent 45 /*attach accent in text ( \.{\\accent} )*/
+@d math_accent 46 /*attach accent in math ( \.{\\mathaccent} )*/
+@d discretionary 47 /*discretionary texts ( \.{\\-}, \.{\\discretionary} )*/
+@d eq_no 48 /*equation number ( \.{\\eqno}, \.{\\leqno} )*/
+@d left_right 49 /*variable delimiter ( \.{\\left}, \.{\\right} )*/
+ /*( or \.{\\middle} )*/
+@d math_comp 50 /*component of formula ( \.{\\mathbin}, etc.~)*/
+@d limit_switch 51 /*diddle limit conventions ( \.{\\displaylimits}, etc.~)*/
+@d above 52 /*generalized fraction ( \.{\\above}, \.{\\atop}, etc.~)*/
+@d math_style 53 /*style specification ( \.{\\displaystyle}, etc.~)*/
+@d math_choice 54 /*choice specification ( \.{\\mathchoice} )*/
+@d non_script 55 /*conditional math glue ( \.{\\nonscript} )*/
+@d vcenter 56 /*vertically center a vbox ( \.{\\vcenter} )*/
+@d case_shift 57 /*force specific case ( \.{\\lowercase}, \.{\\uppercase}~)*/
+@d message 58 /*send to user ( \.{\\message}, \.{\\errmessage} )*/
+@d extension 59 /*extensions to \TeX\ ( \.{\\write}, \.{\\special}, etc.~)*/
+@d in_stream 60 /*files for reading ( \.{\\openin}, \.{\\closein} )*/
+@d begin_group 61 /*begin local grouping ( \.{\\begingroup} )*/
+@d end_group 62 /*end local grouping ( \.{\\endgroup} )*/
+@d omit 63 /*omit alignment template ( \.{\\omit} )*/
+@d ex_space 64 /*explicit space ( \.{\\\ } )*/
+@d no_boundary 65 /*suppress boundary ligatures ( \.{\\noboundary} )*/
+@d radical 66 /*square root and similar signs ( \.{\\radical} )*/
+@d end_cs_name 67 /*end control sequence ( \.{\\endcsname} )*/
+@d min_internal 68 /*the smallest code that can follow \.{\\the}*/
+@d char_given 68 /*character code defined by \.{\\chardef}*/
+@d math_given 69 /*math code defined by \.{\\mathchardef}*/
+@d last_item 70 /*most recent item ( \.{\\lastpenalty},
+ \.{\\lastkern}, \.{\\lastskip} )*/
+@d max_non_prefixed_command 70 /*largest command code that can't be \.{\\global}*/
+
+@ The next codes are special; they all relate to mode-independent
+assignment of values to \TeX's internal registers or tables.
+Codes that are |max_internal| or less represent internal quantities
+that might be expanded by `\.{\\the}'.
+
+@d toks_register 71 /*token list register ( \.{\\toks} )*/
+@d assign_toks 72 /*special token list ( \.{\\output}, \.{\\everypar}, etc.~)*/
+@d assign_int 73 /*user-defined integer ( \.{\\tolerance}, \.{\\day}, etc.~)*/
+@d assign_dimen 74 /*user-defined length ( \.{\\hsize}, etc.~)*/
+@d assign_glue 75 /*user-defined glue ( \.{\\baselineskip}, etc.~)*/
+@d assign_mu_glue 76 /*user-defined muglue ( \.{\\thinmuskip}, etc.~)*/
+@d assign_font_dimen 77 /*user-defined font dimension ( \.{\\fontdimen} )*/
+@d assign_font_int 78 /*user-defined font integer ( \.{\\hyphenchar},
+ \.{\\skewchar} )*/
+@d set_aux 79 /*specify state info ( \.{\\spacefactor}, \.{\\prevdepth} )*/
+@d set_prev_graf 80 /*specify state info ( \.{\\prevgraf} )*/
+@d set_page_dimen 81 /*specify state info ( \.{\\pagegoal}, etc.~)*/
+@d set_page_int 82 /*specify state info ( \.{\\deadcycles},
+ \.{\\insertpenalties} )*/
+ /*( or \.{\\interactionmode} )*/
+@d set_box_dimen 83 /*change dimension of box ( \.{\\wd}, \.{\\ht}, \.{\\dp} )*/
+@d set_shape 84 /*specify fancy paragraph shape ( \.{\\parshape} )*/
+ /*(or \.{\\interlinepenalties}, etc.~)*/
+@d def_code 85 /*define a character code ( \.{\\catcode}, etc.~)*/
+@d def_family 86 /*declare math fonts ( \.{\\textfont}, etc.~)*/
+@d set_font 87 /*set current font ( font identifiers )*/
+@d def_font 88 /*define a font file ( \.{\\font} )*/
+@d internal_register 89 /*internal register ( \.{\\count}, \.{\\dimen}, etc.~)*/
+@d max_internal 89 /*the largest code that can follow \.{\\the}*/
+@d advance 90 /*advance a register or parameter ( \.{\\advance} )*/
+@d multiply 91 /*multiply a register or parameter ( \.{\\multiply} )*/
+@d divide 92 /*divide a register or parameter ( \.{\\divide} )*/
+@d prefix 93 /*qualify a definition ( \.{\\global}, \.{\\long}, \.{\\outer} )*/
+ /*( or \.{\\protected} )*/
+@d let 94 /*assign a command code ( \.{\\let}, \.{\\futurelet} )*/
+@d shorthand_def 95 /*code definition ( \.{\\chardef}, \.{\\countdef}, etc.~)*/
+@d read_to_cs 96 /*read into a control sequence ( \.{\\read} )*/
+ /*( or \.{\\readline} )*/
+@d def 97 /*macro definition ( \.{\\def}, \.{\\gdef}, \.{\\xdef}, \.{\\edef} )*/
+@d set_box 98 /*set a box ( \.{\\setbox} )*/
+@d hyph_data 99 /*hyphenation data ( \.{\\hyphenation}, \.{\\patterns} )*/
+@d set_interaction 100 /*define level of interaction ( \.{\\batchmode}, etc.~)*/
+@d max_command 100 /*the largest command code seen at |big_switch|*/
+
+@ The remaining command codes are extra special, since they cannot get through
+\TeX's scanner to the main control routine. They have been given values higher
+than |max_command| so that their special nature is easily discernible.
+The ``expandable'' commands come first.
+
+@d undefined_cs (max_command+1) /*initial state of most |eq_type| fields*/
+@d expand_after (max_command+2) /*special expansion ( \.{\\expandafter} )*/
+@d no_expand (max_command+3) /*special nonexpansion ( \.{\\noexpand} )*/
+@d input (max_command+4) /*input a source file ( \.{\\input}, \.{\\endinput} )*/
+ /*( or \.{\\scantokens} )*/
+@d if_test (max_command+5) /*conditional text ( \.{\\if}, \.{\\ifcase}, etc.~)*/
+@d fi_or_else (max_command+6) /*delimiters for conditionals ( \.{\\else}, etc.~)*/
+@d cs_name (max_command+7) /*make a control sequence from tokens ( \.{\\csname} )*/
+@d convert (max_command+8) /*convert to text ( \.{\\number}, \.{\\string}, etc.~)*/
+@d the (max_command+9) /*expand an internal quantity ( \.{\\the} )*/
+ /*( or \.{\\unexpanded}, \.{\\detokenize} )*/
+@d top_bot_mark (max_command+10) /*inserted mark ( \.{\\topmark}, etc.~)*/
+@d call (max_command+11) /*non-long, non-outer control sequence*/
+@d long_call (max_command+12) /*long, non-outer control sequence*/
+@d outer_call (max_command+13) /*non-long, outer control sequence*/
+@d long_outer_call (max_command+14) /*long, outer control sequence*/
+@d end_template (max_command+15) /*end of an alignment template*/
+@d dont_expand (max_command+16) /*the following token was marked by \.{\\noexpand}*/
+@d glue_ref (max_command+17) /*the equivalent points to a glue specification*/
+@d shape_ref (max_command+18) /*the equivalent points to a parshape specification*/
+@d box_ref (max_command+19) /*the equivalent points to a box node, or is |null|*/
+@d data (max_command+20) /*the equivalent is simply a halfword number*/
+
+@* The semantic nest.
+\TeX\ is typically in the midst of building many lists at once. For example,
+when a math formula is being processed, \TeX\ is in math mode and
+working on an mlist; this formula has temporarily interrupted \TeX\ from
+being in horizontal mode and building the hlist of a paragraph; and this
+paragraph has temporarily interrupted \TeX\ from being in vertical mode
+and building the vlist for the next page of a document. Similarly, when a
+\.{\\vbox} occurs inside of an \.{\\hbox}, \TeX\ is temporarily
+interrupted from working in restricted horizontal mode, and it enters
+internal vertical mode. The ``semantic nest'' is a stack that
+keeps track of what lists and modes are currently suspended.
+
+At each level of processing we are in one of six modes:
+
+\yskip\hang|vmode| stands for vertical mode (the page builder);
+
+\hang|hmode| stands for horizontal mode (the paragraph builder);
+
+\hang|mmode| stands for displayed formula mode;
+
+\hang|-vmode| stands for internal vertical mode (e.g., in a \.{\\vbox});
+
+\hang|-hmode| stands for restricted horizontal mode (e.g., in an \.{\\hbox});
+
+\hang|-mmode| stands for math formula mode (not displayed).
+
+\yskip\noindent The mode is temporarily set to zero while processing \.{\\write}
+texts.
+
+Numeric values are assigned to |vmode|, |hmode|, and |mmode| so that
+\TeX's ``big semantic switch'' can select the appropriate thing to
+do by computing the value |abs(mode)+cur_cmd|, where |mode| is the current
+mode and |cur_cmd| is the current command code.
+
+@d vmode 1 /*vertical mode*/
+@d hmode (vmode+max_command+1) /*horizontal mode*/
+@d mmode (hmode+max_command+1) /*math mode*/
+
+@p static void print_mode(int @!m) /*prints the mode represented by |m|*/
+{@+if (m > 0)
+ switch (m/(max_command+1)) {
+ case 0: print("vertical");@+break;
+ case 1: print("horizontal");@+break;
+ case 2: print("display math");
+ }
+else if (m==0) print("no");
+else switch ((-m)/(max_command+1)) {
+ case 0: print("internal vertical");@+break;
+ case 1: print("restricted horizontal");@+break;
+ case 2: print("math");
+ }
+print(" mode");
+}
+
+@ The state of affairs at any semantic level can be represented by
+five values:
+
+\yskip\hang|mode| is the number representing the semantic mode, as
+just explained.
+
+\yskip\hang|head| is a |pointer| to a list head for the list being built;
+|link(head)| therefore points to the first element of the list, or
+to |null| if the list is empty.
+
+\yskip\hang|tail| is a |pointer| to the final node of the list being
+built; thus, |tail==head| if and only if the list is empty.
+
+\yskip\hang|prev_graf| is the number of lines of the current paragraph that
+have already been put into the present vertical list.
+
+\yskip\hang|aux| is an auxiliary |memory_word| that gives further information
+that is needed to characterize the situation.
+
+\yskip\noindent
+In vertical mode, |aux| is also known as |prev_depth|; it is the scaled
+value representing the depth of the previous box, for use in baseline
+calculations, or it is | <= -1000|pt if the next box on the vertical list is to
+be exempt from baseline calculations. In horizontal mode, |aux| is also
+known as |space_factor| and |clang|; it holds the current space factor used in
+spacing calculations, and the current language used for hyphenation.
+(The value of |clang| is undefined in restricted horizontal mode.)
+In math mode, |aux| is also known as |incompleat_noad|; if
+not |null|, it points to a record that represents the numerator of a
+generalized fraction for which the denominator is currently being formed
+in the current list.
+
+There is also a sixth quantity, |mode_line|, which correlates
+the semantic nest with the user's input; |mode_line| contains the source
+line number at which the current level of nesting was entered. The negative
+of this line number is the |mode_line| at the level of the
+user's output routine.
+
+A seventh quantity, |eTeX_aux|, is used by the extended features \eTeX.
+In vertical modes it is known as |LR_save| and holds the LR stack when a
+paragraph is interrupted by a displayed formula. In display math mode
+it is known as |LR_box| and holds a pointer to a prototype box for the
+display. In math mode it is known as |delim_ptr| and points to the most
+recent |left_noad| or |middle_noad| of a |math_left_group|.
+
+In horizontal mode, the |prev_graf| field is used for initial language data.
+
+The semantic nest is an array called |nest| that holds the |mode|, |head|,
+|tail|, |prev_graf|, |aux|, and |mode_line| values for all semantic levels
+below the currently active one. Information about the currently active
+level is kept in the global quantities |mode|, |head|, |tail|, |prev_graf|,
+|aux|, and |mode_line|, which live in a \PASCAL\ record that is ready to
+be pushed onto |nest| if necessary.
+
+@d ignore_depth -65536000 /*|prev_depth| value that is ignored*/
+
+@<Types...@>=
+typedef struct { int16_t @!mode_field;@+
+ pointer @!head_field, @!tail_field;
+ pointer @!eTeX_aux_field;
+ int @!pg_field, @!ml_field;@+
+ memory_word @!aux_field;
+ } list_state_record;
+
+@ @d mode cur_list.mode_field /*current mode*/
+@d head cur_list.head_field /*header node of current list*/
+@d tail cur_list.tail_field /*final node on current list*/
+@d eTeX_aux cur_list.eTeX_aux_field /*auxiliary data for \eTeX*/
+@d LR_save eTeX_aux /*LR stack when a paragraph is interrupted*/
+@d LR_box eTeX_aux /*prototype box for display*/
+@d delim_ptr eTeX_aux /*most recent left or right noad of a math left group*/
+@d prev_graf cur_list.pg_field /*number of paragraph lines accumulated*/
+@d aux cur_list.aux_field /*auxiliary data about the current list*/
+@d prev_depth aux.sc /*the name of |aux| in vertical mode*/
+@d space_factor aux.hh.lh /*part of |aux| in horizontal mode*/
+@d clang aux.hh.rh /*the other part of |aux| in horizontal mode*/
+@d incompleat_noad aux.i /*the name of |aux| in math mode*/
+@d mode_line cur_list.ml_field /*source file line number at beginning of list*/
+
+@<Glob...@>=
+static list_state_record @!nest[nest_size+1];
+static int @!nest_ptr; /*first unused location of |nest|*/
+static int @!max_nest_stack; /*maximum of |nest_ptr| when pushing*/
+static list_state_record @!cur_list; /*the ``top'' semantic state*/
+static int @!shown_mode; /*most recent mode shown by \.{\\tracingcommands}*/
+
+@ Here is a common way to make the current list grow:
+
+@d tail_append(A) {@+link(tail)=A;tail=link(tail);
+ }
+
+@ We will see later that the vertical list at the bottom semantic level is split
+into two parts; the ``current page'' runs from |page_head| to |page_tail|,
+and the ``contribution list'' runs from |contrib_head| to |tail| of
+semantic level zero. The idea is that contributions are first formed in
+vertical mode, then ``contributed'' to the current page (during which time
+the page-breaking decisions are made). For now, we don't need to know
+any more details about the page-building process.
+
+@<Set init...@>=
+nest_ptr=0;max_nest_stack=0;
+mode=vmode;head=contrib_head;tail=contrib_head;
+eTeX_aux=null;
+prev_depth=ignore_depth;mode_line=0;
+prev_graf=0;shown_mode=0;
+@<Start a new current page@>;
+
+@ When \TeX's work on one level is interrupted, the state is saved by
+calling |push_nest|. This routine changes |head| and |tail| so that
+a new (empty) list is begun; it does not change |mode| or |aux|.
+
+@p static void push_nest(void) /*enter a new semantic level, save the old*/
+{@+if (nest_ptr > max_nest_stack)
+ {@+max_nest_stack=nest_ptr;
+ if (nest_ptr==nest_size) overflow("semantic nest size", nest_size);
+@:TeX capacity exceeded semantic nest size}{\quad semantic nest size@>
+ }
+nest[nest_ptr]=cur_list; /*stack the record*/
+incr(nest_ptr);head=get_avail();tail=head;prev_graf=0;mode_line=line;
+eTeX_aux=null;
+}
+
+@ Conversely, when \TeX\ is finished on the current level, the former
+state is restored by calling |pop_nest|. This routine will never be
+called at the lowest semantic level, nor will it be called unless |head|
+is a node that should be returned to free memory.
+
+@p static void pop_nest(void) /*leave a semantic level, re-enter the old*/
+{@+free_avail(head);decr(nest_ptr);cur_list=nest[nest_ptr];
+}
+
+@ Here is a procedure that displays what \TeX\ is working on, at all levels.
+
+@p static void print_totals(void);
+static void show_activities(void)
+{@+int p; /*index into |nest|*/
+int @!m; /*mode*/
+memory_word @!a; /*auxiliary*/
+pointer @!q, @!r; /*for showing the current page*/
+int @!t; /*ditto*/
+nest[nest_ptr]=cur_list; /*put the top level into the array*/
+print_nl("");print_ln();
+for (p=nest_ptr; p>=0; p--)
+ {@+m=nest[p].mode_field;a=nest[p].aux_field;
+ print_nl("### ");print_mode(m);
+ print(" entered at line ");print_int(abs(nest[p].ml_field));
+ if (m==hmode) if (nest[p].pg_field!=040600000)
+ {@+print(" (language");print_int(nest[p].pg_field%0200000);
+ print(":hyphenmin");print_int(nest[p].pg_field/020000000);
+ print_char(',');print_int((nest[p].pg_field/0200000)%0100);
+ print_char(')');
+ }
+ if (nest[p].ml_field < 0) print(" (\\output routine)");
+ if (p==0)
+ {@+@<Show the status of the current page@>;
+ if (link(contrib_head)!=null)
+ print_nl("### recent contributions:");
+ }
+ show_box(link(nest[p].head_field));
+ @<Show the auxiliary field, |a|@>;
+ }
+}
+
+@ @<Show the auxiliary...@>=
+switch (abs(m)/(max_command+1)) {
+case 0: {@+print_nl("prevdepth ");
+ if (a.sc <= ignore_depth) print("ignored");
+ else print_scaled(a.sc);
+ if (nest[p].pg_field!=0)
+ {@+print(", prevgraf ");
+ print_int(nest[p].pg_field);print(" line");
+ if (nest[p].pg_field!=1) print_char('s');
+ }
+ } @+break;
+case 1: {@+print_nl("spacefactor ");print_int(a.hh.lh);
+ if (m > 0) @+if (a.hh.rh > 0)
+ {@+print(", current language ");print_int(a.hh.rh);@+
+ }
+ } @+break;
+case 2: if (a.i!=null)
+ {@+print("this will begin denominator of:");show_box(a.i);@+
+ }
+} /*there are no other cases*/
+
+@* The table of equivalents.
+Now that we have studied the data structures for \TeX's semantic routines,
+we ought to consider the data structures used by its syntactic routines. In
+other words, our next concern will be
+the tables that \TeX\ looks at when it is scanning
+what the user has written.
+
+The biggest and most important such table is called |eqtb|. It holds the
+current ``equivalents'' of things; i.e., it explains what things mean
+or what their current values are, for all quantities that are subject to
+the nesting structure provided by \TeX's grouping mechanism. There are six
+parts to |eqtb|:
+
+\yskip\hangg 1) |eqtb[active_base dotdot(hash_base-1)]| holds the current
+equivalents of single-character control sequences.
+
+\yskip\hangg 2) |eqtb[hash_base dotdot(glue_base-1)]| holds the current
+equivalents of multiletter control sequences.
+
+\yskip\hangg 3) |eqtb[glue_base dotdot(local_base-1)]| holds the current
+equivalents of glue parameters like the current baselineskip.
+
+\yskip\hangg 4) |eqtb[local_base dotdot(int_base-1)]| holds the current
+equivalents of local halfword quantities like the current box registers,
+the current ``catcodes,'' the current font, and a pointer to the current
+paragraph shape.
+
+\yskip\hangg 5) |eqtb[int_base dotdot(dimen_base-1)]| holds the current
+equivalents of fullword integer parameters like the current hyphenation
+penalty.
+
+\yskip\hangg 6) |eqtb[dimen_base dotdot eqtb_size]| holds the current equivalents
+of fullword dimension parameters like the current hsize or amount of
+hanging indentation.
+
+\yskip\noindent Note that, for example, the current amount of
+baselineskip glue is determined by the setting of a particular location
+in region~3 of |eqtb|, while the current meaning of the control sequence
+`\.{\\baselineskip}' (which might have been changed by \.{\\def} or
+\.{\\let}) appears in region~2.
+
+@ Each entry in |eqtb| is a |memory_word|. Most of these words are of type
+|two_halves|, and subdivided into three fields:
+
+\yskip\hangg 1) The |eq_level| (a quarterword) is the level of grouping at
+which this equivalent was defined. If the level is |level_zero|, the
+equivalent has never been defined; |level_one| refers to the outer level
+(outside of all groups), and this level is also used for global
+definitions that never go away. Higher levels are for equivalents that
+will disappear at the end of their group. @^global definitions@>
+
+\yskip\hangg 2) The |eq_type| (another quarterword) specifies what kind of
+entry this is. There are many types, since each \TeX\ primitive like
+\.{\\hbox}, \.{\\def}, etc., has its own special code. The list of
+command codes above includes all possible settings of the |eq_type| field.
+
+\yskip\hangg 3) The |equiv| (a halfword) is the current equivalent value.
+This may be a font number, a pointer into |mem|, or a variety of other
+things.
+
+@d eq_level_field(A) A.hh.b1
+@d eq_type_field(A) A.hh.b0
+@d equiv_field(A) A.hh.rh
+@d eq_level(A) eq_level_field(eqtb[A]) /*level of definition*/
+@d eq_type(A) eq_type_field(eqtb[A]) /*command code for equivalent*/
+@d equiv(A) equiv_field(eqtb[A]) /*equivalent value*/
+@d level_zero min_quarterword /*level for undefined quantities*/
+@d level_one (level_zero+1) /*outermost level for defined quantities*/
+
+@ Many locations in |eqtb| have symbolic names. The purpose of the next
+paragraphs is to define these names, and to set up the initial values of the
+equivalents.
+
+In the first region we have 256 equivalents for ``active characters'' that
+act as control sequences, followed by 256 equivalents for single-character
+control sequences.
+
+Then comes region~2, which corresponds to the hash table that we will
+define later. The maximum address in this region is used for a dummy
+control sequence that is perpetually undefined. There also are several
+locations for control sequences that are perpetually defined
+(since they are used in error recovery).
+
+@d active_base 1 /*beginning of region 1, for active character equivalents*/
+@d single_base (active_base+256) /*equivalents of one-character control sequences*/
+@d null_cs (single_base+256) /*equivalent of \.{\\csname\\endcsname}*/
+@d hash_base (null_cs+1) /*beginning of region 2, for the hash table*/
+@d frozen_control_sequence (hash_base+hash_size) /*for error recovery*/
+@d frozen_protection frozen_control_sequence /*inaccessible but definable*/
+@d frozen_cr (frozen_control_sequence+1) /*permanent `\.{\\cr}'*/
+@d frozen_end_group (frozen_control_sequence+2) /*permanent `\.{\\endgroup}'*/
+@d frozen_right (frozen_control_sequence+3) /*permanent `\.{\\right}'*/
+@d frozen_fi (frozen_control_sequence+4) /*permanent `\.{\\fi}'*/
+@d frozen_end_template (frozen_control_sequence+5) /*permanent `\.{\\endtemplate}'*/
+@d frozen_endv (frozen_control_sequence+6) /*second permanent `\.{\\endtemplate}'*/
+@d frozen_relax (frozen_control_sequence+7) /*permanent `\.{\\relax}'*/
+@d end_write (frozen_control_sequence+8) /*permanent `\.{\\endwrite}'*/
+@d frozen_dont_expand (frozen_control_sequence+9)
+ /*permanent `\.{\\notexpanded:}'*/
+@d frozen_primitive (frozen_control_sequence+10)
+ /*permanent `\.{\\primitive:}'*/
+@d frozen_null_font (frozen_control_sequence+11)
+ /*permanent `\.{\\nullfont}'*/
+@d font_id_base (frozen_null_font-font_base)
+ /*begins table of 257 permanent font identifiers*/
+@d undefined_control_sequence (frozen_null_font+257) /*dummy location*/
+@d glue_base (undefined_control_sequence+1) /*beginning of region 3*/
+
+@<Initialize table entries...@>=
+eq_type(undefined_control_sequence)=undefined_cs;
+equiv(undefined_control_sequence)=null;
+eq_level(undefined_control_sequence)=level_zero;
+for (k=active_base; k<=undefined_control_sequence-1; k++)
+ eqtb[k]=eqtb[undefined_control_sequence];
+
+@ Here is a routine that displays the current meaning of an |eqtb| entry
+in region 1 or~2. (Similar routines for the other regions will appear
+below.)
+
+@<Show equivalent |n|, in region 1 or 2@>=
+{@+sprint_cs(n);print_char('=');print_cmd_chr(eq_type(n), equiv(n));
+if (eq_type(n) >= call)
+ {@+print_char(':');show_token_list(link(equiv(n)), null, 32);
+ }
+}
+
+@ Region 3 of |eqtb| contains the 256 \.{\\skip} registers, as well as the
+glue parameters defined here. It is important that the ``muskip''
+parameters have larger numbers than the others.
+
+@d line_skip_code 0 /*interline glue if |baseline_skip| is infeasible*/
+@d baseline_skip_code 1 /*desired glue between baselines*/
+@d par_skip_code 2 /*extra glue just above a paragraph*/
+@d above_display_skip_code 3 /*extra glue just above displayed math*/
+@d below_display_skip_code 4 /*extra glue just below displayed math*/
+@d above_display_short_skip_code 5
+ /*glue above displayed math following short lines*/
+@d below_display_short_skip_code 6
+ /*glue below displayed math following short lines*/
+@d left_skip_code 7 /*glue at left of justified lines*/
+@d right_skip_code 8 /*glue at right of justified lines*/
+@d top_skip_code 9 /*glue at top of main pages*/
+@d split_top_skip_code 10 /*glue at top of split pages*/
+@d tab_skip_code 11 /*glue between aligned entries*/
+@d space_skip_code 12 /*glue between words (if not |zero_glue|)*/
+@d xspace_skip_code 13 /*glue after sentences (if not |zero_glue|)*/
+@d par_fill_skip_code 14 /*glue on last line of paragraph*/
+@d thin_mu_skip_code 15 /*thin space in math formula*/
+@d med_mu_skip_code 16 /*medium space in math formula*/
+@d thick_mu_skip_code 17 /*thick space in math formula*/
+@d glue_pars 18 /*total number of glue parameters*/
+@d skip_base (glue_base+glue_pars) /*table of 256 ``skip'' registers*/
+@d mu_skip_base (skip_base+256) /*table of 256 ``muskip'' registers*/
+@d local_base (mu_skip_base+256) /*beginning of region 4*/
+@#
+@d skip(A) equiv(skip_base+A) /*|mem| location of glue specification*/
+@d mu_skip(A) equiv(mu_skip_base+A) /*|mem| location of math glue spec*/
+@d glue_par(A) equiv(glue_base+A) /*|mem| location of glue specification*/
+@d line_skip glue_par(line_skip_code)
+@d baseline_skip glue_par(baseline_skip_code)
+@d par_skip glue_par(par_skip_code)
+@d above_display_skip glue_par(above_display_skip_code)
+@d below_display_skip glue_par(below_display_skip_code)
+@d above_display_short_skip glue_par(above_display_short_skip_code)
+@d below_display_short_skip glue_par(below_display_short_skip_code)
+@d left_skip glue_par(left_skip_code)
+@d right_skip glue_par(right_skip_code)
+@d top_skip glue_par(top_skip_code)
+@d split_top_skip glue_par(split_top_skip_code)
+@d tab_skip glue_par(tab_skip_code)
+@d space_skip glue_par(space_skip_code)
+@d xspace_skip glue_par(xspace_skip_code)
+@d par_fill_skip glue_par(par_fill_skip_code)
+@d thin_mu_skip glue_par(thin_mu_skip_code)
+@d med_mu_skip glue_par(med_mu_skip_code)
+@d thick_mu_skip glue_par(thick_mu_skip_code)
+
+@<Current |mem| equivalent of glue parameter number |n|@>=glue_par(n)
+
+@ Sometimes we need to convert \TeX's internal code numbers into symbolic
+form. The |print_skip_param| routine gives the symbolic name of a glue
+parameter.
+
+@<Declare the procedure called |print_skip_param|@>=
+static void print_skip_param(int @!n)
+{@+switch (n) {
+case line_skip_code: print_esc("lineskip");@+break;
+case baseline_skip_code: print_esc("baselineskip");@+break;
+case par_skip_code: print_esc("parskip");@+break;
+case above_display_skip_code: print_esc("abovedisplayskip");@+break;
+case below_display_skip_code: print_esc("belowdisplayskip");@+break;
+case above_display_short_skip_code: print_esc("abovedisplayshortskip");@+break;
+case below_display_short_skip_code: print_esc("belowdisplayshortskip");@+break;
+case left_skip_code: print_esc("leftskip");@+break;
+case right_skip_code: print_esc("rightskip");@+break;
+case top_skip_code: print_esc("topskip");@+break;
+case split_top_skip_code: print_esc("splittopskip");@+break;
+case tab_skip_code: print_esc("tabskip");@+break;
+case space_skip_code: print_esc("spaceskip");@+break;
+case xspace_skip_code: print_esc("xspaceskip");@+break;
+case par_fill_skip_code: print_esc("parfillskip");@+break;
+case thin_mu_skip_code: print_esc("thinmuskip");@+break;
+case med_mu_skip_code: print_esc("medmuskip");@+break;
+case thick_mu_skip_code: print_esc("thickmuskip");@+break;
+default:print("[unknown glue parameter!]");
+}
+}
+
+@ The symbolic names for glue parameters are put into \TeX's hash table
+by using the routine called |primitive|, defined below. Let us enter them
+now, so that we don't have to list all those parameter names anywhere else.
+
+@<Put each of \TeX's primitives into the hash table@>=
+primitive("lineskip", assign_glue, glue_base+line_skip_code);@/
+@!@:line\_skip\_}{\.{\\lineskip} primitive@>
+primitive("baselineskip", assign_glue, glue_base+baseline_skip_code);@/
+@!@:baseline\_skip\_}{\.{\\baselineskip} primitive@>
+primitive("parskip", assign_glue, glue_base+par_skip_code);@/
+@!@:par\_skip\_}{\.{\\parskip} primitive@>
+primitive("abovedisplayskip", assign_glue, glue_base+above_display_skip_code);@/
+@!@:above\_display\_skip\_}{\.{\\abovedisplayskip} primitive@>
+primitive("belowdisplayskip", assign_glue, glue_base+below_display_skip_code);@/
+@!@:below\_display\_skip\_}{\.{\\belowdisplayskip} primitive@>
+primitive("abovedisplayshortskip",
+ assign_glue, glue_base+above_display_short_skip_code);@/
+@!@:above\_display\_short\_skip\_}{\.{\\abovedisplayshortskip} primitive@>
+primitive("belowdisplayshortskip",
+ assign_glue, glue_base+below_display_short_skip_code);@/
+@!@:below\_display\_short\_skip\_}{\.{\\belowdisplayshortskip} primitive@>
+primitive("leftskip", assign_glue, glue_base+left_skip_code);@/
+@!@:left\_skip\_}{\.{\\leftskip} primitive@>
+primitive("rightskip", assign_glue, glue_base+right_skip_code);@/
+@!@:right\_skip\_}{\.{\\rightskip} primitive@>
+primitive("topskip", assign_glue, glue_base+top_skip_code);@/
+@!@:top\_skip\_}{\.{\\topskip} primitive@>
+primitive("splittopskip", assign_glue, glue_base+split_top_skip_code);@/
+@!@:split\_top\_skip\_}{\.{\\splittopskip} primitive@>
+primitive("tabskip", assign_glue, glue_base+tab_skip_code);@/
+@!@:tab\_skip\_}{\.{\\tabskip} primitive@>
+primitive("spaceskip", assign_glue, glue_base+space_skip_code);@/
+@!@:space\_skip\_}{\.{\\spaceskip} primitive@>
+primitive("xspaceskip", assign_glue, glue_base+xspace_skip_code);@/
+@!@:xspace\_skip\_}{\.{\\xspaceskip} primitive@>
+primitive("parfillskip", assign_glue, glue_base+par_fill_skip_code);@/
+@!@:par\_fill\_skip\_}{\.{\\parfillskip} primitive@>
+primitive("thinmuskip", assign_mu_glue, glue_base+thin_mu_skip_code);@/
+@!@:thin\_mu\_skip\_}{\.{\\thinmuskip} primitive@>
+primitive("medmuskip", assign_mu_glue, glue_base+med_mu_skip_code);@/
+@!@:med\_mu\_skip\_}{\.{\\medmuskip} primitive@>
+primitive("thickmuskip", assign_mu_glue, glue_base+thick_mu_skip_code);@/
+@!@:thick\_mu\_skip\_}{\.{\\thickmuskip} primitive@>
+
+@ @<Cases of |print_cmd_chr| for symbolic printing of primitives@>=
+case assign_glue: case assign_mu_glue: if (chr_code < skip_base)
+ print_skip_param(chr_code-glue_base);
+ else if (chr_code < mu_skip_base)
+ {@+print_esc("skip");print_int(chr_code-skip_base);
+ }
+ else{@+print_esc("muskip");print_int(chr_code-mu_skip_base);
+ } @+break;
+
+@ All glue parameters and registers are initially `\.{0pt plus0pt minus0pt}'.
+
+@<Initialize table entries...@>=
+equiv(glue_base)=zero_glue;eq_level(glue_base)=level_one;
+eq_type(glue_base)=glue_ref;
+for (k=glue_base+1; k<=local_base-1; k++) eqtb[k]=eqtb[glue_base];
+glue_ref_count(zero_glue)=glue_ref_count(zero_glue)+local_base-glue_base;
+
+@ @<Show equivalent |n|, in region 3@>=
+if (n < skip_base)
+ {@+print_skip_param(n-glue_base);print_char('=');
+ if (n < glue_base+thin_mu_skip_code) print_spec(equiv(n),"pt");
+ else print_spec(equiv(n),"mu");
+ }
+else if (n < mu_skip_base)
+ {@+print_esc("skip");print_int(n-skip_base);print_char('=');
+ print_spec(equiv(n),"pt");
+ }
+else{@+print_esc("muskip");print_int(n-mu_skip_base);print_char('=');
+ print_spec(equiv(n),"mu");
+ }
+
+@ Region 4 of |eqtb| contains the local quantities defined here. The
+bulk of this region is taken up by five tables that are indexed by eight-bit
+characters; these tables are important to both the syntactic and semantic
+portions of \TeX. There are also a bunch of special things like font and
+token parameters, as well as the tables of \.{\\toks} and \.{\\box}
+registers.
+
+@d par_shape_loc local_base /*specifies paragraph shape*/
+@d output_routine_loc (local_base+1) /*points to token list for \.{\\output}*/
+@d every_par_loc (local_base+2) /*points to token list for \.{\\everypar}*/
+@d every_math_loc (local_base+3) /*points to token list for \.{\\everymath}*/
+@d every_display_loc (local_base+4) /*points to token list for \.{\\everydisplay}*/
+@d every_hbox_loc (local_base+5) /*points to token list for \.{\\everyhbox}*/
+@d every_vbox_loc (local_base+6) /*points to token list for \.{\\everyvbox}*/
+@d every_job_loc (local_base+7) /*points to token list for \.{\\everyjob}*/
+@d every_cr_loc (local_base+8) /*points to token list for \.{\\everycr}*/
+@d err_help_loc (local_base+9) /*points to token list for \.{\\errhelp}*/
+@d tex_toks (local_base+10) /*end of \TeX's token list parameters*/
+@#
+@d etex_toks_base pdf_toks /*base for \eTeX's token list parameters*/
+@d every_eof_loc etex_toks_base /*points to token list for \.{\\everyeof}*/
+@d etex_toks (etex_toks_base+1) /*end of \eTeX's token list parameters*/
+@#
+@d toks_base etex_toks /*table of 256 token list registers*/
+@#
+@d etex_pen_base (toks_base+256) /*start of table of \eTeX's penalties*/
+@d inter_line_penalties_loc etex_pen_base /*additional penalties between lines*/
+@d club_penalties_loc (etex_pen_base+1) /*penalties for creating club lines*/
+@d widow_penalties_loc (etex_pen_base+2) /*penalties for creating widow lines*/
+@d display_widow_penalties_loc (etex_pen_base+3) /*ditto, just before a display*/
+@d etex_pens (etex_pen_base+4) /*end of table of \eTeX's penalties*/
+@#
+@d box_base etex_pens /*table of 256 box registers*/
+@d cur_font_loc (box_base+256) /*internal font number outside math mode*/
+@d math_font_base (cur_font_loc+1) /*table of 48 math font numbers*/
+@d cat_code_base (math_font_base+48)
+ /*table of 256 command codes (the ``catcodes'')*/
+@d lc_code_base (cat_code_base+256) /*table of 256 lowercase mappings*/
+@d uc_code_base (lc_code_base+256) /*table of 256 uppercase mappings*/
+@d sf_code_base (uc_code_base+256) /*table of 256 spacefactor mappings*/
+@d math_code_base (sf_code_base+256) /*table of 256 math mode mappings*/
+@d int_base (math_code_base+256) /*beginning of region 5*/
+@#
+@d par_shape_ptr equiv(par_shape_loc)
+@d output_routine equiv(output_routine_loc)
+@d every_par equiv(every_par_loc)
+@d every_math equiv(every_math_loc)
+@d every_display equiv(every_display_loc)
+@d every_hbox equiv(every_hbox_loc)
+@d every_vbox equiv(every_vbox_loc)
+@d every_job equiv(every_job_loc)
+@d every_cr equiv(every_cr_loc)
+@d err_help equiv(err_help_loc)
+@d toks(X) equiv(toks_base+X)
+@d box(A) equiv(box_base+A)
+@d cur_font equiv(cur_font_loc)
+@d fam_fnt(A) equiv(math_font_base+A)
+@d cat_code(A) equiv(cat_code_base+A)
+@d lc_code(A) equiv(lc_code_base+A)
+@d uc_code(A) equiv(uc_code_base+A)
+@d sf_code(A) equiv(sf_code_base+A)
+@d math_code(A) equiv(math_code_base+A)
+ /*Note: |math_code(c)| is the true math code plus |min_halfword|*/
+
+@<Put each...@>=
+primitive("output", assign_toks, output_routine_loc);
+@!@:output\_}{\.{\\output} primitive@>
+primitive("everypar", assign_toks, every_par_loc);
+@!@:every\_par\_}{\.{\\everypar} primitive@>
+primitive("everymath", assign_toks, every_math_loc);
+@!@:every\_math\_}{\.{\\everymath} primitive@>
+primitive("everydisplay", assign_toks, every_display_loc);
+@!@:every\_display\_}{\.{\\everydisplay} primitive@>
+primitive("everyhbox", assign_toks, every_hbox_loc);
+@!@:every\_hbox\_}{\.{\\everyhbox} primitive@>
+primitive("everyvbox", assign_toks, every_vbox_loc);
+@!@:every\_vbox\_}{\.{\\everyvbox} primitive@>
+primitive("everyjob", assign_toks, every_job_loc);
+@!@:every\_job\_}{\.{\\everyjob} primitive@>
+primitive("everycr", assign_toks, every_cr_loc);
+@!@:every\_cr\_}{\.{\\everycr} primitive@>
+primitive("errhelp", assign_toks, err_help_loc);
+@!@:err\_help\_}{\.{\\errhelp} primitive@>
+
+@ @<Cases of |print_cmd_chr|...@>=
+case assign_toks: if (chr_code >= toks_base)
+ {@+print_esc("toks");print_int(chr_code-toks_base);
+ }
+else switch (chr_code) {
+ case output_routine_loc: print_esc("output");@+break;
+ case every_par_loc: print_esc("everypar");@+break;
+ case every_math_loc: print_esc("everymath");@+break;
+ case every_display_loc: print_esc("everydisplay");@+break;
+ case every_hbox_loc: print_esc("everyhbox");@+break;
+ case every_vbox_loc: print_esc("everyvbox");@+break;
+ case every_job_loc: print_esc("everyjob");@+break;
+ case every_cr_loc: print_esc("everycr");@+break;
+ @/@<Cases of |assign_toks| for |print_cmd_chr|@>@/
+ default:print_esc("errhelp");
+ } @+break;
+
+@ We initialize most things to null or undefined values. An undefined font
+is represented by the internal code |font_base|.
+
+However, the character code tables are given initial values based on the
+conventional interpretation of ASCII code. These initial values should
+not be changed when \TeX\ is adapted for use with non-English languages;
+all changes to the initialization conventions should be made in format
+packages, not in \TeX\ itself, so that global interchange of formats is
+possible.
+
+@d null_font font_base
+@d var_code 070000 /*math code meaning ``use the current family''*/
+
+@<Initialize table entries...@>=
+par_shape_ptr=null;eq_type(par_shape_loc)=shape_ref;
+eq_level(par_shape_loc)=level_one;@/
+for (k=etex_pen_base; k<=etex_pens-1; k++)
+ eqtb[k]=eqtb[par_shape_loc];
+for (k=output_routine_loc; k<=toks_base+255; k++)
+ eqtb[k]=eqtb[undefined_control_sequence];
+box(0)=null;eq_type(box_base)=box_ref;eq_level(box_base)=level_one;
+for (k=box_base+1; k<=box_base+255; k++) eqtb[k]=eqtb[box_base];
+cur_font=null_font;eq_type(cur_font_loc)=data;
+eq_level(cur_font_loc)=level_one;@/
+for (k=math_font_base; k<=math_font_base+47; k++) eqtb[k]=eqtb[cur_font_loc];
+equiv(cat_code_base)=0;eq_type(cat_code_base)=data;
+eq_level(cat_code_base)=level_one;@/
+for (k=cat_code_base+1; k<=int_base-1; k++) eqtb[k]=eqtb[cat_code_base];
+for (k=0; k<=255; k++)
+ {@+cat_code(k)=other_char;math_code(k)=hi(k);sf_code(k)=1000;
+ }
+cat_code(carriage_return)=car_ret;cat_code(' ')=spacer;
+cat_code('\\')=escape;cat_code('%')=comment;
+cat_code(invalid_code)=invalid_char;cat_code(null_code)=ignore;
+for (k='0'; k<='9'; k++) math_code(k)=hi(k+var_code);
+for (k='A'; k<='Z'; k++)
+ {@+cat_code(k)=letter;cat_code(k+'a'-'A')=letter;@/
+ math_code(k)=hi(k+var_code+0x100);
+ math_code(k+'a'-'A')=hi(k+'a'-'A'+var_code+0x100);@/
+ lc_code(k)=k+'a'-'A';lc_code(k+'a'-'A')=k+'a'-'A';@/
+ uc_code(k)=k;uc_code(k+'a'-'A')=k;@/
+ sf_code(k)=999;
+ }
+
+@ @<Show equivalent |n|, in region 4@>=
+if ((n==par_shape_loc)||((n >= etex_pen_base)&&(n < etex_pens)))
+ {@+print_cmd_chr(set_shape, n);print_char('=');
+ if (equiv(n)==null) print_char('0');
+ else if (n > par_shape_loc)
+ {@+print_int(penalty(equiv(n)));print_char(' ');
+ print_int(penalty(equiv(n)+1));
+ if (penalty(equiv(n)) > 1) print_esc("ETC.");
+ }
+ else print_int(info(par_shape_ptr));
+ }
+else if (n < toks_base)
+ {@+print_cmd_chr(assign_toks, n);print_char('=');
+ if (equiv(n)!=null) show_token_list(link(equiv(n)), null, 32);
+ }
+else if (n < box_base)
+ {@+print_esc("toks");print_int(n-toks_base);print_char('=');
+ if (equiv(n)!=null) show_token_list(link(equiv(n)), null, 32);
+ }
+else if (n < cur_font_loc)
+ {@+print_esc("box");print_int(n-box_base);print_char('=');
+ if (equiv(n)==null) print("void");
+ else{@+depth_threshold=0;breadth_max=1;show_node_list(equiv(n));
+ }
+ }
+else if (n < cat_code_base) @<Show the font identifier in |eqtb[n]|@>@;
+else@<Show the halfword code in |eqtb[n]|@>@;
+
+@ @<Show the font identifier in |eqtb[n]|@>=
+{@+if (n==cur_font_loc) print("current font");
+else if (n < math_font_base+16)
+ {@+print_esc("textfont");print_int(n-math_font_base);
+ }
+else if (n < math_font_base+32)
+ {@+print_esc("scriptfont");print_int(n-math_font_base-16);
+ }
+else{@+print_esc("scriptscriptfont");print_int(n-math_font_base-32);
+ }
+print_char('=');@/
+printn_esc(hash[font_id_base+equiv(n)].rh);
+ /*that's |font_id_text(equiv(n))|*/
+}
+
+@ @<Show the halfword code in |eqtb[n]|@>=
+if (n < math_code_base)
+ {@+if (n < lc_code_base)
+ {@+print_esc("catcode");print_int(n-cat_code_base);
+ }
+ else if (n < uc_code_base)
+ {@+print_esc("lccode");print_int(n-lc_code_base);
+ }
+ else if (n < sf_code_base)
+ {@+print_esc("uccode");print_int(n-uc_code_base);
+ }
+ else{@+print_esc("sfcode");print_int(n-sf_code_base);
+ }
+ print_char('=');print_int(equiv(n));
+ }
+else{@+print_esc("mathcode");print_int(n-math_code_base);
+ print_char('=');print_int(ho(equiv(n)));
+ }
+
+@ Region 5 of |eqtb| contains the integer parameters and registers defined
+here, as well as the |del_code| table. The latter table differs from the
+|cat_code dotdot math_code| tables that precede it, since delimiter codes are
+fullword integers while the other kinds of codes occupy at most a
+halfword. This is what makes region~5 different from region~4. We will
+store the |eq_level| information in an auxiliary array of quarterwords
+that will be defined later.
+
+@d pretolerance_code 0 /*badness tolerance before hyphenation*/
+@d tolerance_code 1 /*badness tolerance after hyphenation*/
+@d line_penalty_code 2 /*added to the badness of every line*/
+@d hyphen_penalty_code 3 /*penalty for break after discretionary hyphen*/
+@d ex_hyphen_penalty_code 4 /*penalty for break after explicit hyphen*/
+@d club_penalty_code 5 /*penalty for creating a club line*/
+@d widow_penalty_code 6 /*penalty for creating a widow line*/
+@d display_widow_penalty_code 7 /*ditto, just before a display*/
+@d broken_penalty_code 8 /*penalty for breaking a page at a broken line*/
+@d bin_op_penalty_code 9 /*penalty for breaking after a binary operation*/
+@d rel_penalty_code 10 /*penalty for breaking after a relation*/
+@d pre_display_penalty_code 11
+ /*penalty for breaking just before a displayed formula*/
+@d post_display_penalty_code 12
+ /*penalty for breaking just after a displayed formula*/
+@d inter_line_penalty_code 13 /*additional penalty between lines*/
+@d double_hyphen_demerits_code 14 /*demerits for double hyphen break*/
+@d final_hyphen_demerits_code 15 /*demerits for final hyphen break*/
+@d adj_demerits_code 16 /*demerits for adjacent incompatible lines*/
+@d mag_code 17 /*magnification ratio*/
+@d delimiter_factor_code 18 /*ratio for variable-size delimiters*/
+@d looseness_code 19 /*change in number of lines for a paragraph*/
+@d time_code 20 /*current time of day*/
+@d day_code 21 /*current day of the month*/
+@d month_code 22 /*current month of the year*/
+@d year_code 23 /*current year of our Lord*/
+@d show_box_breadth_code 24 /*nodes per level in |show_box|*/
+@d show_box_depth_code 25 /*maximum level in |show_box|*/
+@d hbadness_code 26 /*hboxes exceeding this badness will be shown by |hpack|*/
+@d vbadness_code 27 /*vboxes exceeding this badness will be shown by |vpack|*/
+@d pausing_code 28 /*pause after each line is read from a file*/
+@d tracing_online_code 29 /*show diagnostic output on terminal*/
+@d tracing_macros_code 30 /*show macros as they are being expanded*/
+@d tracing_stats_code 31 /*show memory usage if \TeX\ knows it*/
+@d tracing_paragraphs_code 32 /*show line-break calculations*/
+@d tracing_pages_code 33 /*show page-break calculations*/
+@d tracing_output_code 34 /*show boxes when they are shipped out*/
+@d tracing_lost_chars_code 35 /*show characters that aren't in the font*/
+@d tracing_commands_code 36 /*show command codes at |big_switch|*/
+@d tracing_restores_code 37 /*show equivalents when they are restored*/
+@d uc_hyph_code 38 /*hyphenate words beginning with a capital letter*/
+@d output_penalty_code 39 /*penalty found at current page break*/
+@d max_dead_cycles_code 40 /*bound on consecutive dead cycles of output*/
+@d hang_after_code 41 /*hanging indentation changes after this many lines*/
+@d floating_penalty_code 42 /*penalty for insertions held over after a split*/
+@d global_defs_code 43 /*override \.{\\global} specifications*/
+@d cur_fam_code 44 /*current family*/
+@d escape_char_code 45 /*escape character for token output*/
+@d default_hyphen_char_code 46 /*value of \.{\\hyphenchar} when a font is loaded*/
+@d default_skew_char_code 47 /*value of \.{\\skewchar} when a font is loaded*/
+@d end_line_char_code 48 /*character placed at the right end of the buffer*/
+@d new_line_char_code 49 /*character that prints as |print_ln|*/
+@d language_code 50 /*current hyphenation table*/
+@d left_hyphen_min_code 51 /*minimum left hyphenation fragment size*/
+@d right_hyphen_min_code 52 /*minimum right hyphenation fragment size*/
+@d holding_inserts_code 53 /*do not remove insertion nodes from \.{\\box255}*/
+@d error_context_lines_code 54 /*maximum intermediate line pairs shown*/
+@d tex_int_pars 55 /*total number of \TeX's integer parameters*/
+@#
+@d etex_int_base pdf_int_pars /*base for \eTeX's integer parameters*/
+@d tracing_assigns_code etex_int_base /*show assignments*/
+@d tracing_groups_code (etex_int_base+1) /*show save/restore groups*/
+@d tracing_ifs_code (etex_int_base+2) /*show conditionals*/
+@d tracing_scan_tokens_code (etex_int_base+3) /*show pseudo file open and close*/
+@d tracing_nesting_code (etex_int_base+4) /*show incomplete groups and ifs within files*/
+@d saving_vdiscards_code (etex_int_base+5) /*save items discarded from vlists*/
+@d saving_hyph_codes_code (etex_int_base+6) /*save hyphenation codes for languages*/
+@d expand_depth_code (etex_int_base+7) /*maximum depth for expansion---\eTeX*/
+@d eTeX_state_code (etex_int_base+8) /*\eTeX\ state variables*/
+@d etex_int_pars (eTeX_state_code+eTeX_states) /*total number of \eTeX's integer parameters*/
+@#
+@d int_pars etex_int_pars /*total number of integer parameters*/
+@d count_base (int_base+int_pars) /*256 user \.{\\count} registers*/
+@d del_code_base (count_base+256) /*256 delimiter code mappings*/
+@d dimen_base (del_code_base+256) /*beginning of region 6*/
+@#
+@d del_code(A) eqtb[del_code_base+A].i
+@d count(A) eqtb[count_base+A].i
+@d int_par(A) eqtb[int_base+A].i /*an integer parameter*/
+@d pretolerance int_par(pretolerance_code)
+@d tolerance int_par(tolerance_code)
+@d line_penalty int_par(line_penalty_code)
+@d hyphen_penalty int_par(hyphen_penalty_code)
+@d ex_hyphen_penalty int_par(ex_hyphen_penalty_code)
+@d club_penalty int_par(club_penalty_code)
+@d widow_penalty int_par(widow_penalty_code)
+@d display_widow_penalty int_par(display_widow_penalty_code)
+@d broken_penalty int_par(broken_penalty_code)
+@d bin_op_penalty int_par(bin_op_penalty_code)
+@d rel_penalty int_par(rel_penalty_code)
+@d pre_display_penalty int_par(pre_display_penalty_code)
+@d post_display_penalty int_par(post_display_penalty_code)
+@d inter_line_penalty int_par(inter_line_penalty_code)
+@d double_hyphen_demerits int_par(double_hyphen_demerits_code)
+@d final_hyphen_demerits int_par(final_hyphen_demerits_code)
+@d adj_demerits int_par(adj_demerits_code)
+@d mag int_par(mag_code)
+@d delimiter_factor int_par(delimiter_factor_code)
+@d looseness int_par(looseness_code)
+@d time int_par(time_code)
+@d day int_par(day_code)
+@d month int_par(month_code)
+@d year int_par(year_code)
+@d show_box_breadth int_par(show_box_breadth_code)
+@d show_box_depth int_par(show_box_depth_code)
+@d hbadness int_par(hbadness_code)
+@d vbadness int_par(vbadness_code)
+@d pausing int_par(pausing_code)
+@d tracing_online int_par(tracing_online_code)
+@d tracing_macros int_par(tracing_macros_code)
+@d tracing_stats int_par(tracing_stats_code)
+@d tracing_paragraphs int_par(tracing_paragraphs_code)
+@d tracing_pages int_par(tracing_pages_code)
+@d tracing_output int_par(tracing_output_code)
+@d tracing_lost_chars int_par(tracing_lost_chars_code)
+@d tracing_commands int_par(tracing_commands_code)
+@d tracing_restores int_par(tracing_restores_code)
+@d uc_hyph int_par(uc_hyph_code)
+@d output_penalty int_par(output_penalty_code)
+@d max_dead_cycles int_par(max_dead_cycles_code)
+@d hang_after int_par(hang_after_code)
+@d floating_penalty int_par(floating_penalty_code)
+@d global_defs int_par(global_defs_code)
+@d cur_fam int_par(cur_fam_code)
+@d escape_char int_par(escape_char_code)
+@d default_hyphen_char int_par(default_hyphen_char_code)
+@d default_skew_char int_par(default_skew_char_code)
+@d end_line_char int_par(end_line_char_code)
+@d new_line_char int_par(new_line_char_code)
+@d language int_par(language_code)
+@d left_hyphen_min int_par(left_hyphen_min_code)
+@d right_hyphen_min int_par(right_hyphen_min_code)
+@d holding_inserts int_par(holding_inserts_code)
+@d error_context_lines int_par(error_context_lines_code)
+@#
+@d tracing_assigns int_par(tracing_assigns_code)
+@d tracing_groups int_par(tracing_groups_code)
+@d tracing_ifs int_par(tracing_ifs_code)
+@d tracing_scan_tokens int_par(tracing_scan_tokens_code)
+@d tracing_nesting int_par(tracing_nesting_code)
+@d saving_vdiscards int_par(saving_vdiscards_code)
+@d saving_hyph_codes int_par(saving_hyph_codes_code)
+@d expand_depth int_par(expand_depth_code)
+
+@<Assign the values |depth_threshold:=show_box_depth|...@>=
+depth_threshold=show_box_depth;
+breadth_max=show_box_breadth
+
+@ We can print the symbolic name of an integer parameter as follows.
+
+@p static void print_param(int @!n)
+{@+switch (n) {
+case pretolerance_code: print_esc("pretolerance");@+break;
+case tolerance_code: print_esc("tolerance");@+break;
+case line_penalty_code: print_esc("linepenalty");@+break;
+case hyphen_penalty_code: print_esc("hyphenpenalty");@+break;
+case ex_hyphen_penalty_code: print_esc("exhyphenpenalty");@+break;
+case club_penalty_code: print_esc("clubpenalty");@+break;
+case widow_penalty_code: print_esc("widowpenalty");@+break;
+case display_widow_penalty_code: print_esc("displaywidowpenalty");@+break;
+case broken_penalty_code: print_esc("brokenpenalty");@+break;
+case bin_op_penalty_code: print_esc("binoppenalty");@+break;
+case rel_penalty_code: print_esc("relpenalty");@+break;
+case pre_display_penalty_code: print_esc("predisplaypenalty");@+break;
+case post_display_penalty_code: print_esc("postdisplaypenalty");@+break;
+case inter_line_penalty_code: print_esc("interlinepenalty");@+break;
+case double_hyphen_demerits_code: print_esc("doublehyphendemerits");@+break;
+case final_hyphen_demerits_code: print_esc("finalhyphendemerits");@+break;
+case adj_demerits_code: print_esc("adjdemerits");@+break;
+case mag_code: print_esc("mag");@+break;
+case delimiter_factor_code: print_esc("delimiterfactor");@+break;
+case looseness_code: print_esc("looseness");@+break;
+case time_code: print_esc("time");@+break;
+case day_code: print_esc("day");@+break;
+case month_code: print_esc("month");@+break;
+case year_code: print_esc("year");@+break;
+case show_box_breadth_code: print_esc("showboxbreadth");@+break;
+case show_box_depth_code: print_esc("showboxdepth");@+break;
+case hbadness_code: print_esc("hbadness");@+break;
+case vbadness_code: print_esc("vbadness");@+break;
+case pausing_code: print_esc("pausing");@+break;
+case tracing_online_code: print_esc("tracingonline");@+break;
+case tracing_macros_code: print_esc("tracingmacros");@+break;
+case tracing_stats_code: print_esc("tracingstats");@+break;
+case tracing_paragraphs_code: print_esc("tracingparagraphs");@+break;
+case tracing_pages_code: print_esc("tracingpages");@+break;
+case tracing_output_code: print_esc("tracingoutput");@+break;
+case tracing_lost_chars_code: print_esc("tracinglostchars");@+break;
+case tracing_commands_code: print_esc("tracingcommands");@+break;
+case tracing_restores_code: print_esc("tracingrestores");@+break;
+case uc_hyph_code: print_esc("uchyph");@+break;
+case output_penalty_code: print_esc("outputpenalty");@+break;
+case max_dead_cycles_code: print_esc("maxdeadcycles");@+break;
+case hang_after_code: print_esc("hangafter");@+break;
+case floating_penalty_code: print_esc("floatingpenalty");@+break;
+case global_defs_code: print_esc("globaldefs");@+break;
+case cur_fam_code: print_esc("fam");@+break;
+case escape_char_code: print_esc("escapechar");@+break;
+case default_hyphen_char_code: print_esc("defaulthyphenchar");@+break;
+case default_skew_char_code: print_esc("defaultskewchar");@+break;
+case end_line_char_code: print_esc("endlinechar");@+break;
+case new_line_char_code: print_esc("newlinechar");@+break;
+case language_code: print_esc("language");@+break;
+case left_hyphen_min_code: print_esc("lefthyphenmin");@+break;
+case right_hyphen_min_code: print_esc("righthyphenmin");@+break;
+case holding_inserts_code: print_esc("holdinginserts");@+break;
+case error_context_lines_code: print_esc("errorcontextlines");@+break;
+@/@<Cases for |print_param|@>@/
+default:print("[unknown integer parameter!]");
+}
+}
+
+@ The integer parameter names must be entered into the hash table.
+
+@<Put each...@>=
+primitive("pretolerance", assign_int, int_base+pretolerance_code);@/
+@!@:pretolerance\_}{\.{\\pretolerance} primitive@>
+primitive("tolerance", assign_int, int_base+tolerance_code);@/
+@!@:tolerance\_}{\.{\\tolerance} primitive@>
+primitive("linepenalty", assign_int, int_base+line_penalty_code);@/
+@!@:line\_penalty\_}{\.{\\linepenalty} primitive@>
+primitive("hyphenpenalty", assign_int, int_base+hyphen_penalty_code);@/
+@!@:hyphen\_penalty\_}{\.{\\hyphenpenalty} primitive@>
+primitive("exhyphenpenalty", assign_int, int_base+ex_hyphen_penalty_code);@/
+@!@:ex\_hyphen\_penalty\_}{\.{\\exhyphenpenalty} primitive@>
+primitive("clubpenalty", assign_int, int_base+club_penalty_code);@/
+@!@:club\_penalty\_}{\.{\\clubpenalty} primitive@>
+primitive("widowpenalty", assign_int, int_base+widow_penalty_code);@/
+@!@:widow\_penalty\_}{\.{\\widowpenalty} primitive@>
+primitive("displaywidowpenalty",
+ assign_int, int_base+display_widow_penalty_code);@/
+@!@:display\_widow\_penalty\_}{\.{\\displaywidowpenalty} primitive@>
+primitive("brokenpenalty", assign_int, int_base+broken_penalty_code);@/
+@!@:broken\_penalty\_}{\.{\\brokenpenalty} primitive@>
+primitive("binoppenalty", assign_int, int_base+bin_op_penalty_code);@/
+@!@:bin\_op\_penalty\_}{\.{\\binoppenalty} primitive@>
+primitive("relpenalty", assign_int, int_base+rel_penalty_code);@/
+@!@:rel\_penalty\_}{\.{\\relpenalty} primitive@>
+primitive("predisplaypenalty", assign_int, int_base+pre_display_penalty_code);@/
+@!@:pre\_display\_penalty\_}{\.{\\predisplaypenalty} primitive@>
+primitive("postdisplaypenalty", assign_int, int_base+post_display_penalty_code);@/
+@!@:post\_display\_penalty\_}{\.{\\postdisplaypenalty} primitive@>
+primitive("interlinepenalty", assign_int, int_base+inter_line_penalty_code);@/
+@!@:inter\_line\_penalty\_}{\.{\\interlinepenalty} primitive@>
+primitive("doublehyphendemerits",
+ assign_int, int_base+double_hyphen_demerits_code);@/
+@!@:double\_hyphen\_demerits\_}{\.{\\doublehyphendemerits} primitive@>
+primitive("finalhyphendemerits",
+ assign_int, int_base+final_hyphen_demerits_code);@/
+@!@:final\_hyphen\_demerits\_}{\.{\\finalhyphendemerits} primitive@>
+primitive("adjdemerits", assign_int, int_base+adj_demerits_code);@/
+@!@:adj\_demerits\_}{\.{\\adjdemerits} primitive@>
+primitive("mag", assign_int, int_base+mag_code);@/
+@!@:mag\_}{\.{\\mag} primitive@>
+primitive("delimiterfactor", assign_int, int_base+delimiter_factor_code);@/
+@!@:delimiter\_factor\_}{\.{\\delimiterfactor} primitive@>
+primitive("looseness", assign_int, int_base+looseness_code);@/
+@!@:looseness\_}{\.{\\looseness} primitive@>
+primitive("time", assign_int, int_base+time_code);@/
+@!@:time\_}{\.{\\time} primitive@>
+primitive("day", assign_int, int_base+day_code);@/
+@!@:day\_}{\.{\\day} primitive@>
+primitive("month", assign_int, int_base+month_code);@/
+@!@:month\_}{\.{\\month} primitive@>
+primitive("year", assign_int, int_base+year_code);@/
+@!@:year\_}{\.{\\year} primitive@>
+primitive("showboxbreadth", assign_int, int_base+show_box_breadth_code);@/
+@!@:show\_box\_breadth\_}{\.{\\showboxbreadth} primitive@>
+primitive("showboxdepth", assign_int, int_base+show_box_depth_code);@/
+@!@:show\_box\_depth\_}{\.{\\showboxdepth} primitive@>
+primitive("hbadness", assign_int, int_base+hbadness_code);@/
+@!@:hbadness\_}{\.{\\hbadness} primitive@>
+primitive("vbadness", assign_int, int_base+vbadness_code);@/
+@!@:vbadness\_}{\.{\\vbadness} primitive@>
+primitive("pausing", assign_int, int_base+pausing_code);@/
+@!@:pausing\_}{\.{\\pausing} primitive@>
+primitive("tracingonline", assign_int, int_base+tracing_online_code);@/
+@!@:tracing\_online\_}{\.{\\tracingonline} primitive@>
+primitive("tracingmacros", assign_int, int_base+tracing_macros_code);@/
+@!@:tracing\_macros\_}{\.{\\tracingmacros} primitive@>
+primitive("tracingstats", assign_int, int_base+tracing_stats_code);@/
+@!@:tracing\_stats\_}{\.{\\tracingstats} primitive@>
+primitive("tracingparagraphs", assign_int, int_base+tracing_paragraphs_code);@/
+@!@:tracing\_paragraphs\_}{\.{\\tracingparagraphs} primitive@>
+primitive("tracingpages", assign_int, int_base+tracing_pages_code);@/
+@!@:tracing\_pages\_}{\.{\\tracingpages} primitive@>
+primitive("tracingoutput", assign_int, int_base+tracing_output_code);@/
+@!@:tracing\_output\_}{\.{\\tracingoutput} primitive@>
+primitive("tracinglostchars", assign_int, int_base+tracing_lost_chars_code);@/
+@!@:tracing\_lost\_chars\_}{\.{\\tracinglostchars} primitive@>
+primitive("tracingcommands", assign_int, int_base+tracing_commands_code);@/
+@!@:tracing\_commands\_}{\.{\\tracingcommands} primitive@>
+primitive("tracingrestores", assign_int, int_base+tracing_restores_code);@/
+@!@:tracing\_restores\_}{\.{\\tracingrestores} primitive@>
+primitive("uchyph", assign_int, int_base+uc_hyph_code);@/
+@!@:uc\_hyph\_}{\.{\\uchyph} primitive@>
+primitive("outputpenalty", assign_int, int_base+output_penalty_code);@/
+@!@:output\_penalty\_}{\.{\\outputpenalty} primitive@>
+primitive("maxdeadcycles", assign_int, int_base+max_dead_cycles_code);@/
+@!@:max\_dead\_cycles\_}{\.{\\maxdeadcycles} primitive@>
+primitive("hangafter", assign_int, int_base+hang_after_code);@/
+@!@:hang\_after\_}{\.{\\hangafter} primitive@>
+primitive("floatingpenalty", assign_int, int_base+floating_penalty_code);@/
+@!@:floating\_penalty\_}{\.{\\floatingpenalty} primitive@>
+primitive("globaldefs", assign_int, int_base+global_defs_code);@/
+@!@:global\_defs\_}{\.{\\globaldefs} primitive@>
+primitive("fam", assign_int, int_base+cur_fam_code);@/
+@!@:fam\_}{\.{\\fam} primitive@>
+primitive("escapechar", assign_int, int_base+escape_char_code);@/
+@!@:escape\_char\_}{\.{\\escapechar} primitive@>
+primitive("defaulthyphenchar", assign_int, int_base+default_hyphen_char_code);@/
+@!@:default\_hyphen\_char\_}{\.{\\defaulthyphenchar} primitive@>
+primitive("defaultskewchar", assign_int, int_base+default_skew_char_code);@/
+@!@:default\_skew\_char\_}{\.{\\defaultskewchar} primitive@>
+primitive("endlinechar", assign_int, int_base+end_line_char_code);@/
+@!@:end\_line\_char\_}{\.{\\endlinechar} primitive@>
+primitive("newlinechar", assign_int, int_base+new_line_char_code);@/
+@!@:new\_line\_char\_}{\.{\\newlinechar} primitive@>
+primitive("language", assign_int, int_base+language_code);@/
+@!@:language\_}{\.{\\language} primitive@>
+primitive("lefthyphenmin", assign_int, int_base+left_hyphen_min_code);@/
+@!@:left\_hyphen\_min\_}{\.{\\lefthyphenmin} primitive@>
+primitive("righthyphenmin", assign_int, int_base+right_hyphen_min_code);@/
+@!@:right\_hyphen\_min\_}{\.{\\righthyphenmin} primitive@>
+primitive("holdinginserts", assign_int, int_base+holding_inserts_code);@/
+@!@:holding\_inserts\_}{\.{\\holdinginserts} primitive@>
+primitive("errorcontextlines", assign_int, int_base+error_context_lines_code);@/
+@!@:error\_context\_lines\_}{\.{\\errorcontextlines} primitive@>
+
+@ @<Cases of |print_cmd_chr|...@>=
+case assign_int: if (chr_code < count_base) print_param(chr_code-int_base);
+ else{@+print_esc("count");print_int(chr_code-count_base);
+ } @+break;
+
+@ The integer parameters should really be initialized by a macro package;
+the following initialization does the minimum to keep \TeX\ from
+complete failure.
+@^null delimiter@>
+
+@<Initialize table entries...@>=
+for (k=int_base; k<=del_code_base-1; k++) eqtb[k].i=0;
+mag=1000;tolerance=10000;hang_after=1;max_dead_cycles=25;
+escape_char='\\';end_line_char=carriage_return;
+for (k=0; k<=255; k++) del_code(k)=-1;
+del_code('.')=0; /*this null delimiter is used in error recovery*/
+
+@ The following procedure, which is called just before \TeX\ initializes its
+input and output, establishes the initial values of the date and time.
+This does include too, for system integrators, the creation date and
+the reference moment for the timer---\Prote\ extensions. If the system
+supports environment variables, if |FORCE_SOURCE_DATE| is set to $1$ and
+|SOURCE_DATE_EPOCH| is set, the date related values: year, month, day
+and time, including creation date, will be taken relative from the value
+defined by |SOURCE_DATE_EPOCH|.
+@^creation date@>
+@^reference time@>
+@^system dependencies@>
+\TeX\ Live calls |tl_now| to obtain the current time as a |tm| structure.
+@p static void fix_date_and_time(void)
+{@+ struct tm *t=tl_now();
+ time=sys_time= t->tm_hour*60+t->tm_min;/*minutes since midnight*/
+ day=sys_day= t->tm_mday;/*day of the month*/
+ month=sys_month=t->tm_mon+1;/*month of the year*/
+ year=sys_year=t->tm_year+1900;/*Anno Domini*/
+}
+
+@ @<Show equivalent |n|, in region 5@>=
+{@+if (n < count_base) print_param(n-int_base);
+else if (n < del_code_base)
+ {@+print_esc("count");print_int(n-count_base);
+ }
+else{@+print_esc("delcode");print_int(n-del_code_base);
+ }
+print_char('=');print_int(eqtb[n].i);
+}
+
+@ @<Set variable |c| to the current escape character@>=c=escape_char
+
+@ @<Character |s| is the current new-line character@>=s==new_line_char
+
+@ \TeX\ is occasionally supposed to print diagnostic information that
+goes only into the transcript file, unless |tracing_online| is positive.
+Here are two routines that adjust the destination of print commands:
+
+@p static void begin_diagnostic(void) /*prepare to do some tracing*/
+{@+old_setting=selector;
+if ((tracing_online <= 0)&&(selector==term_and_log))
+ {@+decr(selector);
+ if (history==spotless) history=warning_issued;
+ }
+}
+@#
+static void end_diagnostic(bool @!blank_line)
+ /*restore proper conditions after tracing*/
+{@+print_nl("");
+if (blank_line) print_ln();
+selector=old_setting;
+}
+
+@ Of course we had better declare a few more global variables, if the previous
+routines are going to work.
+
+@<Glob...@>=
+static int @!old_setting;
+static int @!sys_time, @!sys_day, @!sys_month, @!sys_year;
+ /*date and time supplied by external system*/
+
+@ The final region of |eqtb| contains the dimension parameters defined
+here, and the 256 \.{\\dimen} registers.
+
+@d par_indent_code 0 /*indentation of paragraphs*/
+@d math_surround_code 1 /*space around math in text*/
+@d line_skip_limit_code 2 /*threshold for |line_skip| instead of |baseline_skip|*/
+@d hsize_code 3 /*line width in horizontal mode*/
+@d vsize_code 4 /*page height in vertical mode*/
+@d max_depth_code 5 /*maximum depth of boxes on main pages*/
+@d split_max_depth_code 6 /*maximum depth of boxes on split pages*/
+@d box_max_depth_code 7 /*maximum depth of explicit vboxes*/
+@d hfuzz_code 8 /*tolerance for overfull hbox messages*/
+@d vfuzz_code 9 /*tolerance for overfull vbox messages*/
+@d delimiter_shortfall_code 10 /*maximum amount uncovered by variable delimiters*/
+@d null_delimiter_space_code 11 /*blank space in null delimiters*/
+@d script_space_code 12 /*extra space after subscript or superscript*/
+@d pre_display_size_code 13 /*length of text preceding a display*/
+@d display_width_code 14 /*length of line for displayed equation*/
+@d display_indent_code 15 /*indentation of line for displayed equation*/
+@d overfull_rule_code 16 /*width of rule that identifies overfull hboxes*/
+@d hang_indent_code 17 /*amount of hanging indentation*/
+@d h_offset_code 18 /*amount of horizontal offset when shipping pages out*/
+@d v_offset_code 19 /*amount of vertical offset when shipping pages out*/
+@d emergency_stretch_code 20 /*reduces badnesses on final pass of line-breaking*/
+@d page_width_code 21 /*current paper page width*/
+@d page_height_code 22 /*current paper page height*/
+@d dimen_pars (pdftex_last_dimen_code+1) /*total number of dimension parameters*/
+@d scaled_base (dimen_base+dimen_pars)
+ /*table of 256 user-defined \.{\\dimen} registers*/
+@d eqtb_size (scaled_base+255) /*largest subscript of |eqtb|*/
+@#
+@d dimen(A) eqtb[scaled_base+A].sc
+@d dimen_par(A) eqtb[dimen_base+A].sc /*a scaled quantity*/
+@d par_indent dimen_par(par_indent_code)
+@d math_surround dimen_par(math_surround_code)
+@d line_skip_limit dimen_par(line_skip_limit_code)
+@d hsize dimen_par(hsize_code)
+@d vsize dimen_par(vsize_code)
+@d max_depth dimen_par(max_depth_code)
+@d split_max_depth dimen_par(split_max_depth_code)
+@d box_max_depth dimen_par(box_max_depth_code)
+@d hfuzz dimen_par(hfuzz_code)
+@d vfuzz dimen_par(vfuzz_code)
+@d delimiter_shortfall dimen_par(delimiter_shortfall_code)
+@d null_delimiter_space dimen_par(null_delimiter_space_code)
+@d script_space dimen_par(script_space_code)
+@d pre_display_size dimen_par(pre_display_size_code)
+@d display_width dimen_par(display_width_code)
+@d display_indent dimen_par(display_indent_code)
+@d overfull_rule dimen_par(overfull_rule_code)
+@d hang_indent dimen_par(hang_indent_code)
+@d h_offset dimen_par(h_offset_code)
+@d v_offset dimen_par(v_offset_code)
+@d emergency_stretch dimen_par(emergency_stretch_code)
+@d page_height dimen_par(page_height_code)
+
+@p static void print_length_param(int @!n)
+{@+switch (n) {
+case par_indent_code: print_esc("parindent");@+break;
+case math_surround_code: print_esc("mathsurround");@+break;
+case line_skip_limit_code: print_esc("lineskiplimit");@+break;
+case hsize_code: print_esc("hsize");@+break;
+case vsize_code: print_esc("vsize");@+break;
+case max_depth_code: print_esc("maxdepth");@+break;
+case split_max_depth_code: print_esc("splitmaxdepth");@+break;
+case box_max_depth_code: print_esc("boxmaxdepth");@+break;
+case hfuzz_code: print_esc("hfuzz");@+break;
+case vfuzz_code: print_esc("vfuzz");@+break;
+case delimiter_shortfall_code: print_esc("delimitershortfall");@+break;
+case null_delimiter_space_code: print_esc("nulldelimiterspace");@+break;
+case script_space_code: print_esc("scriptspace");@+break;
+case pre_display_size_code: print_esc("predisplaysize");@+break;
+case display_width_code: print_esc("displaywidth");@+break;
+case display_indent_code: print_esc("displayindent");@+break;
+case overfull_rule_code: print_esc("overfullrule");@+break;
+case hang_indent_code: print_esc("hangindent");@+break;
+case h_offset_code: print_esc("hoffset");@+break;
+case v_offset_code: print_esc("voffset");@+break;
+case emergency_stretch_code: print_esc("emergencystretch");@+break;
+case page_width_code:
+ if (pdf_on) print_esc("pdfpagewidth");
+ else print_esc("pagewidth");
+ @+break;
+case page_height_code:
+ if (pdf_on) print_esc("pdfpageheight");
+ else print_esc("pageheight");
+ @+break;
+@<Cases for |print_lenght_param|@>@;
+default:print("[unknown dimen parameter!]");
+}
+}
+
+@ @<Put each...@>=
+primitive("parindent", assign_dimen, dimen_base+par_indent_code);@/
+@!@:par\_indent\_}{\.{\\parindent} primitive@>
+primitive("mathsurround", assign_dimen, dimen_base+math_surround_code);@/
+@!@:math\_surround\_}{\.{\\mathsurround} primitive@>
+primitive("lineskiplimit", assign_dimen, dimen_base+line_skip_limit_code);@/
+@!@:line\_skip\_limit\_}{\.{\\lineskiplimit} primitive@>
+primitive("hsize", assign_dimen, dimen_base+hsize_code);@/
+@!@:hsize\_}{\.{\\hsize} primitive@>
+primitive("vsize", assign_dimen, dimen_base+vsize_code);@/
+@!@:vsize\_}{\.{\\vsize} primitive@>
+primitive("maxdepth", assign_dimen, dimen_base+max_depth_code);@/
+@!@:max\_depth\_}{\.{\\maxdepth} primitive@>
+primitive("splitmaxdepth", assign_dimen, dimen_base+split_max_depth_code);@/
+@!@:split\_max\_depth\_}{\.{\\splitmaxdepth} primitive@>
+primitive("boxmaxdepth", assign_dimen, dimen_base+box_max_depth_code);@/
+@!@:box\_max\_depth\_}{\.{\\boxmaxdepth} primitive@>
+primitive("hfuzz", assign_dimen, dimen_base+hfuzz_code);@/
+@!@:hfuzz\_}{\.{\\hfuzz} primitive@>
+primitive("vfuzz", assign_dimen, dimen_base+vfuzz_code);@/
+@!@:vfuzz\_}{\.{\\vfuzz} primitive@>
+primitive("delimitershortfall",
+ assign_dimen, dimen_base+delimiter_shortfall_code);@/
+@!@:delimiter\_shortfall\_}{\.{\\delimitershortfall} primitive@>
+primitive("nulldelimiterspace",
+ assign_dimen, dimen_base+null_delimiter_space_code);@/
+@!@:null\_delimiter\_space\_}{\.{\\nulldelimiterspace} primitive@>
+primitive("scriptspace", assign_dimen, dimen_base+script_space_code);@/
+@!@:script\_space\_}{\.{\\scriptspace} primitive@>
+primitive("predisplaysize", assign_dimen, dimen_base+pre_display_size_code);@/
+@!@:pre\_display\_size\_}{\.{\\predisplaysize} primitive@>
+primitive("displaywidth", assign_dimen, dimen_base+display_width_code);@/
+@!@:display\_width\_}{\.{\\displaywidth} primitive@>
+primitive("displayindent", assign_dimen, dimen_base+display_indent_code);@/
+@!@:display\_indent\_}{\.{\\displayindent} primitive@>
+primitive("overfullrule", assign_dimen, dimen_base+overfull_rule_code);@/
+@!@:overfull\_rule\_}{\.{\\overfullrule} primitive@>
+primitive("hangindent", assign_dimen, dimen_base+hang_indent_code);@/
+@!@:hang\_indent\_}{\.{\\hangindent} primitive@>
+primitive("hoffset", assign_dimen, dimen_base+h_offset_code);@/
+@!@:h\_offset\_}{\.{\\hoffset} primitive@>
+primitive("voffset", assign_dimen, dimen_base+v_offset_code);@/
+@!@:v\_offset\_}{\.{\\voffset} primitive@>
+primitive("emergencystretch", assign_dimen, dimen_base+emergency_stretch_code);@/
+@!@:emergency\_stretch\_}{\.{\\emergencystretch} primitive@>
+
+@ @<Cases of |print_cmd_chr|...@>=
+case assign_dimen: if (chr_code < scaled_base)
+ print_length_param(chr_code-dimen_base);
+ else{@+print_esc("dimen");print_int(chr_code-scaled_base);
+ } @+break;
+
+@ @<Initialize table entries...@>=
+for (k=dimen_base; k<=eqtb_size; k++) eqtb[k].sc=0;
+
+@ @<Show equivalent |n|, in region 6@>=
+{@+if (n < scaled_base) print_length_param(n-dimen_base);
+else{@+print_esc("dimen");print_int(n-scaled_base);
+ }
+print_char('=');print_scaled(eqtb[n].sc);print("pt");
+}
+
+@ Here is a procedure that displays the contents of |eqtb[n]|
+symbolically.
+
+@p @t\4@>@<Declare the procedure called |print_cmd_chr|@>@;@/
+#ifdef @!STAT
+static void show_eqtb(pointer @!n)
+{@+if (n < active_base) print_char('?'); /*this can't happen*/
+else if (n < glue_base) @<Show equivalent |n|, in region 1 or 2@>@;
+else if (n < local_base) @<Show equivalent |n|, in region 3@>@;
+else if (n < int_base) @<Show equivalent |n|, in region 4@>@;
+else if (n < dimen_base) @<Show equivalent |n|, in region 5@>@;
+else if (n <= eqtb_size) @<Show equivalent |n|, in region 6@>@;
+else print_char('?'); /*this can't happen either*/
+}
+#endif
+
+@ The last two regions of |eqtb| have fullword values instead of the
+three fields |eq_level|, |eq_type|, and |equiv|. An |eq_type| is unnecessary,
+but \TeX\ needs to store the |eq_level| information in another array
+called |xeq_level|.
+
+@<Glob...@>=
+static memory_word @!eqtb0[eqtb_size-active_base+1],
+ *const @!eqtb = @!eqtb0-active_base;
+static quarterword @!xeq_level0[eqtb_size-int_base+1],
+ *const @!xeq_level = @!xeq_level0-int_base;
+
+@ @<Set init...@>=
+for (k=int_base; k<=eqtb_size; k++) xeq_level[k]=level_one;
+
+@ When the debugging routine |search_mem| is looking for pointers having a
+given value, it is interested only in regions 1 to~3 of~|eqtb|, and in the
+first part of region~4.
+
+@<Search |eqtb| for equivalents equal to |p|@>=
+for (q=active_base; q<=box_base+255; q++)
+ {@+if (equiv(q)==p)
+ {@+print_nl("EQUIV(");print_int(q);print_char(')');
+ }
+ }
+
+@* The hash table.
+Control sequences are stored and retrieved by means of a fairly standard hash
+table algorithm called the method of ``coalescing lists'' (cf.\ Algorithm 6.4C
+in {\sl The Art of Computer Programming\/}). Once a control sequence enters the
+table, it is never removed, because there are complicated situations
+involving \.{\\gdef} where the removal of a control sequence at the end of
+a group would be a mistake preventable only by the introduction of a
+complicated reference-count mechanism.
+
+The actual sequence of letters forming a control sequence identifier is
+stored in the |str_pool| array together with all the other strings. An
+auxiliary array |hash| consists of items with two halfword fields per
+word. The first of these, called |next(p)|, points to the next identifier
+belonging to the same coalesced list as the identifier corresponding to~|p|;
+and the other, called |text(p)|, points to the |str_start| entry for
+|p|'s identifier. If position~|p| of the hash table is empty, we have
+|text(p)==0|; if position |p| is either empty or the end of a coalesced
+hash list, we have |next(p)==0|. An auxiliary pointer variable called
+|hash_used| is maintained in such a way that all locations |p >= hash_used|
+are nonempty. The global variable |cs_count| tells how many multiletter
+control sequences have been defined, if statistics are being kept.
+
+A global boolean variable called |no_new_control_sequence| is set to
+|true| during the time that new hash table entries are forbidden.
+
+@d next(A) hash[A].lh /*link for coalesced lists*/
+@d text(A) hash[A].rh /*string number for control sequence name*/
+@d hash_is_full (hash_used==hash_base) /*test if all positions are occupied*/
+@d font_id_text(A) text(font_id_base+A) /*a frozen font identifier's name*/
+
+@<Glob...@>=
+static two_halves @!hash0[undefined_control_sequence-hash_base],
+ *const @!hash = @!hash0-hash_base;
+ /*the hash table*/
+static pointer @!hash_used; /*allocation pointer for |hash|*/
+static bool @!no_new_control_sequence; /*are new identifiers legal?*/
+static int @!cs_count; /*total number of known identifiers*/
+
+@ @<Set init...@>=
+no_new_control_sequence=true; /*new identifiers are usually forbidden*/
+next(hash_base)=0;text(hash_base)=0;
+for (k=hash_base+1; k<=undefined_control_sequence-1; k++) hash[k]=hash[hash_base];
+
+@ @<Initialize table entries...@>=
+hash_used=frozen_control_sequence; /*nothing is used*/
+cs_count=0;
+eq_type(frozen_dont_expand)=dont_expand;
+text(frozen_dont_expand)=s_no("notexpanded:");
+@.notexpanded:@>
+
+@ Here is the subroutine that searches the hash table for an identifier
+that matches a given string of length |l > 1| appearing in |buffer[j dotdot
+(j+l-1)]|. If the identifier is found, the corresponding hash table address
+is returned. Otherwise, if the global variable |no_new_control_sequence|
+is |true|, the dummy address |undefined_control_sequence| is returned.
+Otherwise the identifier is inserted into the hash table and its location
+is returned.
+
+@p static pointer id_lookup(int @!j, int @!l) /*search the hash table*/
+{@+ /*go here if you found it*/
+int h; /*hash code*/
+int @!d; /*number of characters in incomplete current string*/
+pointer @!p; /*index in |hash| array*/
+int @!k; /*index in |buffer| array*/
+@<Compute the hash code |h|@>;
+p=h+hash_base; /*we start searching here; note that |0 <= h < hash_prime|*/
+loop@+{@+if (text(p) > 0) if (length(text(p))==l)
+ if (str_eq_buf(text(p), j)) goto found;
+ if (next(p)==0)
+ {@+if (no_new_control_sequence)
+ p=undefined_control_sequence;
+ else@<Insert a new control sequence after |p|, then make |p| point to
+it@>;
+ goto found;
+ }
+ p=next(p);
+ }
+found: return p;
+}
+
+@ @<Insert a new control...@>=
+{@+if (text(p) > 0)
+ {@+@/do@+{if (hash_is_full) overflow("hash size", hash_size);
+@:TeX capacity exceeded hash size}{\quad hash size@>
+ decr(hash_used);
+ }@+ while (!(text(hash_used)==0)); /*search for an empty location in |hash|*/
+ next(p)=hash_used;p=hash_used;
+ }
+str_room(l);d=cur_length;
+while (pool_ptr > str_start[str_ptr])
+ {@+decr(pool_ptr);str_pool[pool_ptr+l]=str_pool[pool_ptr];
+ } /*move current string up to make room for another*/
+for (k=j; k<=j+l-1; k++) append_char(buffer[k]);
+text(p)=make_string();pool_ptr=pool_ptr+d;
+#ifdef @!STAT
+incr(cs_count);
+#endif
+@;@/
+}
+
+@ The value of |hash_prime| should be roughly 85\pct! of |hash_size|, and it
+should be a prime number. The theory of hashing tells us to expect fewer
+than two table probes, on the average, when the search is successful.
+[See J.~S. Vitter, {\sl Journal of the ACM\/ \bf30} (1983), 231--258.]
+@^Vitter, Jeffrey Scott@>
+
+@<Compute the hash code |h|@>=
+h=buffer[j];
+for (k=j+1; k<=j+l-1; k++)
+ {@+h=h+h+buffer[k];
+ while (h >= hash_prime) h=h-hash_prime;
+ }
+
+@ Single-character control sequences do not need to be looked up in a hash
+table, since we can use the character code itself as a direct address.
+The procedure |print_cs| prints the name of a control sequence, given
+a pointer to its address in |eqtb|. A space is printed after the name
+unless it is a single nonletter or an active character. This procedure
+might be invoked with invalid data, so it is ``extra robust.'' The
+individual characters must be printed one at a time using |print|, since
+they may be unprintable.
+
+@<Basic printing...@>=
+static void print_cs(int @!p) /*prints a purported control sequence*/
+{@+if (p < hash_base) /*single character*/
+ if (p >= single_base)
+ if (p==null_cs)
+ {@+print_esc("csname");print_esc("endcsname");print_char(' ');
+ }
+ else{@+printn_esc(p-single_base);
+ if (cat_code(p-single_base)==letter) print_char(' ');
+ }
+ else if (p < active_base) print_esc("IMPOSSIBLE.");
+@.IMPOSSIBLE@>
+ else printn(p-active_base);
+else if (p >= undefined_control_sequence) print_esc("IMPOSSIBLE.");
+else if ((text(p) < 0)||(text(p) >= str_ptr)) print_esc("NONEXISTENT.");
+@.NONEXISTENT@>
+else{@+if (p==frozen_primitive) print_esc("primitive");
+ printn_esc(text(p));print_char(' ');
+ }
+}
+
+@ Here is a similar procedure; it avoids the error checks, and it never
+prints a space after the control sequence.
+
+@<Basic printing procedures@>=
+static void sprint_cs(pointer @!p) /*prints a control sequence*/
+{@+if (p < hash_base)
+ if (p < single_base) printn(p-active_base);
+ else if (p < null_cs) printn_esc(p-single_base);
+ else{@+print_esc("csname");print_esc("endcsname");
+ }
+else printn_esc(text(p));
+}
+
+@ We need to put \TeX's ``primitive'' control sequences into the hash
+table, together with their command code (which will be the |eq_type|)
+and an operand (which will be the |equiv|). The |primitive| procedure
+does this, in a way that no \TeX\ user can. The global value |cur_val|
+contains the new |eqtb| pointer after |primitive| has acted.
+
+@p
+#ifdef @!INIT
+static void primitive(char *@!str, quarterword @!c, halfword @!o)
+{@+str_number s=s_no(str);
+int k; /*index into |str_pool|*/
+int @!j; /*index into |buffer|*/
+small_number @!l; /*length of the string*/
+pointer @!p; /*pointer in |ROM|*/
+if (s < 256) cur_val=s+single_base;
+else{@+k=str_start[s];l=str_start[s+1]-k;
+ /*we will move |s| into the (possibly non-empty) |buffer|*/
+ if (first+l > buf_size+1)
+ overflow("buffer size", buf_size);
+@:TeX capacity exceeded buffer size}{\quad buffer size@>
+ for (j=0; j<=l-1; j++) buffer[first+j]=so(str_pool[k+j]);
+ cur_val=id_lookup(first, l); /*|no_new_control_sequence| is |false|*/
+ flush_string;text(cur_val)=s; /*we don't want to have the string twice*/
+ }
+eq_level(cur_val)=level_one;eq_type(cur_val)=c;equiv(cur_val)=o;
+@<Add primitive definition to the |ROM| array@>;
+}
+#endif
+
+@ Many of \TeX's primitives need no |equiv|, since they are identifiable
+by their |eq_type| alone. These primitives are loaded into the hash table
+as follows:
+
+@<Put each of \TeX's primitives into the hash table@>=
+primitive(" ", ex_space, 0);@/
+@!@:Single-character primitives /}{\quad\.{\\\ }@>
+primitive("/", ital_corr, 0);@/
+@!@:Single-character primitives /}{\quad\.{\\/}@>
+primitive("accent", accent, 0);@/
+@!@:accent\_}{\.{\\accent} primitive@>
+primitive("advance", advance, 0);@/
+@!@:advance\_}{\.{\\advance} primitive@>
+primitive("afterassignment", after_assignment, 0);@/
+@!@:after\_assignment\_}{\.{\\afterassignment} primitive@>
+primitive("aftergroup", after_group, 0);@/
+@!@:after\_group\_}{\.{\\aftergroup} primitive@>
+primitive("begingroup", begin_group, 0);@/
+@!@:begin\_group\_}{\.{\\begingroup} primitive@>
+primitive("char", char_num, 0);@/
+@!@:char\_}{\.{\\char} primitive@>
+primitive("csname", cs_name, 0);@/
+@!@:cs\_name\_}{\.{\\csname} primitive@>
+primitive("delimiter", delim_num, 0);@/
+@!@:delimiter\_}{\.{\\delimiter} primitive@>
+primitive("divide", divide, 0);@/
+@!@:divide\_}{\.{\\divide} primitive@>
+primitive("endcsname", end_cs_name, 0);@/
+@!@:end\_cs\_name\_}{\.{\\endcsname} primitive@>
+primitive("endgroup", end_group, 0);
+@!@:end\_group\_}{\.{\\endgroup} primitive@>
+text(frozen_end_group)=text(cur_val);eqtb[frozen_end_group]=eqtb[cur_val];@/
+primitive("expandafter", expand_after, 0);@/
+@!@:expand\_after\_}{\.{\\expandafter} primitive@>
+primitive("font", def_font, 0);@/
+@!@:font\_}{\.{\\font} primitive@>
+primitive("fontdimen", assign_font_dimen, 0);@/
+@!@:font\_dimen\_}{\.{\\fontdimen} primitive@>
+primitive("halign", halign, 0);@/
+@!@:halign\_}{\.{\\halign} primitive@>
+primitive("hrule", hrule, 0);@/
+@!@:hrule\_}{\.{\\hrule} primitive@>
+primitive("ignorespaces", ignore_spaces, 0);@/
+@!@:ignore\_spaces\_}{\.{\\ignorespaces} primitive@>
+primitive("insert", insert, 0);@/
+@!@:insert\_}{\.{\\insert} primitive@>
+primitive("mark", mark, 0);@/
+@!@:mark\_}{\.{\\mark} primitive@>
+primitive("mathaccent", math_accent, 0);@/
+@!@:math\_accent\_}{\.{\\mathaccent} primitive@>
+primitive("mathchar", math_char_num, 0);@/
+@!@:math\_char\_}{\.{\\mathchar} primitive@>
+primitive("mathchoice", math_choice, 0);@/
+@!@:math\_choice\_}{\.{\\mathchoice} primitive@>
+primitive("multiply", multiply, 0);@/
+@!@:multiply\_}{\.{\\multiply} primitive@>
+primitive("noalign", no_align, 0);@/
+@!@:no\_align\_}{\.{\\noalign} primitive@>
+primitive("noboundary", no_boundary, 0);@/
+@!@:no\_boundary\_}{\.{\\noboundary} primitive@>
+primitive("noexpand", no_expand, 0);@/
+@!@:no\_expand\_}{\.{\\noexpand} primitive@>
+primitive("nonscript", non_script, 0);@/
+@!@:non\_script\_}{\.{\\nonscript} primitive@>
+primitive("omit", omit, 0);@/
+@!@:omit\_}{\.{\\omit} primitive@>
+primitive("parshape", set_shape, par_shape_loc);@/
+@!@:par\_shape\_}{\.{\\parshape} primitive@>
+primitive("penalty", break_penalty, 0);@/
+@!@:penalty\_}{\.{\\penalty} primitive@>
+primitive("prevgraf", set_prev_graf, 0);@/
+@!@:prev\_graf\_}{\.{\\prevgraf} primitive@>
+primitive("radical", radical, 0);@/
+@!@:radical\_}{\.{\\radical} primitive@>
+primitive("read", read_to_cs, 0);@/
+@!@:read\_}{\.{\\read} primitive@>
+primitive("relax", relax, 256); /*cf.\ |scan_file_name|*/
+@!@:relax\_}{\.{\\relax} primitive@>
+text(frozen_relax)=text(cur_val);eqtb[frozen_relax]=eqtb[cur_val];@/
+primitive("setbox", set_box, 0);@/
+@!@:set\_box\_}{\.{\\setbox} primitive@>
+primitive("the", the, 0);@/
+@!@:the\_}{\.{\\the} primitive@>
+primitive("toks", toks_register, mem_bot);@/
+@!@:toks\_}{\.{\\toks} primitive@>
+primitive("vadjust", vadjust, 0);@/
+@!@:vadjust\_}{\.{\\vadjust} primitive@>
+primitive("valign", valign, 0);@/
+@!@:valign\_}{\.{\\valign} primitive@>
+primitive("vcenter", vcenter, 0);@/
+@!@:vcenter\_}{\.{\\vcenter} primitive@>
+primitive("vrule", vrule, 0);@/
+@!@:vrule\_}{\.{\\vrule} primitive@>
+
+@ Each primitive has a corresponding inverse, so that it is possible to
+display the cryptic numeric contents of |eqtb| in symbolic form.
+Every call of |primitive| in this program is therefore accompanied by some
+straightforward code that forms part of the |print_cmd_chr| routine
+below.
+
+@<Cases of |print_cmd_chr|...@>=
+case accent: print_esc("accent");@+break;
+case advance: print_esc("advance");@+break;
+case after_assignment: print_esc("afterassignment");@+break;
+case after_group: print_esc("aftergroup");@+break;
+case assign_font_dimen: print_esc("fontdimen");@+break;
+case begin_group: print_esc("begingroup");@+break;
+case break_penalty: print_esc("penalty");@+break;
+case char_num: print_esc("char");@+break;
+case cs_name: print_esc("csname");@+break;
+case def_font: print_esc("font");@+break;
+case delim_num: print_esc("delimiter");@+break;
+case divide: print_esc("divide");@+break;
+case end_cs_name: print_esc("endcsname");@+break;
+case end_group: print_esc("endgroup");@+break;
+case ex_space: print_esc(" ");@+break;
+case expand_after: switch (chr_code) {
+case 0: print_esc("expandafter");@+break;
+@/@<Cases of |expandafter| for |print_cmd_chr|@>@/
+} @+break; /*there are no other cases*/
+case halign: print_esc("halign");@+break;
+case hrule: print_esc("hrule");@+break;
+case ignore_spaces: print_esc("ignorespaces");@+break;
+case insert: print_esc("insert");@+break;
+case ital_corr: print_esc("/");@+break;
+case mark: {@+print_esc("mark");
+ if (chr_code > 0) print_char('s');
+ } @+break;
+case math_accent: print_esc("mathaccent");@+break;
+case math_char_num: print_esc("mathchar");@+break;
+case math_choice: print_esc("mathchoice");@+break;
+case multiply: print_esc("multiply");@+break;
+case no_align: print_esc("noalign");@+break;
+case no_boundary: print_esc("noboundary");@+break;
+case no_expand: print_esc("noexpand");@+break;
+case non_script: print_esc("nonscript");@+break;
+case omit: print_esc("omit");@+break;
+case radical: print_esc("radical");@+break;
+case read_to_cs: if (chr_code==0) print_esc("read")
+ @<Cases of |read| for |print_cmd_chr|@>;@+break;
+case relax: print_esc("relax");@+break;
+case set_box: print_esc("setbox");@+break;
+case set_prev_graf: print_esc("prevgraf");@+break;
+case set_shape: switch (chr_code) {
+ case par_shape_loc: print_esc("parshape");@+break;
+ @<Cases of |set_shape| for |print_cmd_chr|@>@;@/
+ } @+break; /*there are no other cases*/
+case the: if (chr_code==0) print_esc("the")
+ @<Cases of |the| for |print_cmd_chr|@>;@+break;
+case toks_register: @<Cases of |toks_register| for |print_cmd_chr|@>@;@+break;
+case vadjust: print_esc("vadjust");@+break;
+case valign: print_esc("valign");@+break;
+case vcenter: print_esc("vcenter");@+break;
+case vrule: print_esc("vrule");@+break;
+
+@ We will deal with the other primitives later, at some point in the program
+where their |eq_type| and |equiv| values are more meaningful. For example,
+the primitives for math mode will be loaded when we consider the routines
+that deal with formulas. It is easy to find where each particular
+primitive was treated by looking in the index at the end; for example, the
+section where |"radical"| entered |eqtb| is listed under `\.{\\radical}
+primitive'. (Primitives consisting of a single nonalphabetic character,
+@!like `\.{\\/}', are listed under `Single-character primitives'.)
+@!@^Single-character primitives@>
+
+Meanwhile, this is a convenient place to catch up on something we were unable
+to do before the hash table was defined:
+
+@<Print the font identifier for |font(p)|@>=
+printn_esc(font_id_text(font(p)))
+
+@* Saving and restoring equivalents.
+The nested structure provided by `$\.{\char'173}\ldots\.{\char'175}$' groups
+in \TeX\ means that |eqtb| entries valid in outer groups should be saved
+and restored later if they are overridden inside the braces. When a new |eqtb|
+value is being assigned, the program therefore checks to see if the previous
+entry belongs to an outer level. In such a case, the old value is placed
+on the |save_stack| just before the new value enters |eqtb|. At the
+end of a grouping level, i.e., when the right brace is sensed, the
+|save_stack| is used to restore the outer values, and the inner ones are
+destroyed.
+
+Entries on the |save_stack| are of type |memory_word|. The top item on
+this stack is |save_stack[p]|, where |p==save_ptr-1|; it contains three
+fields called |save_type|, |save_level|, and |save_index|, and it is
+interpreted in one of five ways:
+
+\yskip\hangg 1) If |save_type(p)==restore_old_value|, then
+|save_index(p)| is a location in |eqtb| whose current value should
+be destroyed at the end of the current group and replaced by |save_stack[p-1]|.
+Furthermore if |save_index(p) >= int_base|, then |save_level(p)|
+should replace the corresponding entry in |xeq_level|.
+
+\yskip\hangg 2) If |save_type(p)==restore_zero|, then |save_index(p)|
+is a location in |eqtb| whose current value should be destroyed at the end
+of the current group, when it should be
+replaced by the value of |eqtb[undefined_control_sequence]|.
+
+\yskip\hangg 3) If |save_type(p)==insert_token|, then |save_index(p)|
+is a token that should be inserted into \TeX's input when the current
+group ends.
+
+\yskip\hangg 4) If |save_type(p)==level_boundary|, then |save_level(p)|
+is a code explaining what kind of group we were previously in, and
+|save_index(p)| points to the level boundary word at the bottom of
+the entries for that group.
+Furthermore, in extended \eTeX\ mode, |save_stack[p-1]| contains the
+source line number at which the current level of grouping was entered.
+
+\yskip\hang 5) If |save_type(p)==restore_sa|, then |sa_chain| points to a
+chain of sparse array entries to be restored at the end of the current
+group. Furthermore |save_index(p)| and |save_level(p)| should replace
+the values of |sa_chain| and |sa_level| respectively.
+
+@d save_type(A) save_stack[A].hh.b0 /*classifies a |save_stack| entry*/
+@d save_level(A) save_stack[A].hh.b1
+ /*saved level for regions 5 and 6, or group code*/
+@d save_index(A) save_stack[A].hh.rh
+ /*|eqtb| location or token or |save_stack| location*/
+@d restore_old_value 0 /*|save_type| when a value should be restored later*/
+@d restore_zero 1 /*|save_type| when an undefined entry should be restored*/
+@d insert_token 2 /*|save_type| when a token is being saved for later use*/
+@d level_boundary 3 /*|save_type| corresponding to beginning of group*/
+@d restore_sa 4 /*|save_type| when sparse array entries should be restored*/
+
+@p @t\4@>@<Declare \eTeX\ procedures for tracing and input@>@;
+
+@ Here are the group codes that are used to discriminate between different
+kinds of groups. They allow \TeX\ to decide what special actions, if any,
+should be performed when a group ends.
+\def\grp{\.{\char'173...\char'175}}
+
+Some groups are not supposed to be ended by right braces. For example,
+the `\.\$' that begins a math formula causes a |math_shift_group| to
+be started, and this should be terminated by a matching `\.\$'. Similarly,
+a group that starts with \.{\\left} should end with \.{\\right}, and
+one that starts with \.{\\begingroup} should end with \.{\\endgroup}.
+
+@d bottom_level 0 /*group code for the outside world*/
+@d simple_group 1 /*group code for local structure only*/
+@d hbox_group 2 /*code for `\.{\\hbox}\grp'*/
+@d adjusted_hbox_group 3 /*code for `\.{\\hbox}\grp' in vertical mode*/
+@d vbox_group 4 /*code for `\.{\\vbox}\grp'*/
+@d vtop_group 5 /*code for `\.{\\vtop}\grp'*/
+@d align_group 6 /*code for `\.{\\halign}\grp', `\.{\\valign}\grp'*/
+@d no_align_group 7 /*code for `\.{\\noalign}\grp'*/
+@d output_group 8 /*code for output routine*/
+@d math_group 9 /*code for, e.g., `\.{\char'136}\grp'*/
+@d disc_group 10 /*code for `\.{\\discretionary}\grp\grp\grp'*/
+@d insert_group 11 /*code for `\.{\\insert}\grp', `\.{\\vadjust}\grp'*/
+@d vcenter_group 12 /*code for `\.{\\vcenter}\grp'*/
+@d math_choice_group 13 /*code for `\.{\\mathchoice}\grp\grp\grp\grp'*/
+@d semi_simple_group 14 /*code for `\.{\\begingroup...\\endgroup}'*/
+@d math_shift_group 15 /*code for `\.{\$...\$}'*/
+@d math_left_group 16 /*code for `\.{\\left...\\right}'*/
+@d max_group_code 16
+
+@<Types...@>=
+typedef int8_t group_code; /*|save_level| for a level boundary*/
+
+@ The global variable |cur_group| keeps track of what sort of group we are
+currently in. Another global variable, |cur_boundary|, points to the
+topmost |level_boundary| word. And |cur_level| is the current depth of
+nesting. The routines are designed to preserve the condition that no entry
+in the |save_stack| or in |eqtb| ever has a level greater than |cur_level|.
+
+@ @<Glob...@>=
+static memory_word @!save_stack[save_size+1];
+static int @!save_ptr; /*first unused entry on |save_stack|*/
+static int @!max_save_stack; /*maximum usage of save stack*/
+static quarterword @!cur_level; /*current nesting level for groups*/
+static group_code @!cur_group; /*current group type*/
+static int @!cur_boundary; /*where the current level begins*/
+
+@ At this time it might be a good idea for the reader to review the introduction
+to |eqtb| that was given above just before the long lists of parameter names.
+Recall that the ``outer level'' of the program is |level_one|, since
+undefined control sequences are assumed to be ``defined'' at |level_zero|.
+
+@<Set init...@>=
+save_ptr=0;cur_level=level_one;cur_group=bottom_level;cur_boundary=0;
+max_save_stack=0;
+
+@ The following macro is used to test if there is room for up to seven more
+entries on |save_stack|. By making a conservative test like this, we can
+get by with testing for overflow in only a few places.
+
+@d check_full_save_stack if (save_ptr > max_save_stack)
+ {@+max_save_stack=save_ptr;
+ if (max_save_stack > save_size-7) overflow("save size", save_size);
+@:TeX capacity exceeded save size}{\quad save size@>
+ }
+
+@ Procedure |new_save_level| is called when a group begins. The
+argument is a group identification code like `|hbox_group|'. After
+calling this routine, it is safe to put five more entries on |save_stack|.
+
+In some cases integer-valued items are placed onto the
+|save_stack| just below a |level_boundary| word, because this is a
+convenient place to keep information that is supposed to ``pop up'' just
+when the group has finished.
+For example, when `\.{\\hbox to 100pt}\grp' is being treated, the 100pt
+dimension is stored on |save_stack| just before |new_save_level| is
+called.
+
+We use the notation |saved(k)| to stand for an integer item that
+appears in location |save_ptr+k| of the save stack.
+
+@d saved(A) save_stack[save_ptr+A].i
+
+@p static void new_save_level(group_code @!c) /*begin a new level of grouping*/
+{@+check_full_save_stack;
+if (eTeX_ex)
+ {@+saved(0)=line;incr(save_ptr);
+ }
+save_type(save_ptr)=level_boundary;save_level(save_ptr)=cur_group;
+save_index(save_ptr)=cur_boundary;
+if (cur_level==max_quarterword) overflow("grouping levels",
+@:TeX capacity exceeded grouping levels}{\quad grouping levels@>
+ max_quarterword-min_quarterword);
+ /*quit if |(cur_level+1)| is too big to be stored in |eqtb|*/
+cur_boundary=save_ptr;cur_group=c;
+#ifdef @!STAT
+if (tracing_groups > 0) group_trace(false);
+#endif
+@;@/
+incr(cur_level);incr(save_ptr);
+}
+
+@ Just before an entry of |eqtb| is changed, the following procedure should
+be called to update the other data structures properly. It is important
+to keep in mind that reference counts in |mem| include references from
+within |save_stack|, so these counts must be handled carefully.
+@^reference counts@>
+
+@p static void eq_destroy(memory_word @!w) /*gets ready to forget |w|*/
+{@+pointer q; /*|equiv| field of |w|*/
+switch (eq_type_field(w)) {
+case call: case long_call: case outer_call:
+ case long_outer_call: delete_token_ref(equiv_field(w));@+break;
+case glue_ref: delete_glue_ref(equiv_field(w));@+break;
+case shape_ref: {@+q=equiv_field(w); /*we need to free a \.{\\parshape} block*/
+ if (q!=null) free_node(q, info(q)+info(q)+1);
+ } @+break; /*such a block is |2 n+1| words long, where |n==info(q)|*/
+case box_ref: flush_node_list(equiv_field(w));@+break;
+@/@<Cases for |eq_destroy|@>@/
+default:do_nothing;
+}
+}
+
+@ To save a value of |eqtb[p]| that was established at level |l|, we
+can use the following subroutine.
+
+@p static void eq_save(pointer @!p, quarterword @!l) /*saves |eqtb[p]|*/
+{@+check_full_save_stack;
+if (l==level_zero) save_type(save_ptr)=restore_zero;
+else{@+save_stack[save_ptr]=eqtb[p];incr(save_ptr);
+ save_type(save_ptr)=restore_old_value;
+ }
+save_level(save_ptr)=l;save_index(save_ptr)=p;incr(save_ptr);
+}
+
+@ The procedure |eq_define| defines an |eqtb| entry having specified
+|eq_type| and |equiv| fields, and saves the former value if appropriate.
+This procedure is used only for entries in the first four regions of |eqtb|,
+i.e., only for entries that have |eq_type| and |equiv| fields.
+After calling this routine, it is safe to put four more entries on
+|save_stack|, provided that there was room for four more entries before
+the call, since |eq_save| makes the necessary test.
+
+@p
+#ifdef @!STAT
+#define assign_trace(A, B) if (tracing_assigns > 0) restore_trace(A, B);
+#else
+#define assign_trace(A, B)
+#endif
+
+static void eq_define(pointer @!p, quarterword @!t, halfword @!e)
+ /*new data for |eqtb|*/
+{@+
+if (eTeX_ex&&(eq_type(p)==t)&&(equiv(p)==e))
+ {@+assign_trace(p,"reassigning")@;@/
+ eq_destroy(eqtb[p]);return;
+ }
+assign_trace(p,"changing")@;@/
+if (eq_level(p)==cur_level) eq_destroy(eqtb[p]);
+else if (cur_level > level_one) eq_save(p, eq_level(p));
+eq_level(p)=cur_level;eq_type(p)=t;equiv(p)=e;
+assign_trace(p,"into")@;@/
+}
+
+@ The counterpart of |eq_define| for the remaining (fullword) positions in
+|eqtb| is called |eq_word_define|. Since |xeq_level[p] >= level_one| for all
+|p|, a `|restore_zero|' will never be used in this case.
+
+@p static void eq_word_define(pointer @!p, int @!w)
+{@+
+if (eTeX_ex&&(eqtb[p].i==w))
+ {@+assign_trace(p,"reassigning")@;@/
+ return;
+ }
+assign_trace(p,"changing")@;@/
+if (xeq_level[p]!=cur_level)
+ {@+eq_save(p, xeq_level[p]);xeq_level[p]=cur_level;
+ }
+eqtb[p].i=w;
+assign_trace(p,"into")@;@/
+}
+
+@ The |eq_define| and |eq_word_define| routines take care of local definitions.
+@^global definitions@>
+Global definitions are done in almost the same way, but there is no need
+to save old values, and the new value is associated with |level_one|.
+
+@p static void geq_define(pointer @!p, quarterword @!t, halfword @!e)
+ /*global |eq_define|*/
+{@+assign_trace(p,"globally changing")@;@/
+{@+eq_destroy(eqtb[p]);
+eq_level(p)=level_one;eq_type(p)=t;equiv(p)=e;
+}
+assign_trace(p,"into");@/
+}
+@#
+static void geq_word_define(pointer @!p, int @!w) /*global |eq_word_define|*/
+{@+assign_trace(p,"globally changing")@;@/
+{@+eqtb[p].i=w;xeq_level[p]=level_one;
+}
+assign_trace(p,"into");@/
+}
+
+@ Subroutine |save_for_after| puts a token on the stack for save-keeping.
+
+@p static void save_for_after(halfword @!t)
+{@+if (cur_level > level_one)
+ {@+check_full_save_stack;
+ save_type(save_ptr)=insert_token;save_level(save_ptr)=level_zero;
+ save_index(save_ptr)=t;incr(save_ptr);
+ }
+}
+
+@ The |unsave| routine goes the other way, taking items off of |save_stack|.
+This routine takes care of restoration when a level ends; everything
+belonging to the topmost group is cleared off of the save stack.
+
+@p
+static void back_input(void);
+static void unsave(void) /*pops the top level off the save stack*/
+{@+
+pointer p; /*position to be restored*/
+quarterword @!l; /*saved level, if in fullword regions of |eqtb|*/
+halfword @!t; /*saved value of |cur_tok|*/
+bool @!a; /*have we already processed an \.{\\aftergroup} ?*/
+a=false;
+if (cur_level > level_one)
+ {@+decr(cur_level);
+ @<Clear off top level from |save_stack|@>;
+ }
+else confusion("curlevel"); /*|unsave| is not used when |cur_group==bottom_level|*/
+@:this can't happen curlevel}{\quad curlevel@>
+}
+
+@ @<Clear off...@>=
+loop@+{@+decr(save_ptr);
+ if (save_type(save_ptr)==level_boundary) goto done;
+ p=save_index(save_ptr);
+ if (save_type(save_ptr)==insert_token)
+ @<Insert token |p| into \TeX's input@>@;
+ else if (save_type(save_ptr)==restore_sa)
+ {@+sa_restore();sa_chain=p;sa_level=save_level(save_ptr);
+ }
+ else{@+if (save_type(save_ptr)==restore_old_value)
+ {@+l=save_level(save_ptr);decr(save_ptr);
+ }
+ else save_stack[save_ptr]=eqtb[undefined_control_sequence];
+ @<Store \(s)|save_stack[save_ptr]| in |eqtb[p]|, unless |eqtb[p]| holds
+a global value@>;
+ }
+ }
+done:
+#ifdef @!STAT
+if (tracing_groups > 0) group_trace(true);
+#endif
+@;@/
+if (grp_stack[in_open]==cur_boundary) group_warning();
+ /*groups possibly not properly nested with files*/
+cur_group=save_level(save_ptr);cur_boundary=save_index(save_ptr);
+if (eTeX_ex) decr(save_ptr)
+
+@ A global definition, which sets the level to |level_one|,
+@^global definitions@>
+will not be undone by |unsave|. If at least one global definition of
+|eqtb[p]| has been carried out within the group that just ended, the
+last such definition will therefore survive.
+
+@<Store \(s)|save...@>=
+if (p < int_base)
+ if (eq_level(p)==level_one)
+ {@+eq_destroy(save_stack[save_ptr]); /*destroy the saved value*/
+#ifdef @!STAT
+ if (tracing_restores > 0) restore_trace(p,"retaining");
+#endif
+@;@/
+ }
+ else{@+eq_destroy(eqtb[p]); /*destroy the current value*/
+ eqtb[p]=save_stack[save_ptr]; /*restore the saved value*/
+#ifdef @!STAT
+ if (tracing_restores > 0) restore_trace(p,"restoring");
+#endif
+@;@/
+ }
+else if (xeq_level[p]!=level_one)
+ {@+eqtb[p]=save_stack[save_ptr];xeq_level[p]=l;
+#ifdef @!STAT
+ if (tracing_restores > 0) restore_trace(p,"restoring");
+#endif
+@;@/
+ }
+else{
+#ifdef @!STAT
+ if (tracing_restores > 0) restore_trace(p,"retaining");
+#endif
+@;@/
+ }
+
+@ @<Declare \eTeX\ procedures for tr...@>=
+#ifdef @!STAT
+static void restore_trace(pointer @!p, char *@!s)
+ /*|eqtb[p]| has just been restored or retained*/
+{@+begin_diagnostic();print_char('{');print(s);print_char(' ');
+show_eqtb(p);print_char('}');
+end_diagnostic(false);
+}
+#endif
+
+@ When looking for possible pointers to a memory location, it is helpful
+to look for references from |eqtb| that might be waiting on the
+save stack. Of course, we might find spurious pointers too; but this
+routine is merely an aid when debugging, and at such times we are
+grateful for any scraps of information, even if they prove to be irrelevant.
+@^dirty \PASCAL@>
+
+@<Search |save_stack| for equivalents that point to |p|@>=
+if (save_ptr > 0) for (q=0; q<=save_ptr-1; q++)
+ {@+if (equiv_field(save_stack[q])==p)
+ {@+print_nl("SAVE(");print_int(q);print_char(')');
+ }
+ }
+
+@ Most of the parameters kept in |eqtb| can be changed freely, but there's
+an exception: The magnification should not be used with two different
+values during any \TeX\ job, since a single magnification is applied to an
+entire run. The global variable |mag_set| is set to the current magnification
+whenever it becomes necessary to ``freeze'' it at a particular value.
+
+@<Glob...@>=
+static int @!mag_set; /*if nonzero, this magnification should be used henceforth*/
+
+@ @<Set init...@>=
+mag_set=0;
+
+@ The |prepare_mag| subroutine is called whenever \TeX\ wants to use |mag|
+for magnification.
+
+@p static void prepare_mag(void)
+{@+if ((mag_set > 0)&&(mag!=mag_set))
+ {@+print_err("Incompatible magnification (");print_int(mag);
+@.Incompatible magnification@>
+ print(");");print_nl(" the previous value will be retained");
+ help2("I can handle only one magnification ratio per job. So I've",@/
+ "reverted to the magnification you used earlier on this run.");@/
+ int_error(mag_set);
+ geq_word_define(int_base+mag_code, mag_set); /*|mag=mag_set|*/
+ }
+if ((mag <= 0)||(mag > 32768))
+ {@+print_err("Illegal magnification has been changed to 1000");@/
+@.Illegal magnification...@>
+ help1("The magnification ratio must be between 1 and 32768.");
+ int_error(mag);geq_word_define(int_base+mag_code, 1000);
+ }
+mag_set=mag;
+}
+
+@* Token lists.
+A \TeX\ token is either a character or a control sequence, and it is
+@^token@>
+represented internally in one of two ways: (1)~A character whose ASCII
+code number is |c| and whose command code is |m| is represented as the
+number $2^8m+c$; the command code is in the range |1 <= m <= 14|. (2)~A control
+sequence whose |eqtb| address is |p| is represented as the number
+|cs_token_flag+p|. Here |cs_token_flag==@t$2^{12}-1$@>| is larger than
+$2^8m+c$, yet it is small enough that |cs_token_flag+p < max_halfword|;
+thus, a token fits comfortably in a halfword.
+
+A token |t| represents a |left_brace| command if and only if
+|t < left_brace_limit|; it represents a |right_brace| command if and only if
+we have |left_brace_limit <= t < right_brace_limit|; and it represents a |match| or
+|end_match| command if and only if |match_token <= t <= end_match_token|.
+The following definitions take care of these token-oriented constants
+and a few others.
+
+@d cs_token_flag 07777 /*amount added to the |eqtb| location in a
+ token that stands for a control sequence; is a multiple of~256, less~1*/
+@d left_brace_token 00400 /*$2^8\cdot|left_brace|$*/
+@d left_brace_limit 01000 /*$2^8\cdot(|left_brace|+1)$*/
+@d right_brace_token 01000 /*$2^8\cdot|right_brace|$*/
+@d right_brace_limit 01400 /*$2^8\cdot(|right_brace|+1)$*/
+@d math_shift_token 01400 /*$2^8\cdot|math_shift|$*/
+@d tab_token 02000 /*$2^8\cdot|tab_mark|$*/
+@d out_param_token 02400 /*$2^8\cdot|out_param|$*/
+@d space_token 05040 /*$2^8\cdot|spacer|+|' '|$*/
+@d letter_token 05400 /*$2^8\cdot|letter|$*/
+@d other_token 06000 /*$2^8\cdot|other_char|$*/
+@d match_token 06400 /*$2^8\cdot|match|$*/
+@d end_match_token 07000 /*$2^8\cdot|end_match|$*/
+@d protected_token 07001 /*$2^8\cdot|end_match|+1$*/
+
+@ @<Check the ``constant''...@>=
+if (cs_token_flag+undefined_control_sequence > max_halfword) bad=21;
+
+@ A token list is a singly linked list of one-word nodes in |mem|, where
+each word contains a token and a link. Macro definitions, output-routine
+definitions, marks, \.{\\write} texts, and a few other things
+are remembered by \TeX\ in the form
+of token lists, usually preceded by a node with a reference count in its
+|token_ref_count| field. The token stored in location |p| is called
+|info(p)|.
+
+Three special commands appear in the token lists of macro definitions.
+When |m==match|, it means that \TeX\ should scan a parameter
+for the current macro; when |m==end_match|, it means that parameter
+matching should end and \TeX\ should start reading the macro text; and
+when |m==out_param|, it means that \TeX\ should insert parameter
+number |c| into the text at this point.
+
+The enclosing \.{\char'173} and \.{\char'175} characters of a macro
+definition are omitted, but an output routine
+will be enclosed in braces.
+
+Here is an example macro definition that illustrates these conventions.
+After \TeX\ processes the text
+$$\.{\\def\\mac a\#1\#2 \\b \{\#1\\-a \#\#1\#2 \#2\}}$$
+the definition of \.{\\mac} is represented as a token list containing
+$$\def\,{\hskip2pt}
+\vbox{\halign{\hfil#\hfil\cr
+(reference count), |letter|\,\.a, |match|\,\#, |match|\,\#, |spacer|\,\.\ ,
+\.{\\b}, |end_match|,\cr
+|out_param|\,1, \.{\\-}, |letter|\,\.a, |spacer|\,\.\ , |mac_param|\,\#,
+|other_char|\,\.1,\cr
+|out_param|\,2, |spacer|\,\.\ , |out_param|\,2.\cr}}$$
+The procedure |scan_toks| builds such token lists, and |macro_call|
+does the parameter matching.
+@^reference counts@>
+
+Examples such as
+$$\.{\\def\\m\{\\def\\m\{a\}\ b\}}$$
+explain why reference counts would be needed even if \TeX\ had no \.{\\let}
+operation: When the token list for \.{\\m} is being read, the redefinition of
+\.{\\m} changes the |eqtb| entry before the token list has been fully
+consumed, so we dare not simply destroy a token list when its
+control sequence is being redefined.
+
+If the parameter-matching part of a definition ends with `\.{\#\{}',
+the corresponding token list will have `\.\{' just before the `|end_match|'
+and also at the very end. The first `\.\{' is used to delimit the parameter; the
+second one keeps the first from disappearing.
+
+@ The procedure |show_token_list|, which prints a symbolic form of
+the token list that starts at a given node |p|, illustrates these
+conventions. The token list being displayed should not begin with a reference
+count. However, the procedure is intended to be robust, so that if the
+memory links are awry or if |p| is not really a pointer to a token list,
+nothing catastrophic will happen.
+
+An additional parameter |q| is also given; this parameter is either null
+or it points to a node in the token list where a certain magic computation
+takes place that will be explained later. (Basically, |q| is non-null when
+we are printing the two-line context information at the time of an error
+message; |q| marks the place corresponding to where the second line
+should begin.)
+
+For example, if |p| points to the node containing the first \.a in the
+token list above, then |show_token_list| will print the string
+$$\hbox{`\.{a\#1\#2\ \\b\ ->\#1\\-a\ \#\#1\#2\ \#2}';}$$
+and if |q| points to the node containing the second \.a,
+the magic computation will be performed just before the second \.a is printed.
+
+The generation will stop, and `\.{\\ETC.}' will be printed, if the length
+of printing exceeds a given limit~|l|. Anomalous entries are printed in the
+form of control sequences that are not followed by a blank space, e.g.,
+`\.{\\BAD.}'; this cannot be confused with actual control sequences because
+a real control sequence named \.{BAD} would come out `\.{\\BAD\ }'.
+
+@<Declare the procedure called |show_token_list|@>=
+static void show_token_list(int @!p, int @!q, int @!l)
+{@+
+int m, @!c; /*pieces of a token*/
+ASCII_code @!match_chr; /*character used in a `|match|'*/
+ASCII_code @!n; /*the highest parameter number, as an ASCII digit*/
+match_chr='#';n='0';tally=0;
+while ((p!=null)&&(tally < l))
+ {@+if (p==q) @<Do magic computation@>;
+ @<Display token |p|, and |return| if there are problems@>;
+ p=link(p);
+ }
+if (p!=null) print_esc("ETC.");
+@.ETC@>
+
+}
+
+@ @<Display token |p|...@>=
+if ((p < hi_mem_min)||(p > mem_end))
+ {@+print_esc("CLOBBERED.");return;
+@.CLOBBERED@>
+ }
+if (info(p) >= cs_token_flag) print_cs(info(p)-cs_token_flag);
+else{@+m=info(p)/0400;c=info(p)%0400;
+ if (info(p) < 0) print_esc("BAD.");
+@.BAD@>
+ else@<Display the token $(|m|,|c|)$@>;
+ }
+
+@ The procedure usually ``learns'' the character code used for macro
+parameters by seeing one in a |match| command before it runs into any
+|out_param| commands.
+
+@<Display the token...@>=
+switch (m) {
+case left_brace: case right_brace: case math_shift:
+ case tab_mark: case sup_mark: case sub_mark:
+ case spacer:
+ case letter: case other_char: printn(c);@+break;
+case mac_param: {@+printn(c);printn(c);
+ } @+break;
+case out_param: {@+printn(match_chr);
+ if (c <= 9) print_char(c+'0');
+ else{@+print_char('!');return;
+ }
+ } @+break;
+case match: {@+match_chr=c;printn(c);incr(n);print_char(n);
+ if (n > '9') return;
+ } @+break;
+case end_match: if (c==0) print("->");@+break;
+@.->@>
+default:print_esc("BAD.");
+@.BAD@>
+}
+
+@ Here's the way we sometimes want to display a token list, given a pointer
+to its reference count; the pointer may be null.
+
+@p static void token_show(pointer @!p)
+{@+if (p!=null) show_token_list(link(p), null, 10000000);
+}
+
+@ The |print_meaning| subroutine displays |cur_cmd| and |cur_chr| in
+symbolic form, including the expansion of a macro or mark.
+
+@p static void print_meaning(void)
+{@+print_cmd_chr(cur_cmd, cur_chr);
+if (cur_cmd >= call)
+ {@+print_char(':');print_ln();token_show(cur_chr);
+ }
+else if ((cur_cmd==top_bot_mark)&&(cur_chr < marks_code))
+ {@+print_char(':');print_ln();
+ token_show(cur_mark[cur_chr]);
+ }
+}
+
+@* Introduction to the syntactic routines.
+Let's pause a moment now and try to look at the Big Picture.
+The \TeX\ program consists of three main parts: syntactic routines,
+semantic routines, and output routines. The chief purpose of the
+syntactic routines is to deliver the user's input to the semantic routines,
+one token at a time. The semantic routines act as an interpreter
+responding to these tokens, which may be regarded as commands. And the
+output routines are periodically called on to convert box-and-glue
+lists into a compact set of instructions that will be sent
+to a typesetter. We have discussed the basic data structures and utility
+routines of \TeX, so we are good and ready to plunge into the real activity by
+considering the syntactic routines.
+
+Our current goal is to come to grips with the |get_next| procedure,
+which is the keystone of \TeX's input mechanism. Each call of |get_next|
+sets the value of three variables |cur_cmd|, |cur_chr|, and |cur_cs|,
+representing the next input token.
+$$\vbox{\halign{#\hfil\cr
+ \hbox{|cur_cmd| denotes a command code from the long list of codes
+ given above;}\cr
+ \hbox{|cur_chr| denotes a character code or other modifier of the command
+ code;}\cr
+ \hbox{|cur_cs| is the |eqtb| location of the current control sequence,}\cr
+ \hbox{\qquad if the current token was a control sequence,
+ otherwise it's zero.}\cr}}$$
+Underlying this external behavior of |get_next| is all the machinery
+necessary to convert from character files to tokens. At a given time we
+may be only partially finished with the reading of several files (for
+which \.{\\input} was specified), and partially finished with the expansion
+of some user-defined macros and/or some macro parameters, and partially
+finished with the generation of some text in a template for \.{\\halign},
+and so on. When reading a character file, special characters must be
+classified as math delimiters, etc.; comments and extra blank spaces must
+be removed, paragraphs must be recognized, and control sequences must be
+found in the hash table. Furthermore there are occasions in which the
+scanning routines have looked ahead for a word like `\.{plus}' but only
+part of that word was found, hence a few characters must be put back
+into the input and scanned again.
+
+To handle these situations, which might all be present simultaneously,
+\TeX\ uses various stacks that hold information about the incomplete
+activities, and there is a finite state control for each level of the
+input mechanism. These stacks record the current state of an implicitly
+recursive process, but the |get_next| procedure is not recursive.
+Therefore it will not be difficult to translate these algorithms into
+low-level languages that do not support recursion.
+
+@<Glob...@>=
+static eight_bits @!cur_cmd; /*current command set by |get_next|*/
+static halfword @!cur_chr; /*operand of current command*/
+static pointer @!cur_cs; /*control sequence found here, zero if none found*/
+static halfword @!cur_tok; /*packed representative of |cur_cmd| and |cur_chr|*/
+
+@ The |print_cmd_chr| routine prints a symbolic interpretation of a
+command code and its modifier. This is used in certain `\.{You can\'t}'
+error messages, and in the implementation of diagnostic routines like
+\.{\\show}.
+
+The body of |print_cmd_chr| is a rather tedious listing of print
+commands, and most of it is essentially an inverse to the |primitive|
+routine that enters a \TeX\ primitive into |eqtb|. Therefore much of
+this procedure appears elsewhere in the program,
+together with the corresponding |primitive| calls.
+
+@d chr_cmd(A) {@+print(A);print_ASCII(chr_code);
+ }
+
+@<Declare the procedure called |print_cmd_chr|@>=
+static void print_cmd_chr(quarterword @!cmd, halfword @!chr_code)
+{@+int n; /*temp variable*/
+switch (cmd) {
+case left_brace: chr_cmd("begin-group character ")@;@+break;
+case right_brace: chr_cmd("end-group character ")@;@+break;
+case math_shift: chr_cmd("math shift character ")@;@+break;
+case mac_param: chr_cmd("macro parameter character ")@;@+break;
+case sup_mark: chr_cmd("superscript character ")@;@+break;
+case sub_mark: chr_cmd("subscript character ")@;@+break;
+case endv: print("end of alignment template");@+break;
+case spacer: chr_cmd("blank space ")@;@+break;
+case letter: chr_cmd("the letter ")@;@+break;
+case other_char: chr_cmd("the character ")@;@+break;
+@t\4@>@<Cases of |print_cmd_chr| for symbolic printing of primitives@>@/
+default:print("[unknown command code!]");
+}
+}
+
+@ Here is a procedure that displays the current command.
+
+@p static void show_cur_cmd_chr(void)
+{@+int n; /*level of \.{\\if...\\fi} nesting*/
+int @!l; /*line where \.{\\if} started*/
+pointer @!p;
+begin_diagnostic();print_nl("{");
+if (mode!=shown_mode)
+ {@+print_mode(mode);print(": ");shown_mode=mode;
+ }
+print_cmd_chr(cur_cmd, cur_chr);
+if (tracing_ifs > 0)
+ if (cur_cmd >= if_test) if (cur_cmd <= fi_or_else)
+ {@+print(": ");
+ if (cur_cmd==fi_or_else)
+ {@+print_cmd_chr(if_test, cur_if);print_char(' ');
+ n=0;l=if_line;
+ }
+ else{@+n=1;l=line;
+ }
+ p=cond_ptr;
+ while (p!=null)
+ {@+incr(n);p=link(p);
+ }
+ print("(level ");print_int(n);print_char(')');print_if_line(l);
+ }
+print_char('}');
+end_diagnostic(false);
+}
+
+@* Input stacks and states.
+This implementation of
+\TeX\ uses two different conventions for representing sequential stacks.
+@^stack conventions@>@^conventions for representing stacks@>
+
+\yskip\hangg 1) If there is frequent access to the top entry, and if the
+stack is essentially never empty, then the top entry is kept in a global
+variable (even better would be a machine register), and the other entries
+appear in the array $\\{stack}[0\to(\\{ptr}-1)]$. For example, the
+semantic stack described above is handled this way, and so is the input
+stack that we are about to study.
+
+\yskip\hangg 2) If there is infrequent top access, the entire stack contents
+are in the array $\\{stack}[0\to(\\{ptr}-1)]$. For example, the |save_stack|
+is treated this way, as we have seen.
+
+\yskip\noindent
+The state of \TeX's input mechanism appears in the input stack, whose
+entries are records with six fields, called |state|, |index|, |start|, |loc|,
+|limit|, and |name|. This stack is maintained with
+convention~(1), so it is declared in the following way:
+
+@<Types...@>=
+typedef struct {
+ quarterword @!state_field, @!index_field;
+ halfword @!start_field, @!loc_field, @!limit_field, @!name_field;
+ halfword @!depth_field;
+ } in_state_record;
+
+@ @<Glob...@>=
+static in_state_record @!input_stack[stack_size+1];
+static int @!input_ptr; /*first unused location of |input_stack|*/
+static int @!max_in_stack; /*largest value of |input_ptr| when pushing*/
+static in_state_record @!cur_input;
+ /*the ``top'' input state, according to convention (1)*/
+
+@ We've already defined the special variable |loc==cur_input.loc_field|
+in our discussion of basic input-output routines. The other components of
+|cur_input| are defined in the same way:
+
+@d state cur_input.state_field /*current scanner state*/
+@d index cur_input.index_field /*reference for buffer information*/
+@d start cur_input.start_field /*starting position in |buffer|*/
+@d limit cur_input.limit_field /*end of current line in |buffer|*/
+@d name cur_input.name_field /*name of the current file*/
+@d cur_depth cur_input.depth_field /* nesting depth of current macro */
+
+@ Let's look more closely now at the control variables
+(|state|,~|index|,~|start|,~|loc|,~|limit|,~|name|),
+assuming that \TeX\ is reading a line of characters that have been input
+from some file or from the user's terminal. There is an array called
+|buffer| that acts as a stack of all lines of characters that are
+currently being read from files, including all lines on subsidiary
+levels of the input stack that are not yet completed. \TeX\ will return to
+the other lines when it is finished with the present input file.
+
+(Incidentally, on a machine with byte-oriented addressing, it might be
+appropriate to combine |buffer| with the |str_pool| array,
+letting the buffer entries grow downward from the top of the string pool
+and checking that these two tables don't bump into each other.)
+
+The line we are currently working on begins in position |start| of the
+buffer; the next character we are about to read is |buffer[loc]|; and
+|limit| is the location of the last character present. If |loc > limit|,
+the line has been completely read. Usually |buffer[limit]| is the
+|end_line_char|, denoting the end of a line, but this is not
+true if the current line is an insertion that was entered on the user's
+terminal in response to an error message.
+
+The |name| variable is a string number that designates the name of
+the current file, if we are reading a text file. It is zero if we
+are reading from the terminal; it is |n+1| if we are reading from
+input stream |n|, where |0 <= n <= 16|. (Input stream 16 stands for
+an invalid stream number; in such cases the input is actually from
+the terminal, under control of the procedure |read_toks|.)
+Finally |18 <= name <= 19| indicates that we are reading a pseudo file
+created by the \.{\\scantokens} command.
+
+The |state| variable has one of three values, when we are scanning such
+files:
+$$\baselineskip 15pt\vbox{\halign{#\hfil\cr
+1) |state==mid_line| is the normal state.\cr
+2) |state==skip_blanks| is like |mid_line|, but blanks are ignored.\cr
+3) |state==new_line| is the state at the beginning of a line.\cr}}$$
+These state values are assigned numeric codes so that if we add the state
+code to the next character's command code, we get distinct values. For
+example, `|mid_line+spacer|' stands for the case that a blank
+space character occurs in the middle of a line when it is not being
+ignored; after this case is processed, the next value of |state| will
+be |skip_blanks|.
+
+@d mid_line 1 /*|state| code when scanning a line of characters*/
+@d skip_blanks (2+max_char_code) /*|state| code when ignoring blanks*/
+@d new_line (3+max_char_code+max_char_code) /*|state| code at start of line*/
+
+@ Additional information about the current line is available via the
+|index| variable, which counts how many lines of characters are present
+in the buffer below the current level. We have |index==0| when reading
+from the terminal and prompting the user for each line; then if the user types,
+e.g., `\.{\\input paper}', we will have |index==1| while reading
+the file \.{paper.tex}. However, it does not follow that |index| is the
+same as the input stack pointer, since many of the levels on the input
+stack may come from token lists. For example, the instruction `\.{\\input
+paper}' might occur in a token list.
+
+The global variable |in_open| is equal to the |index|
+value of the highest non-token-list level. Thus, the number of partially read
+lines in the buffer is |in_open+1|, and we have |in_open==index|
+when we are not reading a token list.
+
+If we are not currently reading from the terminal, or from an input
+stream, we are reading from the file variable |input_file[index]|. We use
+the notation |terminal_input| as a convenient abbreviation for |name==0|,
+and |cur_file| as an abbreviation for |input_file[index]|.
+
+The global variable |line| contains the line number in the topmost
+open file, for use in error messages. If we are not reading from
+the terminal, |line_stack[index]| holds the line number for the
+enclosing level, so that |line| can be restored when the current
+file has been read. Line numbers should never be negative, since the
+negative of the current line number is used to identify the user's output
+routine in the |mode_line| field of the semantic nest entries.
+
+If more information about the input state is needed, it can be
+included in small arrays like those shown here. For example,
+the current page or segment number in the input file might be
+put into a variable |@!page|, maintained for enclosing levels in
+`\ignorespaces|@!page_stack: array[1 dotdot max_in_open]int|\unskip'
+by analogy with |line_stack|.
+@^system dependencies@>
+
+@d terminal_input (name==0) /*are we reading from the terminal?*/
+@d cur_file input_file[index] /*the current |alpha_file| variable*/
+
+@<Glob...@>=
+static int @!in_open; /*the number of lines in the buffer, less one*/
+static int @!open_parens; /*the number of open text files*/
+static alpha_file @!input_file0[max_in_open],
+ *const @!input_file = @!input_file0-1;
+static int @!line; /*current line number in the current source file*/
+static int @!line_stack0[max_in_open],
+ *const @!line_stack = @!line_stack0-1;
+
+@ Users of \TeX\ sometimes forget to balance left and right braces properly,
+and one of the ways \TeX\ tries to spot such errors is by considering an
+input file as broken into subfiles by control sequences that
+are declared to be \.{\\outer}.
+
+A variable called |scanner_status| tells \TeX\ whether or not to complain
+when a subfile ends. This variable has six possible values:
+
+\yskip\hang|normal|, means that a subfile can safely end here without incident.
+
+\yskip\hang|skipping|, means that a subfile can safely end here, but not a file,
+because we're reading past some conditional text that was not selected.
+
+\yskip\hang|defining|, means that a subfile shouldn't end now because a
+macro is being defined.
+
+\yskip\hang|matching|, means that a subfile shouldn't end now because a
+macro is being used and we are searching for the end of its arguments.
+
+\yskip\hang|aligning|, means that a subfile shouldn't end now because we are
+not finished with the preamble of an \.{\\halign} or \.{\\valign}.
+
+\yskip\hang|absorbing|, means that a subfile shouldn't end now because we are
+reading a balanced token list for \.{\\message}, \.{\\write}, etc.
+
+\yskip\noindent
+If the |scanner_status| is not |normal|, the variable |warning_index| points
+to the |eqtb| location for the relevant control sequence name to print
+in an error message.
+
+@d skipping 1 /*|scanner_status| when passing conditional text*/
+@d defining 2 /*|scanner_status| when reading a macro definition*/
+@d matching 3 /*|scanner_status| when reading macro arguments*/
+@d aligning 4 /*|scanner_status| when reading an alignment preamble*/
+@d absorbing 5 /*|scanner_status| when reading a balanced text*/
+
+@<Glob...@>=
+static int @!scanner_status; /*can a subfile end now?*/
+static pointer @!warning_index; /*identifier relevant to non-|normal| scanner status*/
+static pointer @!def_ref; /*reference count of token list being defined*/
+
+@ Here is a procedure that uses |scanner_status| to print a warning message
+when a subfile has ended, and at certain other crucial times:
+
+@<Declare the procedure called |runaway|@>=
+static void runaway(void)
+{@+pointer p; /*head of runaway list*/
+if (scanner_status > skipping)
+ {@+print_nl("Runaway ");
+@.Runaway...@>
+ switch (scanner_status) {
+ case defining: {@+print("definition");p=def_ref;
+ } @+break;
+ case matching: {@+print("argument");p=temp_head;
+ } @+break;
+ case aligning: {@+print("preamble");p=hold_head;
+ } @+break;
+ case absorbing: {@+print("text");p=def_ref;
+ }
+ } /*there are no other cases*/
+ print_char('?');print_ln();show_token_list(link(p), null, error_line-10);
+ }
+}
+
+@ However, all this discussion about input state really applies only to the
+case that we are inputting from a file. There is another important case,
+namely when we are currently getting input from a token list. In this case
+|state==token_list|, and the conventions about the other state variables
+are different:
+
+\yskip\hang|loc| is a pointer to the current node in the token list, i.e.,
+the node that will be read next. If |loc==null|, the token list has been
+fully read.
+
+\yskip\hang|start| points to the first node of the token list; this node
+may or may not contain a reference count, depending on the type of token
+list involved.
+
+\yskip\hang|token_type|, which takes the place of |index| in the
+discussion above, is a code number that explains what kind of token list
+is being scanned.
+
+\yskip\hang|name| points to the |eqtb| address of the control sequence
+being expanded, if the current token list is a macro.
+
+\yskip\hang|param_start|, which takes the place of |limit|, tells where
+the parameters of the current macro begin in the |param_stack|, if the
+current token list is a macro.
+
+\yskip\noindent The |token_type| can take several values, depending on
+where the current token list came from:
+
+\yskip\hang|parameter|, if a parameter is being scanned;
+
+\hang|u_template|, if the \<u_j> part of an alignment
+template is being scanned;
+
+\hang|v_template|, if the \<v_j> part of an alignment
+template is being scanned;
+
+\hang|backed_up|, if the token list being scanned has been inserted as
+`to be read again';
+
+\hang|inserted|, if the token list being scanned has been inserted as
+the text expansion of a \.{\\count} or similar variable;
+
+\hang|macro|, if a user-defined control sequence is being scanned;
+
+\hang|output_text|, if an \.{\\output} routine is being scanned;
+
+\hang|every_par_text|, if the text of \.{\\everypar} is being scanned;
+
+\hang|every_math_text|, if the text of \.{\\everymath} is being scanned;
+
+\hang|every_display_text|, if the text of \.{\\everydisplay} is being scanned;
+
+\hang|every_hbox_text|, if the text of \.{\\everyhbox} is being scanned;
+
+\hang|every_vbox_text|, if the text of \.{\\everyvbox} is being scanned;
+
+\hang|every_job_text|, if the text of \.{\\everyjob} is being scanned;
+
+\hang|every_cr_text|, if the text of \.{\\everycr} is being scanned;
+
+\hang|mark_text|, if the text of a \.{\\mark} is being scanned;
+
+\hang|write_text|, if the text of a \.{\\write} is being scanned.
+
+\yskip\noindent
+The codes for |output_text|, |every_par_text|, etc., are equal to a constant
+plus the corresponding codes for token list parameters |output_routine_loc|,
+|every_par_loc|, etc. The token list begins with a reference count if and
+only if |token_type >= macro|.
+@^reference counts@>
+
+Since \eTeX's additional token list parameters precede |toks_base|, the
+corresponding token types must precede |write_text|.
+
+@d token_list 0 /*|state| code when scanning a token list*/
+@d token_type index /*type of current token list*/
+@d param_start limit /*base of macro parameters in |param_stack|*/
+@d parameter 0 /*|token_type| code for parameter*/
+@d u_template 1 /*|token_type| code for \<u_j> template*/
+@d v_template 2 /*|token_type| code for \<v_j> template*/
+@d backed_up 3 /*|token_type| code for text to be reread*/
+@d inserted 4 /*|token_type| code for inserted texts*/
+@d macro 5 /*|token_type| code for defined control sequences*/
+@d output_text 6 /*|token_type| code for output routines*/
+@d every_par_text 7 /*|token_type| code for \.{\\everypar}*/
+@d every_math_text 8 /*|token_type| code for \.{\\everymath}*/
+@d every_display_text 9 /*|token_type| code for \.{\\everydisplay}*/
+@d every_hbox_text 10 /*|token_type| code for \.{\\everyhbox}*/
+@d every_vbox_text 11 /*|token_type| code for \.{\\everyvbox}*/
+@d every_job_text 12 /*|token_type| code for \.{\\everyjob}*/
+@d every_cr_text 13 /*|token_type| code for \.{\\everycr}*/
+@d mark_text 14 /*|token_type| code for \.{\\topmark}, etc.*/
+@#
+@d eTeX_text_offset (output_routine_loc-output_text)
+@d every_eof_text (every_eof_loc-eTeX_text_offset)
+ /*|token_type| code for \.{\\everyeof}*/
+@#
+@d write_text (toks_base-eTeX_text_offset) /*|token_type| code for \.{\\write}*/
+
+@ The |param_stack| is an auxiliary array used to hold pointers to the token
+lists for parameters at the current level and subsidiary levels of input.
+This stack is maintained with convention (2), and it grows at a different
+rate from the others.
+
+@<Glob...@>=
+static pointer @!param_stack[param_size+1];
+ /*token list pointers for parameters*/
+static int @!param_ptr; /*first unused entry in |param_stack|*/
+static int @!max_param_stack;
+ /*largest value of |param_ptr|, will be | <= param_size+9|*/
+
+@ The input routines must also interact with the processing of
+\.{\\halign} and \.{\\valign}, since the appearance of tab marks and
+\.{\\cr} in certain places is supposed to trigger the beginning of special
+\<v_j> template text in the scanner. This magic is accomplished by an
+|align_state| variable that is increased by~1 when a `\.{\char'173}' is
+scanned and decreased by~1 when a `\.{\char'175}' is scanned. The |align_state|
+is nonzero during the \<u_j> template, after which it is set to zero; the
+\<v_j> template begins when a tab mark or \.{\\cr} occurs at a time that
+|align_state==0|.
+
+The same principle applies when entering the definition of a
+control sequence between \.{\\csname} and \.{\\endcsname}.
+
+@<Glob...@>=
+static int @!align_state; /*group level with respect to current alignment*/
+static int @!incsname_state; /*group level with respect to in csname state*/
+
+@ Thus, the ``current input state'' can be very complicated indeed; there
+can be many levels and each level can arise in a variety of ways. The
+|show_context| procedure, which is used by \TeX's error-reporting routine to
+print out the current input state on all levels down to the most recent
+line of characters from an input file, illustrates most of these conventions.
+The global variable |base_ptr| contains the lowest level that was
+displayed by this procedure.
+
+@<Glob...@>=
+static int @!base_ptr; /*shallowest level shown by |show_context|*/
+
+@ The status at each level is indicated by printing two lines, where the first
+line indicates what was read so far and the second line shows what remains
+to be read. The context is cropped, if necessary, so that the first line
+contains at most |half_error_line| characters, and the second contains
+at most |error_line|. Non-current input levels whose |token_type| is
+`|backed_up|' are shown only if they have not been fully read.
+
+@p static void show_context(void) /*prints where the scanner is*/
+{@+
+int old_setting; /*saved |selector| setting*/
+int @!nn; /*number of contexts shown so far, less one*/
+bool @!bottom_line; /*have we reached the final context to be shown?*/
+@<Local variables for formatting calculations@>@/
+base_ptr=input_ptr;input_stack[base_ptr]=cur_input;
+ /*store current state*/
+nn=-1;bottom_line=false;
+loop@+{@+cur_input=input_stack[base_ptr]; /*enter into the context*/
+ if ((state!=token_list))
+ if ((name > 19)||(base_ptr==0)) bottom_line=true;
+ if ((base_ptr==input_ptr)||bottom_line||(nn < error_context_lines))
+ @<Display the current context@>@;
+ else if (nn==error_context_lines)
+ {@+print_nl("...");incr(nn); /*omitted if |error_context_lines < 0|*/
+ }
+ if (bottom_line) goto done;
+ decr(base_ptr);
+ }
+done: cur_input=input_stack[input_ptr]; /*restore original state*/
+}
+
+@ @<Display the current context@>=
+{@+if ((base_ptr==input_ptr)||(state!=token_list)||
+ (token_type!=backed_up)||(loc!=null))
+ /*we omit backed-up token lists that have already been read*/
+ {@+tally=0; /*get ready to count characters*/
+ old_setting=selector;
+ if (state!=token_list)
+ {@+@<Print location of current line@>;
+ @<Pseudoprint the line@>;
+ }
+ else{@+@<Print type of token list@>;
+ @<Pseudoprint the token list@>;
+ }
+ selector=old_setting; /*stop pseudoprinting*/
+ @<Print two lines using the tricky pseudoprinted information@>;
+ incr(nn);
+ }
+}
+
+@ This routine should be changed, if necessary, to give the best possible
+indication of where the current line resides in the input file.
+For example, on some systems it is best to print both a page and line number.
+@^system dependencies@>
+
+@<Print location of current line@>=
+if (name <= 17)
+ if (terminal_input)
+ if (base_ptr==0) print_nl("<*>");else print_nl("<insert> ");
+ else{@+print_nl("<read ");
+ if (name==17) print_char('*');@+else print_int(name-1);
+@.*\relax@>
+ print_char('>');
+ }
+else{@+print_nl("l.");
+ if (index==in_open) print_int(line);
+ else print_int(line_stack[index+1]); /*input from a pseudo file*/
+ }
+print_char(' ')
+
+@ @<Print type of token list@>=
+switch (token_type) {
+case parameter: print_nl("<argument> ");@+break;
+case u_template: case v_template: print_nl("<template> ");@+break;
+case backed_up: if (loc==null) print_nl("<recently read> ");
+ else print_nl("<to be read again> ");@+break;
+case inserted: print_nl("<inserted text> ");@+break;
+case macro: {@+print_ln();print_cs(name);
+ } @+break;
+case output_text: print_nl("<output> ");@+break;
+case every_par_text: print_nl("<everypar> ");@+break;
+case every_math_text: print_nl("<everymath> ");@+break;
+case every_display_text: print_nl("<everydisplay> ");@+break;
+case every_hbox_text: print_nl("<everyhbox> ");@+break;
+case every_vbox_text: print_nl("<everyvbox> ");@+break;
+case every_job_text: print_nl("<everyjob> ");@+break;
+case every_cr_text: print_nl("<everycr> ");@+break;
+case mark_text: print_nl("<mark> ");@+break;
+case every_eof_text: print_nl("<everyeof> ");@+break;
+case write_text: print_nl("<write> ");@+break;
+default:print_nl("?"); /*this should never happen*/
+}
+
+@ Here it is necessary to explain a little trick. We don't want to store a long
+string that corresponds to a token list, because that string might take up
+lots of memory; and we are printing during a time when an error message is
+being given, so we dare not do anything that might overflow one of \TeX's
+tables. So `pseudoprinting' is the answer: We enter a mode of printing
+that stores characters into a buffer of length |error_line|, where character
+$k+1$ is placed into \hbox{|trick_buf[k%error_line]|} if
+|k < trick_count|, otherwise character |k| is dropped. Initially we set
+|tally=0| and |trick_count=1000000|; then when we reach the
+point where transition from line 1 to line 2 should occur, we
+set |first_count=tally| and |trick_count=@tmax@>(error_line,
+tally+1+error_line-half_error_line)|. At the end of the
+pseudoprinting, the values of |first_count|, |tally|, and
+|trick_count| give us all the information we need to print the two lines,
+and all of the necessary text is in |trick_buf|.
+
+Namely, let |l| be the length of the descriptive information that appears
+on the first line. The length of the context information gathered for that
+line is |k==first_count|, and the length of the context information
+gathered for line~2 is $m=\min(|tally|, |trick_count|)-k$. If |l+k <= h|,
+where |h==half_error_line|, we print |trick_buf[0 dotdot k-1]| after the
+descriptive information on line~1, and set |n=l+k|; here |n| is the
+length of line~1. If $l+k>h$, some cropping is necessary, so we set |n=h|
+and print `\.{...}' followed by
+$$\hbox{|trick_buf[(l+k-h+3)dotdot k-1]|,}$$
+where subscripts of |trick_buf| are circular modulo |error_line|. The
+second line consists of |n|~spaces followed by |trick_buf[k dotdot(k+m-1)]|,
+unless |n+m > error_line|; in the latter case, further cropping is done.
+This is easier to program than to explain.
+
+@<Local variables for formatting...@>=
+int @!i; /*index into |buffer|*/
+int @!j; /*end of current line in |buffer|*/
+int @!l; /*length of descriptive information on line 1*/
+int @!m; /*context information gathered for line 2*/
+int @!n; /*length of line 1*/
+int @!p; /*starting or ending place in |trick_buf|*/
+int @!q; /*temporary index*/
+
+@ The following code sets up the print routines so that they will gather
+the desired information.
+
+@d begin_pseudoprint
+ {@+l=tally;tally=0;selector=pseudo;
+ trick_count=1000000;
+ }
+@d set_trick_count
+ {@+first_count=tally;
+ trick_count=tally+1+error_line-half_error_line;
+ if (trick_count < error_line) trick_count=error_line;
+ }
+
+@ And the following code uses the information after it has been gathered.
+
+@<Print two lines using the tricky pseudoprinted information@>=
+if (trick_count==1000000) set_trick_count;
+ /*|set_trick_count| must be performed*/
+if (tally < trick_count) m=tally-first_count;
+else m=trick_count-first_count; /*context on line 2*/
+if (l+first_count <= half_error_line)
+ {@+p=0;n=l+first_count;
+ }
+else{@+print("...");p=l+first_count-half_error_line+3;
+ n=half_error_line;
+ }
+for (q=p; q<=first_count-1; q++) print_char(trick_buf[q%error_line]);
+print_ln();
+for (q=1; q<=n; q++) print_char(' '); /*print |n| spaces to begin line~2*/
+if (m+n <= error_line) p=first_count+m;else p=first_count+(error_line-n-3);
+for (q=first_count; q<=p-1; q++) print_char(trick_buf[q%error_line]);
+if (m+n > error_line) print("...")
+
+@ But the trick is distracting us from our current goal, which is to
+understand the input state. So let's concentrate on the data structures that
+are being pseudoprinted as we finish up the |show_context| procedure.
+
+@<Pseudoprint the line@>=
+begin_pseudoprint;
+if (buffer[limit]==end_line_char) j=limit;
+else j=limit+1; /*determine the effective end of the line*/
+if (j > 0) for (i=start; i<=j-1; i++)
+ {@+if (i==loc) set_trick_count;
+ printn(buffer[i]);
+ }
+
+@ @<Pseudoprint the token list@>=
+begin_pseudoprint;
+if (token_type < macro) show_token_list(start, loc, 100000);
+else show_token_list(link(start), loc, 100000) /*avoid reference count*/
+
+@ Here is the missing piece of |show_token_list| that is activated when the
+token beginning line~2 is about to be shown:
+
+@<Do magic computation@>=set_trick_count
+
+@* Maintaining the input stacks.
+The following subroutines change the input status in commonly needed ways.
+
+First comes |push_input|, which stores the current state and creates a
+new level (having, initially, the same properties as the old).
+
+@d push_input @t@> /*enter a new input level, save the old*/
+ {@+if (input_ptr > max_in_stack)
+ {@+max_in_stack=input_ptr;
+ if (input_ptr==stack_size) overflow("input stack size", stack_size);
+@:TeX capacity exceeded input stack size}{\quad input stack size@>
+ }
+ input_stack[input_ptr]=cur_input; /*stack the record*/
+ incr(input_ptr);
+ }
+
+@ And of course what goes up must come down.
+
+@d pop_input @t@> /*leave an input level, re-enter the old*/
+ {@+decr(input_ptr);cur_input=input_stack[input_ptr];
+ }
+
+@ Here is a procedure that starts a new level of token-list input, given
+a token list |p| and its type |t|. If |t==macro|, the calling routine should
+set |name| and |loc|.
+
+@d back_list(A) begin_token_list(A, backed_up) /*backs up a simple token list*/
+@d ins_list(A) begin_token_list(A, inserted) /*inserts a simple token list*/
+
+@p static void begin_token_list(pointer @!p, quarterword @!t)
+{@+push_input;state=token_list;start=p;token_type=t;
+if (t >= macro) /*the token list starts with a reference count*/
+ {@+add_token_ref(p);
+ if (t==macro) param_start=param_ptr;
+ else{@+
+ @<additional local variables for |begin_token_list|@>
+ loc=link(p);
+ if (tracing_macros > 1)
+ {@+begin_diagnostic();print_nl("");
+ switch (t) {
+ case mark_text: print_esc("mark");@+break;
+ case write_text: print_esc("write");@+break;
+ default:print_cmd_chr(assign_toks, t-output_text+output_routine_loc);
+ } @/
+ print("->");token_show(p);end_diagnostic(false);
+ }
+ @<update the macro stack@>@;
+ }
+ }
+else loc=p;
+}
+
+@ When a token list has been fully scanned, the following computations
+should be done as we leave that level of input. The |token_type| tends
+to be equal to either |backed_up| or |inserted| about 2/3 of the time.
+@^inner loop@>
+
+@p static void end_token_list(void) /*leave a token-list input level*/
+{@+if (token_type >= backed_up) /*token list to be deleted*/
+ {@+if (token_type <= inserted) flush_list(start);
+ else{@+delete_token_ref(start); /*update reference count*/
+ if (token_type==macro) /*parameters must be flushed*/
+ while (param_ptr > param_start)
+ {@+decr(param_ptr);
+ flush_list(param_stack[param_ptr]);
+ }
+ }
+ }
+else if (token_type==u_template)
+ if (align_state > 500000) align_state=0;
+ else fatal_error("(interwoven alignment preambles are not allowed)");
+@.interwoven alignment preambles...@>
+pop_input;
+check_interrupt;
+}
+
+@ Sometimes \TeX\ has read too far and wants to ``unscan'' what it has
+seen. The |back_input| procedure takes care of this by putting the token
+just scanned back into the input stream, ready to be read again. This
+procedure can be used only if |cur_tok| represents the token to be
+replaced. Some applications of \TeX\ use this procedure a lot,
+so it has been slightly optimized for speed.
+@^inner loop@>
+
+We charge the backup token to the current file and line.
+@^profiling@>
+
+@p static void back_input(void) /*undoes one token of input*/
+{@+pointer p; /*a token list of length one*/
+while ((state==token_list)&&(loc==null)&&(token_type!=v_template))
+ end_token_list(); /*conserve stack space*/
+p=get_avail();info(p)=cur_tok; fl_mem[p]=prof_file_line;
+if (cur_tok < right_brace_limit)
+ if (cur_tok < left_brace_limit) decr(align_state);
+ else incr(align_state);
+push_input;state=token_list;start=p;token_type=backed_up;
+loc=p; /*that was |back_list(p)|, without procedure overhead*/
+}
+
+@ @<Insert token |p| into \TeX's input@>=
+{@+t=cur_tok;cur_tok=p;
+if (a)
+ {@+p=get_avail();info(p)=cur_tok;link(p)=loc;loc=p;start=p;
+ if (cur_tok < right_brace_limit)
+ if (cur_tok < left_brace_limit) decr(align_state);
+ else incr(align_state);
+ }
+else{@+back_input();a=eTeX_ex;
+ }
+cur_tok=t;
+}
+
+@ The |back_error| routine is used when we want to replace an offending token
+just before issuing an error message. This routine, like |back_input|,
+requires that |cur_tok| has been set. We disable interrupts during the
+call of |back_input| so that the help message won't be lost.
+
+@p static void back_error(void) /*back up one token and call |error|*/
+{@+OK_to_interrupt=false;back_input();OK_to_interrupt=true;error();
+}
+@#
+static void ins_error(void) /*back up one inserted token and call |error|*/
+{@+OK_to_interrupt=false;back_input();token_type=inserted;
+OK_to_interrupt=true;error();
+}
+
+@ The |begin_file_reading| procedure starts a new level of input for lines
+of characters to be read from a file, or as an insertion from the
+terminal. It does not take care of opening the file, nor does it set |loc|
+or |limit| or |line|.
+@^system dependencies@>
+
+@p static void begin_file_reading(void)
+{@+if (in_open==max_in_open) overflow("text input levels", max_in_open);
+@:TeX capacity exceeded text input levels}{\quad text input levels@>
+if (first==buf_size) overflow("buffer size", buf_size);
+@:TeX capacity exceeded buffer size}{\quad buffer size@>
+incr(in_open);push_input;index=in_open;@/
+source_filename_stack[index]=NULL; /* \TeX\ Live */
+full_source_filename_stack[index]=NULL; /* \TeX\ Live */
+eof_seen[index]=false;
+grp_stack[index]=cur_boundary;if_stack[index]=cond_ptr;
+line_stack[index]=line;start=first;state=mid_line;
+name=0; /*|terminal_input| is now |true|*/
+cur_file_num=terminal_file;
+}
+
+@ Conversely, the variables must be downdated when such a level of input
+is finished:
+
+@p static void end_file_reading(void)
+{@+first=start;line=line_stack[index];
+if ((name==18)||(name==19)) pseudo_close();else
+if (name > 17) a_close(&cur_file); /*forget it*/
+if (full_source_filename_stack[in_open]!=NULL)
+{ free(full_source_filename_stack[in_open]);
+ full_source_filename_stack[in_open]=NULL;
+}
+pop_input;decr(in_open);
+}
+
+@ In order to keep the stack from overflowing during a long sequence of
+inserted `\.{\\show}' commands, the following routine removes completed
+error-inserted lines from memory.
+
+@p static void clear_for_error_prompt(void)
+{@+while ((state!=token_list)&&terminal_input&&@|
+ (input_ptr > 0)&&(loc > limit)) end_file_reading();
+print_ln();clear_terminal;
+}
+
+@ To get \TeX's whole input mechanism going, we perform the following
+actions.
+
+@<Initialize the input routines@>=
+{@+input_ptr=0;max_in_stack=0;
+in_open=0;open_parens=0;max_buf_stack=0;
+grp_stack[0]=0;if_stack[0]=null;
+param_ptr=0;max_param_stack=0;
+first=buf_size;@/do@+{buffer[first]=0;decr(first);}@+ while (!(first==0));
+scanner_status=normal;warning_index=null;first=1;
+state=new_line;start=1;index=0;line=0;name=0;cur_depth=0;
+force_eof=false;
+align_state=1000000;@/
+if (!init_terminal()) exit(0);
+limit=last;first=last+1; /*|init_terminal| has set |loc| and |last|*/
+}
+
+@* Getting the next token.
+The heart of \TeX's input mechanism is the |get_next| procedure, which
+we shall develop in the next few sections of the program. Perhaps we
+shouldn't actually call it the ``heart,'' however, because it really acts
+as \TeX's eyes and mouth, reading the source files and gobbling them up.
+And it also helps \TeX\ to regurgitate stored token lists that are to be
+processed again.
+@^eyes and mouth@>
+
+The main duty of |get_next| is to input one token and to set |cur_cmd|
+and |cur_chr| to that token's command code and modifier. Furthermore, if
+the input token is a control sequence, the |eqtb| location of that control
+sequence is stored in |cur_cs|; otherwise |cur_cs| is set to zero.
+
+Underlying this simple description is a certain amount of complexity
+because of all the cases that need to be handled.
+However, the inner loop of |get_next| is reasonably short and fast.
+
+When |get_next| is asked to get the next token of a \.{\\read} line,
+it sets |cur_cmd==cur_chr==cur_cs==0| in the case that no more tokens
+appear on that line. (There might not be any tokens at all, if the
+|end_line_char| has |ignore| as its catcode.)
+
+@ The value of |par_loc| is the |eqtb| address of `\.{\\par}'. This quantity
+is needed because a blank line of input is supposed to be exactly equivalent
+to the appearance of \.{\\par}; we must set |cur_cs:=par_loc|
+when detecting a blank line.
+
+The same is true for the input, for the warning message, since input
+is expected by default before every scanning and hence setting of
+|cur_cs|.
+
+@<Glob...@>=
+static pointer @!par_loc; /*location of `\.{\\par}' in |eqtb|*/
+static halfword @!par_token; /*token representing `\.{\\par}'*/
+static pointer @!input_loc; /*location of `\.{\\input}' in |eqtb|*/
+static halfword @!input_token; /*token representing `\.{\\input}'*/
+
+@ @<Put each...@>=
+primitive("par", par_end, 256); /*cf.\ |scan_file_name|*/
+@!@:par\_}{\.{\\par} primitive@>
+par_loc=cur_val;par_token=cs_token_flag+par_loc;
+
+@ @<Cases of |print_cmd_chr|...@>=
+case par_end: print_esc("par");@+break;
+
+@ Before getting into |get_next|, let's consider the subroutine that
+is called when an `\.{\\outer}' control sequence has been scanned or
+when the end of a file has been reached. These two cases are distinguished
+by |cur_cs|, which is zero at the end of a file.
+
+@p static void check_outer_validity(void)
+{@+pointer p; /*points to inserted token list*/
+pointer @!q; /*auxiliary pointer*/
+if (scanner_status!=normal)
+ {@+deletions_allowed=false;
+ @<Back up an outer control sequence so that it can be reread@>;
+ if (scanner_status > skipping)
+ @<Tell the user what has run away and try to recover@>@;
+ else{@+print_err("Incomplete ");print_cmd_chr(if_test, cur_if);
+@.Incomplete \\if...@>
+ print("; all text was ignored after line ");print_int(skip_line);
+ help3("A forbidden control sequence occurred in skipped text.",@/
+ "This kind of error happens when you say `\\if...' and forget",@/
+ "the matching `\\fi'. I've inserted a `\\fi'; this might work.");
+ if (cur_cs!=0) cur_cs=0;
+ else help_line[2]=@|
+ "The file ended while I was skipping conditional text.";
+ cur_tok=cs_token_flag+frozen_fi;ins_error();
+ }
+ deletions_allowed=true;
+ }
+}
+
+@ An outer control sequence that occurs in a \.{\\read} will not be reread,
+since the error recovery for \.{\\read} is not very powerful.
+
+@<Back up an outer control sequence so that it can be reread@>=
+if (cur_cs!=0)
+ {@+if ((state==token_list)||(name < 1)||(name > 17))
+ {@+p=get_avail();info(p)=cs_token_flag+cur_cs;
+ back_list(p); /*prepare to read the control sequence again*/
+ }
+ cur_cmd=spacer;cur_chr=' '; /*replace it by a space*/
+ }
+
+@ @<Tell the user what has run away...@>=
+{@+runaway(); /*print a definition, argument, or preamble*/
+if (cur_cs==0) print_err("File ended");
+@.File ended while scanning...@>
+else{@+cur_cs=0;print_err("Forbidden control sequence found");
+@.Forbidden control sequence...@>
+ }
+print(" while scanning ");
+@<Print either `\.{definition}' or `\.{use}' or `\.{preamble}' or `\.{text}',
+and insert tokens that should lead to recovery@>;
+print(" of ");sprint_cs(warning_index);
+help4("I suspect you have forgotten a `}', causing me",@/
+"to read past where you wanted me to stop.",@/
+"I'll try to recover; but if the error is serious,",@/
+"you'd better type `E' or `X' now and fix your file.");@/
+error();
+}
+
+@ The recovery procedure can't be fully understood without knowing more
+about the \TeX\ routines that should be aborted, but we can sketch the
+ideas here: For a runaway definition or a runaway balanced text
+we will insert a right brace; for a
+runaway preamble, we will insert a special \.{\\cr} token and a right
+brace; and for a runaway argument, we will set |long_state| to
+|outer_call| and insert \.{\\par}.
+
+@<Print either `\.{definition}' or...@>=
+p=get_avail();
+switch (scanner_status) {
+case defining: {@+print("definition");info(p)=right_brace_token+'}';
+ } @+break;
+case matching: {@+print("use");info(p)=par_token;long_state=outer_call;
+ } @+break;
+case aligning: {@+print("preamble");info(p)=right_brace_token+'}';q=p;
+ p=get_avail();link(p)=q;info(p)=cs_token_flag+frozen_cr;
+ align_state=-1000000;
+ } @+break;
+case absorbing: {@+print("text");info(p)=right_brace_token+'}';
+ }
+} /*there are no other cases*/
+ins_list(p)
+
+@ We need to mention a procedure here that may be called by |get_next|.
+
+@p static void firm_up_the_line(void);
+
+@ Now we're ready to take the plunge into |get_next| itself. Parts of
+this routine are executed more often than any other instructions of \TeX.
+@^mastication@>@^inner loop@>
+
+@p static void get_next(void) /*sets |cur_cmd|, |cur_chr|, |cur_cs| to next token*/
+{@+ /*go here to get the next input token*/
+ /*go here to eat the next character from a file*/
+ /*go here to digest it again*/
+ /*go here to start looking for a control sequence*/
+ /*go here when a control sequence has been found*/
+ /*go here when the next input token has been got*/
+int k; /*an index into |buffer|*/
+halfword @!t; /*a token*/
+int @!cat; /*|cat_code(cur_chr)|, usually*/
+ASCII_code @!c, @!cc; /*constituents of a possible expanded code*/
+int @!d; /*number of excess characters in an expanded code*/
+restart: cur_cs=0;
+if (state!=token_list)
+@<Input from external file, |goto restart| if no input found@>@;
+else@<Input from token list, |goto restart| if end of list or if a parameter
+needs to be expanded@>;
+@<If an alignment entry has just ended, take appropriate action@>;
+}
+
+@ An alignment entry ends when a tab or \.{\\cr} occurs, provided that the
+current level of braces is the same as the level that was present at the
+beginning of that alignment entry; i.e., provided that |align_state| has
+returned to the value it had after the \<u_j> template for that entry.
+@^inner loop@>
+
+@<If an alignment entry has just ended, take appropriate action@>=
+if (cur_cmd <= car_ret) if (cur_cmd >= tab_mark) if (align_state==0)
+ @<Insert the \(v)\<v_j> template and |goto restart|@>@;
+
+@ @<Input from external file, |goto restart| if no input found@>=
+@^inner loop@>
+{@+get_cur_chr: if (loc <= limit) /*current line not yet finished*/
+ {@+cur_chr=buffer[loc];incr(loc);
+ @<Set |cur_file_line| based on the information in |cur_input|@>@;
+ reswitch: cur_cmd=cat_code(cur_chr);
+ @<Change state if necessary, and |goto get_cur_chr| if the current character
+should be ignored, or |goto reswitch| if the current character changes to
+another@>;
+ }
+else{@+state=new_line;@/
+ @<Move to next line of file, or |goto restart| if there is no next line,
+or |return| if a \.{\\read} line has finished@>;
+ check_interrupt;
+ goto get_cur_chr;
+ }
+}
+
+@ The following 48-way switch accomplishes the scanning quickly, assuming
+that a decent \PASCAL\ compiler has translated the code. Note that the numeric
+values for |mid_line|, |skip_blanks|, and |new_line| are spaced
+apart from each other by |max_char_code+1|, so we can add a character's
+command code to the state to get a single number that characterizes both.
+
+@d any_state_plus(A) case mid_line+A:
+ case skip_blanks+A: case new_line+A
+
+@<Change state if necessary...@>=
+switch (state+cur_cmd) {
+@<Cases where character is ignored@>: goto get_cur_chr;
+any_state_plus(escape): @<Scan a control sequence and set |state:=skip_blanks|
+or |mid_line|@>@;@+break;
+any_state_plus(active_char): @<Process an active-character control sequence
+and set |state:=mid_line|@>@;@+break;
+any_state_plus(sup_mark): @<If this |sup_mark| starts an expanded character
+like~\.{\^\^A} or~\.{\^\^df}, then |goto reswitch|, otherwise set |state:=mid_line|@>@;@+break;
+any_state_plus(invalid_char): @<Decry the invalid character and |goto restart|@>@;
+@t\4@>@<Handle situations involving spaces, braces, changes of state@>@;
+default:do_nothing;
+}
+
+@ @<Cases where character is ignored@>=
+any_state_plus(ignore): case skip_blanks+spacer:
+ case new_line+spacer
+
+@ We go to |restart| instead of to |get_cur_chr|, because |state| might equal
+|token_list| after the error has been dealt with
+(cf.\ |clear_for_error_prompt|).
+
+@<Decry the invalid...@>=
+{@+print_err("Text line contains an invalid character");
+@.Text line contains...@>
+help2("A funny symbol that I can't read has just been input.",@/
+"Continue, and I'll forget that it ever happened.");@/
+deletions_allowed=false;error();deletions_allowed=true;
+goto restart;
+}
+
+@ @d add_delims_to(A) A+math_shift: A+tab_mark: A+mac_param:
+ A+sub_mark: A+letter: A+other_char
+
+@<Handle situations involving spaces, braces, changes of state@>=
+case mid_line+spacer: @<Enter |skip_blanks| state, emit a space@>@;@+break;
+case mid_line+car_ret: @<Finish line, emit a space@>@;@+break;
+case skip_blanks+car_ret: any_state_plus(comment):
+ @<Finish line, |goto switch|@>@;
+case new_line+car_ret: @<Finish line, emit a \.{\\par}@>@;@+break;
+case mid_line+left_brace: incr(align_state);@+break;
+case skip_blanks+left_brace: case new_line+left_brace: {@+
+ state=mid_line;incr(align_state);
+ } @+break;
+case mid_line+right_brace: decr(align_state);@+break;
+case skip_blanks+right_brace: case new_line+right_brace: {@+
+ state=mid_line;decr(align_state);
+ } @+break;
+add_delims_to(case skip_blanks): add_delims_to(
+ case new_line): state=mid_line;@+break;
+
+@ When a character of type |spacer| gets through, its character code is
+changed to $\.{"\ "}=040$. This means that the ASCII codes for tab and space,
+and for the space inserted at the end of a line, will
+be treated alike when macro parameters are being matched. We do this
+since such characters are indistinguishable on most computer terminal displays.
+
+@<Finish line, emit a space@>=
+{@+loc=limit+1;cur_cmd=spacer;cur_chr=' ';
+}
+
+@ The following code is performed only when |cur_cmd==spacer|.
+
+@<Enter |skip_blanks| state, emit a space@>=
+{@+state=skip_blanks;cur_chr=' ';
+}
+
+@ @<Finish line, |goto switch|@>=
+{@+loc=limit+1;goto get_cur_chr;
+}
+
+@ @<Finish line, emit a \.{\\par}@>=
+{@+loc=limit+1;cur_cs=par_loc;cur_cmd=eq_type(cur_cs);
+cur_chr=equiv(cur_cs);
+if (cur_cmd >= outer_call) check_outer_validity();
+}
+
+@ Notice that a code like \.{\^\^8} becomes \.x if not followed by a hex digit.
+
+@d is_hex(A) (((A >= '0')&&(A <= '9'))||((A >= 'a')&&(A <= 'f')))
+@d hex_to_cur_chr
+ if (c <= '9') cur_chr=c-'0';@+else cur_chr=c-'a'+10;
+ if (cc <= '9') cur_chr=16*cur_chr+cc-'0';
+ else cur_chr=16*cur_chr+cc-'a'+10
+
+@<If this |sup_mark| starts an expanded character...@>=
+{@+if (cur_chr==buffer[loc]) if (loc < limit)
+ {@+c=buffer[loc+1];@+if (c < 0200) /*yes we have an expanded char*/
+ {@+loc=loc+2;
+ if (is_hex(c)) if (loc <= limit)
+ {@+cc=buffer[loc];@+if (is_hex(cc))
+ {@+incr(loc);hex_to_cur_chr;goto reswitch;
+ }
+ }
+ if (c < 0100) cur_chr=c+0100;@+else cur_chr=c-0100;
+ goto reswitch;
+ }
+ }
+state=mid_line;
+}
+
+@ @<Process an active-character...@>=
+{@+cur_cs=cur_chr+active_base;
+cur_cmd=eq_type(cur_cs);cur_chr=equiv(cur_cs);state=mid_line;
+if (cur_cmd >= outer_call) check_outer_validity();
+}
+
+@ Control sequence names are scanned only when they appear in some line of
+a file; once they have been scanned the first time, their |eqtb| location
+serves as a unique identification, so \TeX\ doesn't need to refer to the
+original name any more except when it prints the equivalent in symbolic form.
+
+The program that scans a control sequence has been written carefully
+in order to avoid the blowups that might otherwise occur if a malicious
+user tried something like `\.{\\catcode\'15=0}'. The algorithm might
+look at |buffer[limit+1]|, but it never looks at |buffer[limit+2]|.
+
+If expanded characters like `\.{\^\^A}' or `\.{\^\^df}'
+appear in or just following
+a control sequence name, they are converted to single characters in the
+buffer and the process is repeated, slowly but surely.
+
+@<Scan a control...@>=
+{@+if (loc > limit) cur_cs=null_cs; /*|state| is irrelevant in this case*/
+else{@+start_cs: k=loc;cur_chr=buffer[k];cat=cat_code(cur_chr);
+ incr(k);
+ if (cat==letter) state=skip_blanks;
+ else if (cat==spacer) state=skip_blanks;
+ else state=mid_line;
+ if ((cat==letter)&&(k <= limit))
+ @<Scan ahead in the buffer until finding a nonletter; if an expanded code
+is encountered, reduce it and |goto start_cs|; otherwise if a multiletter
+control sequence is found, adjust |cur_cs| and |loc|, and |goto found|@>@;
+ else@<If an expanded code is present, reduce it and |goto start_cs|@>;
+ cur_cs=single_base+buffer[loc];incr(loc);
+ }
+found: cur_cmd=eq_type(cur_cs);cur_chr=equiv(cur_cs);
+if (cur_cmd >= outer_call) check_outer_validity();
+}
+
+@ Whenever we reach the following piece of code, we will have
+|cur_chr==buffer[k-1]| and |k <= limit+1| and |cat==cat_code(cur_chr)|. If an
+expanded code like \.{\^\^A} or \.{\^\^df} appears in |buffer[(k-1)dotdot(k+1)]|
+or |buffer[(k-1)dotdot(k+2)]|, we
+will store the corresponding code in |buffer[k-1]| and shift the rest of
+the buffer left two or three places.
+
+@<If an expanded...@>=
+{@+if (buffer[k]==cur_chr) @+if (cat==sup_mark) @+if (k < limit)
+ {@+c=buffer[k+1];@+if (c < 0200) /*yes, one is indeed present*/
+ {@+d=2;
+ if (is_hex(c)) @+if (k+2 <= limit)
+ {@+cc=buffer[k+2];@+if (is_hex(cc)) incr(d);
+ }
+ if (d > 2)
+ {@+hex_to_cur_chr;buffer[k-1]=cur_chr;
+ }
+ else if (c < 0100) buffer[k-1]=c+0100;
+ else buffer[k-1]=c-0100;
+ limit=limit-d;first=first-d;
+ while (k <= limit)
+ {@+buffer[k]=buffer[k+d];incr(k);
+ }
+ goto start_cs;
+ }
+ }
+}
+
+@ @<Scan ahead in the buffer...@>=
+{@+@/do@+{cur_chr=buffer[k];cat=cat_code(cur_chr);incr(k);
+}@+ while (!((cat!=letter)||(k > limit)));
+@<If an expanded...@>;
+if (cat!=letter) decr(k);
+ /*now |k| points to first nonletter*/
+if (k > loc+1) /*multiletter control sequence has been scanned*/
+ {@+cur_cs=id_lookup(loc, k-loc);loc=k;goto found;
+ }
+}
+
+@ Let's consider now what happens when |get_next| is looking at a token list.
+We restore the file and line information.
+@^profiling@>
+
+@<Input from token list, |goto restart| if end of list or if a parameter needs
+to be expanded@>=
+if (loc!=null) /*list not exhausted*/
+@^inner loop@>
+ {@+t=info(loc); cur_file_line=fl_mem[loc];
+ loc=link(loc); /*move to next*/
+ if (t >= cs_token_flag) /*a control sequence token*/
+ {@+cur_cs=t-cs_token_flag;
+ cur_cmd=eq_type(cur_cs);cur_chr=equiv(cur_cs);
+ if (cur_cmd >= outer_call)
+ if (cur_cmd==dont_expand)
+ @<Get the next token, suppressing expansion@>@;
+ else check_outer_validity();
+ }
+ else{@+cur_cmd=t/0400;cur_chr=t%0400;
+ switch (cur_cmd) {
+ case left_brace: incr(align_state);@+break;
+ case right_brace: decr(align_state);@+break;
+ case out_param: @<Insert macro parameter and |goto restart|@>@;
+ default:do_nothing;
+ }
+ }
+ }
+else{@+ /*we are done with this token list*/
+ end_token_list();goto restart; /*resume previous level*/
+ }
+
+@ The present point in the program is reached only when the |expand|
+routine has inserted a special marker into the input. In this special
+case, |info(loc)| is known to be a control sequence token, and |link(loc)==null|.
+
+@d no_expand_flag 257 /*this characterizes a special variant of |relax|*/
+
+@<Get the next token, suppressing expansion@>=
+{@+cur_cs=info(loc)-cs_token_flag;loc=null;@/
+cur_cmd=eq_type(cur_cs);cur_chr=equiv(cur_cs);
+if (cur_cmd > max_command)
+ {@+cur_cmd=relax;cur_chr=no_expand_flag;
+ }
+}
+
+@ @<Insert macro parameter...@>=
+{@+begin_token_list(param_stack[param_start+cur_chr-1], parameter);
+goto restart;
+}
+
+@ All of the easy branches of |get_next| have now been taken care of.
+There is one more branch.
+
+@d end_line_char_inactive (end_line_char < 0)||(end_line_char > 255)
+
+@<Move to next line of file, or |goto restart|...@>=
+if (name > 17) @<Read next line of file into |buffer|, or |goto restart| if
+the file has ended@>@;
+else{@+if (!terminal_input) /*\.{\\read} line has ended*/
+ {@+cur_cmd=0;cur_chr=0;
+ @<Set |cur_file_line| when a \.{\\read} line ends@>@;
+ return;
+ }
+ if (input_ptr > 0) /*text was inserted during error recovery*/
+ {@+end_file_reading();goto restart; /*resume previous level*/
+ }
+ if (selector < log_only) open_log_file();
+ if (interaction > nonstop_mode)
+ {@+if (end_line_char_inactive) incr(limit);
+ if (limit==start) /*previous line was empty*/
+ print_nl("(Please type a command or say `\\end')");
+@.Please type...@>
+ print_ln();first=start;
+ prompt_input("*"); /*input on-line into |buffer|*/
+@.*\relax@>
+ limit=last;
+ if (end_line_char_inactive) decr(limit);
+ else buffer[limit]=end_line_char;
+ first=limit+1;
+ loc=start;
+ }
+ else fatal_error("*** (job aborted, no legal \\end found)");
+@.job aborted@>
+ /*nonstop mode, which is intended for overnight batch processing,
+ never waits for on-line input*/
+ }
+
+@ The global variable |force_eof| is normally |false|; it is set |true|
+by an \.{\\endinput} command.
+
+@<Glob...@>=
+static bool @!force_eof; /*should the next \.{\\input} be aborted early?*/
+
+@ @<Read next line of file into |buffer|, or |goto restart| if the file has
+ended@>=
+{@+incr(line);first=start;
+@<check |line| for overflow@>@;
+if (!force_eof)
+ if (name <= 19)
+ {@+if (pseudo_input()) /*not end of file*/
+ firm_up_the_line(); /*this sets |limit|*/
+ else if ((every_eof!=null)&&!eof_seen[index])
+ {@+limit=first-1;eof_seen[index]=true; /*fake one empty line*/
+ begin_token_list(every_eof, every_eof_text);goto restart;
+ }
+ else force_eof=true;
+ }
+ else
+ {@+if (input_ln(&cur_file, true)) /*not end of file*/
+ firm_up_the_line(); /*this sets |limit|*/
+ else if ((every_eof!=null)&&!eof_seen[index])
+ {@+limit=first-1;eof_seen[index]=true; /*fake one empty line*/
+ begin_token_list(every_eof, every_eof_text);goto restart;
+ }
+ else force_eof=true;
+ }
+if (force_eof)
+ {@+if (tracing_nesting > 0)
+ if ((grp_stack[in_open]!=cur_boundary)||@|
+ (if_stack[in_open]!=cond_ptr)) file_warning();
+ /*give warning for some unfinished groups and/or conditionals*/
+ if (name >= 19)
+ {@+print_char(')');decr(open_parens);
+ update_terminal; /*show user that file has been read*/
+ }
+ force_eof=false;
+ end_file_reading(); /*resume previous level*/
+ check_outer_validity();goto restart;
+ }
+if (end_line_char_inactive) decr(limit);
+else buffer[limit]=end_line_char;
+first=limit+1;loc=start; /*ready to read*/
+}
+
+@ If the user has set the |pausing| parameter to some positive value,
+and if nonstop mode has not been selected, each line of input is displayed
+on the terminal and the transcript file, followed by `\.{=>}'.
+\TeX\ waits for a response. If the response is simply |carriage_return|, the
+line is accepted as it stands, otherwise the line typed is
+used instead of the line in the file.
+
+@p static void firm_up_the_line(void)
+{@+int k; /*an index into |buffer|*/
+limit=last;
+if (pausing > 0) if (interaction > nonstop_mode)
+ {@+wake_up_terminal;print_ln();
+ if (start < limit) for (k=start; k<=limit-1; k++) printn(buffer[k]);
+ first=limit;prompt_input("=>"); /*wait for user response*/
+@.=>@>
+ if (last > first)
+ {@+for (k=first; k<=last-1; k++) /*move line down in buffer*/
+ buffer[k+start-first]=buffer[k];
+ limit=start+last-first;
+ }
+ }
+}
+
+@ Since |get_next| is used so frequently in \TeX, it is convenient
+to define three related procedures that do a little more:
+
+\yskip\hang|get_token| not only sets |cur_cmd| and |cur_chr|, it
+also sets |cur_tok|, a packed halfword version of the current token.
+
+\yskip\hang|get_x_token|, meaning ``get an expanded token,'' is like
+|get_token|, but if the current token turns out to be a user-defined
+control sequence (i.e., a macro call), or a conditional,
+or something like \.{\\topmark} or \.{\\expandafter} or \.{\\csname},
+it is eliminated from the input by beginning the expansion of the macro
+or the evaluation of the conditional.
+
+\yskip\hang|x_token| is like |get_x_token| except that it assumes that
+|get_next| has already been called.
+
+\yskip\noindent
+In fact, these three procedures account for almost every use of |get_next|.
+
+@ No new control sequences will be defined except during a call of
+|get_token|, or when \.{\\csname} compresses a token list, because
+|no_new_control_sequence| is always |true| at other times.
+
+@p static void get_token(void) /*sets |cur_cmd|, |cur_chr|, |cur_tok|*/
+{@+no_new_control_sequence=false;get_next();no_new_control_sequence=true;
+@^inner loop@>
+if (cur_cs==0) cur_tok=(cur_cmd*0400)+cur_chr;
+else cur_tok=cs_token_flag+cur_cs;
+}
+
+@* Expanding the next token.
+Only a dozen or so command codes | > max_command| can possibly be returned by
+|get_next|; in increasing order, they are |undefined_cs|, |expand_after|,
+|no_expand|, |input|, |if_test|, |fi_or_else|, |cs_name|, |convert|, |the|,
+|top_bot_mark|, |call|, |long_call|, |outer_call|, |long_outer_call|, and
+|end_template|.{\emergencystretch=40pt\par}
+
+The |expand| subroutine is used when |cur_cmd > max_command|. It removes a
+``call'' or a conditional or one of the other special operations just
+listed. It follows that |expand| might invoke itself recursively. In all
+cases, |expand| destroys the current token, but it sets things up so that
+the next |get_next| will deliver the appropriate next token. The value of
+|cur_tok| need not be known when |expand| is called.
+
+Since several of the basic scanning routines communicate via global variables,
+their values are saved as local variables of |expand| so that
+recursive calls don't invalidate them.
+@^recursion@>
+
+@p @t\4@>@<Declare the procedure called |macro_call|@>@;@/
+@t\4@>@<Declare the procedure called |insert_relax|@>@;@/
+@t\4@>@<Declare \eTeX\ procedures for expanding@>@;@/
+static void pass_text(void);
+static void start_input(void);
+static void conditional(void);
+static void get_x_token(void);
+static void conv_toks(void);
+static void ins_the_toks(void);
+static void expand(void)
+{@+
+halfword t; /*token that is being ``expanded after''*/
+pointer @!p, @!q, @!r; /*for list manipulation*/
+int @!j; /*index into |buffer|*/
+int @!cv_backup; /*to save the global quantity |cur_val|*/
+small_number @!cvl_backup, @!radix_backup, @!co_backup;
+ /*to save |cur_val_level|, etc.*/
+pointer @!backup_backup; /*to save |link(backup_head)|*/
+small_number @!save_scanner_status; /*temporary storage of |scanner_status|*/
+cv_backup=cur_val;cvl_backup=cur_val_level;radix_backup=radix;
+co_backup=cur_order;backup_backup=link(backup_head);
+reswitch:
+if (cur_cmd < call) @<Expand a nonmacro@>@;
+else if (cur_cmd < end_template) macro_call();
+else@<Insert a token containing |frozen_endv|@>;
+cur_val=cv_backup;cur_val_level=cvl_backup;radix=radix_backup;
+cur_order=co_backup;link(backup_head)=backup_backup;
+}
+
+@ @<Expand a nonmacro@>=
+{@+if (tracing_commands > 1) show_cur_cmd_chr();
+switch (cur_cmd) {
+case top_bot_mark: @<Insert the \(a)appropriate mark text into the scanner@>@;@+break;
+case expand_after: switch (cur_chr) {
+case 0: @<Expand the token after the next token@>@;@+break;
+case 1: @<Negate a boolean conditional and |goto reswitch|@>@;@+break;
+@/@<Cases for |expandafter|@>@/
+} @+break; /*there are no other cases*/
+case no_expand: @<Suppress expansion of the next token@>@;@+break;
+case cs_name: @<Manufacture a control sequence name@>@;@+break;
+case convert: conv_toks();@+break; /*this procedure is discussed in Part 27 below*/
+case the: ins_the_toks();@+break; /*this procedure is discussed in Part 27 below*/
+case if_test: conditional();@+break; /*this procedure is discussed in Part 28 below*/
+case fi_or_else: @<Terminate the current conditional and skip to \.{\\fi}@>@;@+break;
+case input: @<Initiate or terminate input from a file@>;@+break;
+default:@<Complain about an undefined macro@>@;
+}
+}
+
+@ It takes only a little shuffling to do what \TeX\ calls \.{\\expandafter}.
+
+@<Expand the token after...@>=
+{@+get_token();t=cur_tok;get_token();
+if (cur_cmd > max_command) expand();@+else back_input();
+cur_tok=t;back_input();
+}
+
+@ The implementation of \.{\\noexpand} is a bit trickier, because it is
+necessary to insert a special `|dont_expand|' marker into \TeX's reading
+mechanism. This special marker is processed by |get_next|, but it does
+not slow down the inner loop.
+
+Since \.{\\outer} macros might arise here, we must also
+clear the |scanner_status| temporarily.
+
+@<Suppress expansion...@>=
+{@+save_scanner_status=scanner_status;scanner_status=normal;
+get_token();scanner_status=save_scanner_status;t=cur_tok;
+back_input(); /*now |start| and |loc| point to the backed-up token |t|*/
+if (t >= cs_token_flag)
+ {@+p=get_avail();info(p)=cs_token_flag+frozen_dont_expand;
+ link(p)=loc;start=p;loc=p;
+ }
+}
+
+@ @<Complain about an undefined macro@>=
+{@+print_err("Undefined control sequence");
+@.Undefined control sequence@>
+help5("The control sequence at the end of the top line",@/
+"of your error message was never \\def'ed. If you have",@/
+"misspelled it (e.g., `\\hobx'), type `I' and the correct",@/
+"spelling (e.g., `I\\hbox'). Otherwise just continue,",@/
+"and I'll forget about whatever was undefined.");
+error();
+}
+
+@ The |expand| procedure and some other routines that construct token
+lists find it convenient to use the following macros, which are valid only if
+the variables |p| and |q| are reserved for token-list building.
+Here we add code to store file and line information for each token.
+@^profiling@>
+
+@d store_new_token(A) {@+q=get_avail();link(p)=q;info(q)=A;
+ fl_mem[q]=cur_file_line;
+ p=q; /*|link(p)| is |null|*/
+ }
+@d fast_store_new_token(A) {@+fast_get_avail(q);link(p)=q;info(q)=A;
+ fl_mem[q]=cur_file_line;
+ p=q; /*|link(p)| is |null|*/
+ }
+
+@ @<Manufacture a control...@>=
+{@+r=get_avail();p=r; /*head of the list of characters*/
+incr(incsname_state);
+@/do@+{get_x_token();
+if (cur_cs==0) store_new_token(cur_tok);
+}@+ while (!(cur_cs!=0));
+if (cur_cmd!=end_cs_name) @<Complain about missing \.{\\endcsname}@>;
+decr(incsname_state);
+@<Look up the characters of list |r| in the hash table, and set |cur_cs|@>;
+flush_list(r);
+if (eq_type(cur_cs)==undefined_cs)
+ {@+eq_define(cur_cs, relax, 256); /*N.B.: The |save_stack| might change*/
+ } /*the control sequence will now match `\.{\\relax}'*/
+cur_tok=cur_cs+cs_token_flag;back_input();
+}
+
+@ @<Complain about missing \.{\\endcsname}@>=
+{@+print_err("Missing ");print_esc("endcsname");print(" inserted");
+@.Missing \\endcsname...@>
+help2("The control sequence marked <to be read again> should",@/
+ "not appear between \\csname and \\endcsname.");
+back_error();
+}
+
+@ @<Look up the characters of list |r| in the hash table...@>=
+j=first;p=link(r);
+while (p!=null)
+ {@+if (j >= max_buf_stack)
+ {@+max_buf_stack=j+1;
+ if (max_buf_stack==buf_size)
+ overflow("buffer size", buf_size);
+@:TeX capacity exceeded buffer size}{\quad buffer size@>
+ }
+ buffer[j]=info(p)%0400;incr(j);p=link(p);
+ }
+if (j==first) cur_cs=null_cs; /*the list is empty*/
+else if (j > first+1)
+ {@+no_new_control_sequence=false;cur_cs=id_lookup(first, j-first);
+ no_new_control_sequence=true;
+ }
+else cur_cs=single_base+buffer[first] /*the list has length one*/
+
+@ An |end_template| command is effectively changed to an |endv| command
+by the following code. (The reason for this is discussed below; the
+|frozen_end_template| at the end of the template has passed the
+|check_outer_validity| test, so its mission of error detection has been
+accomplished.)
+
+@<Insert a token containing |frozen_endv|@>=
+{@+cur_tok=cs_token_flag+frozen_endv;back_input();
+}
+
+@ The processing of \.{\\input} involves the |start_input| subroutine,
+which will be declared later; the processing of \.{\\endinput} is trivial.
+
+@<Put each...@>=
+primitive("input", input, 0);@/
+@!@:input\_}{\.{\\input} primitive@>
+input_loc=cur_val;input_token=cs_token_flag+input_loc;
+primitive("endinput", input, 1);@/
+@!@:end\_input\_}{\.{\\endinput} primitive@>
+
+@ @<Cases of |print_cmd_chr|...@>=
+case input: if (chr_code==0) print_esc("input")
+ @/@<Cases of |input| for |print_cmd_chr|@>;@/
+ else print_esc("endinput");@+break;
+
+@ @<Initiate or terminate input...@>=
+if (cur_chr==1) force_eof=true
+@/@<Cases for |input|@>;@/
+else if (name_in_progress) insert_relax();
+else start_input()
+
+@ Sometimes the expansion looks too far ahead, so we want to insert
+a harmless \.{\\relax} into the user's input.
+
+@<Declare the procedure called |insert_relax|@>=
+static void insert_relax(void)
+{@+cur_tok=cs_token_flag+cur_cs;back_input();
+cur_tok=cs_token_flag+frozen_relax;back_input();token_type=inserted;
+}
+
+@ Here is a recursive procedure that is \TeX's usual way to get the
+next token of input. It has been slightly optimized to take account of
+common cases.
+
+@p static void get_x_token(void) /*sets |cur_cmd|, |cur_chr|, |cur_tok|,
+ and expands macros*/
+{@+
+restart: get_next();
+@^inner loop@>
+if (cur_cmd <= max_command) goto done;
+if (cur_cmd >= call)
+ if (cur_cmd < end_template) macro_call();
+ else{@+cur_cs=frozen_endv;cur_cmd=endv;
+ goto done; /*|cur_chr==null_list|*/
+ }
+else expand();
+goto restart;
+done: if (cur_cs==0) cur_tok=(cur_cmd*0400)+cur_chr;
+else cur_tok=cs_token_flag+cur_cs;
+}
+
+@ The |get_x_token| procedure is essentially equivalent to two consecutive
+procedure calls: |get_next;x_token|.
+
+@p static void x_token(void) /*|get_x_token| without the initial |get_next|*/
+{@+while (cur_cmd > max_command)
+ {@+expand();
+ get_next();
+ }
+if (cur_cs==0) cur_tok=(cur_cmd*0400)+cur_chr;
+else cur_tok=cs_token_flag+cur_cs;
+}
+
+@ A control sequence that has been \.{\\def}'ed by the user is expanded by
+\TeX's |macro_call| procedure.
+
+Before we get into the details of |macro_call|, however, let's consider the
+treatment of primitives like \.{\\topmark}, since they are essentially
+macros without parameters. The token lists for such marks are kept in a
+global array of five pointers; we refer to the individual entries of this
+array by symbolic names |top_mark|, etc. The value of |top_mark| is either
+|null| or a pointer to the reference count of a token list.
+
+@d marks_code 5 /*add this for \.{\\topmarks} etc.*/
+@#
+@d top_mark_code 0 /*the mark in effect at the previous page break*/
+@d first_mark_code 1 /*the first mark between |top_mark| and |bot_mark|*/
+@d bot_mark_code 2 /*the mark in effect at the current page break*/
+@d split_first_mark_code 3 /*the first mark found by \.{\\vsplit}*/
+@d split_bot_mark_code 4 /*the last mark found by \.{\\vsplit}*/
+@d top_mark cur_mark[top_mark_code]
+@d first_mark cur_mark[first_mark_code]
+@d bot_mark cur_mark[bot_mark_code]
+@d split_first_mark cur_mark[split_first_mark_code]
+@d split_bot_mark cur_mark[split_bot_mark_code]
+
+@<Glob...@>=
+static pointer @!cur_mark0[split_bot_mark_code-top_mark_code+1],
+ *const @!cur_mark = @!cur_mark0-top_mark_code;
+ /*token lists for marks*/
+
+@ @<Set init...@>=
+top_mark=null;first_mark=null;bot_mark=null;
+split_first_mark=null;split_bot_mark=null;
+
+@ @<Put each...@>=
+primitive("topmark", top_bot_mark, top_mark_code);
+@!@:top\_mark\_}{\.{\\topmark} primitive@>
+primitive("firstmark", top_bot_mark, first_mark_code);
+@!@:first\_mark\_}{\.{\\firstmark} primitive@>
+primitive("botmark", top_bot_mark, bot_mark_code);
+@!@:bot\_mark\_}{\.{\\botmark} primitive@>
+primitive("splitfirstmark", top_bot_mark, split_first_mark_code);
+@!@:split\_first\_mark\_}{\.{\\splitfirstmark} primitive@>
+primitive("splitbotmark", top_bot_mark, split_bot_mark_code);
+@!@:split\_bot\_mark\_}{\.{\\splitbotmark} primitive@>
+
+@ @<Cases of |print_cmd_chr|...@>=
+case top_bot_mark: {@+switch ((chr_code%marks_code)) {
+ case first_mark_code: print_esc("firstmark");@+break;
+ case bot_mark_code: print_esc("botmark");@+break;
+ case split_first_mark_code: print_esc("splitfirstmark");@+break;
+ case split_bot_mark_code: print_esc("splitbotmark");@+break;
+ default:print_esc("topmark");
+ }
+ if (chr_code >= marks_code) print_char('s');
+ } @+break;
+
+@ The following code is activated when |cur_cmd==top_bot_mark| and
+when |cur_chr| is a code like |top_mark_code|.
+
+@<Insert the \(a)appropriate mark text into the scanner@>=
+{@+t=cur_chr%marks_code;
+if (cur_chr >= marks_code) scan_register_num();@+else cur_val=0;
+if (cur_val==0) cur_ptr=cur_mark[t];
+else@<Compute the mark pointer for mark type |t| and class |cur_val|@>;
+if (cur_ptr!=null) begin_token_list(cur_ptr, mark_text);
+}
+
+@ Now let's consider |macro_call| itself, which is invoked when \TeX\ is
+scanning a control sequence whose |cur_cmd| is either |call|, |long_call|,
+|outer_call|, or |long_outer_call|. The control sequence definition
+appears in the token list whose reference count is in location |cur_chr|
+of |mem|.
+
+The global variable |long_state| will be set to |call| or to |long_call|,
+depending on whether or not the control sequence disallows \.{\\par}
+in its parameters. The |get_next| routine will set |long_state| to
+|outer_call| and emit \.{\\par}, if a file ends or if an \.{\\outer}
+control sequence occurs in the midst of an argument.
+
+@<Glob...@>=
+static int @!long_state; /*governs the acceptance of \.{\\par}*/
+
+@ The parameters, if any, must be scanned before the macro is expanded.
+Parameters are token lists without reference counts. They are placed on
+an auxiliary stack called |pstack| while they are being scanned, since
+the |param_stack| may be losing entries during the matching process.
+(Note that |param_stack| can't be gaining entries, since |macro_call| is
+the only routine that puts anything onto |param_stack|, and it
+is not recursive.)
+
+@<Glob...@>=
+static pointer @!pstack[9]; /*arguments supplied to a macro*/
+
+@ After parameter scanning is complete, the parameters are moved to the
+|param_stack|. Then the macro body is fed to the scanner; in other words,
+|macro_call| places the defined text of the control sequence at the
+top of\/ \TeX's input stack, so that |get_next| will proceed to read it
+next.
+
+The global variable |cur_cs| contains the |eqtb| address of the control sequence
+being expanded, when |macro_call| begins. If this control sequence has not been
+declared \.{\\long}, i.e., if its command code in the |eq_type| field is
+not |long_call| or |long_outer_call|, its parameters are not allowed to contain
+the control sequence \.{\\par}. If an illegal \.{\\par} appears, the macro
+call is aborted, and the \.{\\par} will be rescanned.
+
+@<Declare the procedure called |macro_call|@>=
+static void macro_call(void) /*invokes a user-defined control sequence*/
+{@+
+pointer r; /*current node in the macro's token list*/
+pointer @!p; /*current node in parameter token list being built*/
+pointer @!q; /*new node being put into the token list*/
+pointer @!s; /*backup pointer for parameter matching*/
+pointer @!t; /*cycle pointer for backup recovery*/
+pointer @!u, @!v; /*auxiliary pointers for backup recovery*/
+pointer @!rbrace_ptr; /*one step before the last |right_brace| token*/
+small_number @!n; /*the number of parameters scanned*/
+halfword @!unbalance; /*unmatched left braces in current parameter*/
+int @!m; /*the number of tokens or groups (usually)*/
+pointer @!ref_count; /*start of the token list*/
+small_number @!save_scanner_status; /*|scanner_status| upon entry*/
+pointer @!save_warning_index; /*|warning_index| upon entry*/
+ASCII_code @!match_chr; /*character used in parameter*/
+@<additional local variables for |macro_call|@>@;
+save_scanner_status=scanner_status;save_warning_index=warning_index;
+warning_index=cur_cs;ref_count=cur_chr;r=link(ref_count);n=0;
+if (tracing_macros > 0) @<Show the text of the macro being expanded@>;
+if (info(r)==protected_token) r=link(r);
+if (info(r)!=end_match_token)
+ @<Scan the parameters and make |link(r)| point to the macro body; but |goto end|
+if an illegal \.{\\par} is detected@>;
+@<Feed the macro body and its parameters to the scanner@>;
+end: scanner_status=save_scanner_status;warning_index=save_warning_index;
+@<update the macro stack@>@;
+}
+
+@ Before we put a new token list on the input stack, it is wise to clean off
+all token lists that have recently been depleted. Then a user macro that ends
+with a call to itself will not require unbounded stack space.
+
+@<Feed the macro body and its parameters to the scanner@>=
+while ((state==token_list)&&(loc==null)&&(token_type!=v_template))
+ end_token_list(); /*conserve stack space*/
+begin_token_list(ref_count, macro);name=warning_index;loc=link(r);
+if (n > 0)
+ {@+if (param_ptr+n > max_param_stack)
+ {@+max_param_stack=param_ptr+n;
+ if (max_param_stack > param_size)
+ overflow("parameter stack size", param_size);
+@:TeX capacity exceeded parameter stack size}{\quad parameter stack size@>
+ }
+ for (m=0; m<=n-1; m++) param_stack[param_ptr+m]=pstack[m];
+ param_ptr=param_ptr+n;
+ }
+
+@ At this point, the reader will find it advisable to review the explanation
+of token list format that was presented earlier, since many aspects of that
+format are of importance chiefly in the |macro_call| routine.
+
+The token list might begin with a string of compulsory tokens before the
+first |match| or |end_match|. In that case the macro name is supposed to be
+followed by those tokens; the following program will set |s==null| to
+represent this restriction. Otherwise |s| will be set to the first token of
+a string that will delimit the next parameter.
+
+@<Scan the parameters and make |link(r)| point to the macro body...@>=
+{@+scanner_status=matching;unbalance=0;
+long_state=eq_type(cur_cs);
+if (long_state >= outer_call) long_state=long_state-2;
+@/do@+{link(temp_head)=null;
+if ((info(r) > match_token+255)||(info(r) < match_token)) s=null;
+else{@+match_chr=info(r)-match_token;s=link(r);r=s;
+ p=temp_head;m=0;
+ }
+@<Scan a parameter until its delimiter string has been found; or, if |s=null|,
+simply scan the delimiter string@>;@/
+ /*now |info(r)| is a token whose command code is either |match| or |end_match|*/
+}@+ while (!(info(r)==end_match_token));
+}
+
+@ If |info(r)| is a |match| or |end_match| command, it cannot be equal to
+any token found by |get_token|. Therefore an undelimited parameter---i.e.,
+a |match| that is immediately followed by |match| or |end_match|---will
+always fail the test `|cur_tok==info(r)|' in the following algorithm.
+
+@<Scan a parameter until its delimiter string has been found; or,...@>=
+resume: get_token(); /*set |cur_tok| to the next token of input*/
+if (cur_tok==info(r))
+ @<Advance \(r)|r|; |goto found| if the parameter delimiter has been fully
+matched, otherwise |goto resume|@>;
+@<Contribute the recently matched tokens to the current parameter, and |goto
+resume| if a partial match is still in effect; but abort if |s=null|@>;
+if (cur_tok==par_token) if (long_state!=long_call)
+ @<Report a runaway argument and abort@>;
+if (cur_tok < right_brace_limit)
+ if (cur_tok < left_brace_limit)
+ @<Contribute an entire group to the current parameter@>@;
+ else@<Report an extra right brace and |goto resume|@>@;
+else@<Store the current token, but |goto resume| if it is a blank space
+that would become an undelimited parameter@>;
+incr(m);
+if (info(r) > end_match_token) goto resume;
+if (info(r) < match_token) goto resume;
+found: if (s!=null) @<Tidy up the parameter just scanned, and tuck it away@>@;
+
+@ @<Store the current token, but |goto resume| if it is...@>=
+{@+if (cur_tok==space_token)
+ if (info(r) <= end_match_token)
+ if (info(r) >= match_token) goto resume;
+store_new_token(cur_tok);
+}
+
+@ A slightly subtle point arises here: When the parameter delimiter ends
+with `\.{\#\{}', the token list will have a left brace both before and
+after the |end_match|\kern-.4pt. Only one of these should affect the
+|align_state|, but both will be scanned, so we must make a correction.
+
+@<Advance \(r)|r|; |goto found| if the parameter delimiter has been fully...@>=
+{@+r=link(r);
+if ((info(r) >= match_token)&&(info(r) <= end_match_token))
+ {@+if (cur_tok < left_brace_limit) decr(align_state);
+ goto found;
+ }
+else goto resume;
+}
+
+@ @<Report an extra right brace and |goto resume|@>=
+{@+back_input();print_err("Argument of ");sprint_cs(warning_index);
+@.Argument of \\x has...@>
+print(" has an extra }");
+help6("I've run across a `}' that doesn't seem to match anything.",@/
+ "For example, `\\def\\a#1{...}' and `\\a}' would produce",@/
+ "this error. If you simply proceed now, the `\\par' that",@/
+ "I've just inserted will cause me to report a runaway",@/
+ "argument that might be the root of the problem. But if",@/
+ "your `}' was spurious, just type `2' and it will go away.");
+incr(align_state);long_state=call;cur_tok=par_token;ins_error();
+goto resume;
+} /*a white lie; the \.{\\par} won't always trigger a runaway*/
+
+@ If |long_state==outer_call|, a runaway argument has already been reported.
+
+@<Report a runaway argument and abort@>=
+{@+if (long_state==call)
+ {@+runaway();print_err("Paragraph ended before ");
+@.Paragraph ended before...@>
+ sprint_cs(warning_index);print(" was complete");
+ help3("I suspect you've forgotten a `}', causing me to apply this",@/
+ "control sequence to too much text. How can we recover?",@/
+ "My plan is to forget the whole thing and hope for the best.");
+ back_error();
+ }
+pstack[n]=link(temp_head);align_state=align_state-unbalance;
+for (m=0; m<=n; m++) flush_list(pstack[m]);
+goto end;
+}
+
+@ When the following code becomes active, we have matched tokens from |s| to
+the predecessor of |r|, and we have found that |cur_tok!=info(r)|. An
+interesting situation now presents itself: If the parameter is to be
+delimited by a string such as `\.{ab}', and if we have scanned `\.{aa}',
+we want to contribute one `\.a' to the current parameter and resume
+looking for a `\.b'. The program must account for such partial matches and
+for others that can be quite complex. But most of the time we have |s==r|
+and nothing needs to be done.
+
+Incidentally, it is possible for \.{\\par} tokens to sneak in to certain
+parameters of non-\.{\\long} macros. For example, consider a case like
+`\.{\\def\\a\#1\\par!\{...\}}' where the first \.{\\par} is not followed
+by an exclamation point. In such situations it does not seem appropriate
+to prohibit the \.{\\par}, so \TeX\ keeps quiet about this bending of
+the rules.
+
+@<Contribute the recently matched tokens to the current parameter...@>=
+if (s!=r)
+ if (s==null) @<Report an improper use of the macro and abort@>@;
+ else{@+t=s;
+ @/do@+{store_new_token(info(t));incr(m);u=link(t);v=s;
+ loop@+{@+if (u==r)
+ if (cur_tok!=info(v)) goto done;
+ else{@+r=link(v);goto resume;
+ }
+ if (info(u)!=info(v)) goto done;
+ u=link(u);v=link(v);
+ }
+ done: t=link(t);
+ }@+ while (!(t==r));
+ r=s; /*at this point, no tokens are recently matched*/
+ }
+
+@ @<Report an improper use...@>=
+{@+print_err("Use of ");sprint_cs(warning_index);
+@.Use of x doesn't match...@>
+print(" doesn't match its definition");
+help4("If you say, e.g., `\\def\\a1{...}', then you must always",@/
+ "put `1' after `\\a', since control sequence names are",@/
+ "made up of letters only. The macro here has not been",@/
+ "followed by the required stuff, so I'm ignoring it.");
+error();goto end;
+}
+
+@ @<Contribute an entire group to the current parameter@>=
+{@+unbalance=1;
+@^inner loop@>
+loop@+{@+fast_store_new_token(cur_tok);get_token();
+ if (cur_tok==par_token) if (long_state!=long_call)
+ @<Report a runaway argument and abort@>;
+ if (cur_tok < right_brace_limit)
+ if (cur_tok < left_brace_limit) incr(unbalance);
+ else{@+decr(unbalance);
+ if (unbalance==0) goto done1;
+ }
+ }
+done1: rbrace_ptr=p;store_new_token(cur_tok);
+}
+
+@ If the parameter consists of a single group enclosed in braces, we must
+strip off the enclosing braces. That's why |rbrace_ptr| was introduced.
+
+@<Tidy up the parameter just scanned, and tuck it away@>=
+{@+if ((m==1)&&(info(p) < right_brace_limit))
+ {@+link(rbrace_ptr)=null;free_avail(p);
+ p=link(temp_head);pstack[n]=link(p);free_avail(p);
+ }
+else pstack[n]=link(temp_head);
+incr(n);
+if (tracing_macros > 0)
+ {@+begin_diagnostic();print_nl("");printn(match_chr);print_int(n);
+ print("<-");show_token_list(pstack[n-1], null, 1000);
+ end_diagnostic(false);
+ }
+}
+
+@ @<Show the text of the macro being expanded@>=
+{@+begin_diagnostic();print_ln();print_cs(warning_index);
+token_show(ref_count);end_diagnostic(false);
+}
+
+@* Basic scanning subroutines.
+Let's turn now to some procedures that \TeX\ calls upon frequently to digest
+certain kinds of patterns in the input. Most of these are quite simple;
+some are quite elaborate. Almost all of the routines call |get_x_token|,
+which can cause them to be invoked recursively.
+@^stomach@>
+@^recursion@>
+
+@ The |scan_left_brace| routine is called when a left brace is supposed to be
+the next non-blank token. (The term ``left brace'' means, more precisely,
+a character whose catcode is |left_brace|.) \TeX\ allows \.{\\relax} to
+appear before the |left_brace|.
+
+@p static void scan_left_brace(void) /*reads a mandatory |left_brace|*/
+{@+@<Get the next non-blank non-relax non-call token@>;
+if (cur_cmd!=left_brace)
+ {@+print_err("Missing { inserted");
+@.Missing \{ inserted@>
+ help4("A left brace was mandatory here, so I've put one in.",@/
+ "You might want to delete and/or insert some corrections",@/
+ "so that I will find a matching right brace soon.",@/
+ "(If you're confused by all this, try typing `I}' now.)");
+ back_error();cur_tok=left_brace_token+'{';cur_cmd=left_brace;
+ cur_chr='{';incr(align_state);
+ }
+}
+
+@ @<Get the next non-blank non-relax non-call token@>=
+@/do@+{get_x_token();
+}@+ while (!((cur_cmd!=spacer)&&(cur_cmd!=relax)))
+
+@ The |scan_optional_equals| routine looks for an optional `\.=' sign preceded
+by optional spaces; `\.{\\relax}' is not ignored here.
+
+@p static void scan_optional_equals(void)
+{@+@<Get the next non-blank non-call token@>;
+if (cur_tok!=other_token+'=') back_input();
+}
+
+@ @<Get the next non-blank non-call token@>=
+@/do@+{get_x_token();
+}@+ while (!(cur_cmd!=spacer))
+
+@ In case you are getting bored, here is a slightly less trivial routine:
+Given a string of lowercase letters, like `\.{pt}' or `\.{plus}' or
+`\.{width}', the |scan_keyword| routine checks to see whether the next
+tokens of input match this string. The match must be exact, except that
+uppercase letters will match their lowercase counterparts; uppercase
+equivalents are determined by subtracting |'a'-'A'|, rather than using the
+|uc_code| table, since \TeX\ uses this routine only for its own limited
+set of keywords.
+
+If a match is found, the characters are effectively removed from the input
+and |true| is returned. Otherwise |false| is returned, and the input
+is left essentially unchanged (except for the fact that some macros
+may have been expanded, etc.).
+@^inner loop@>
+
+@p static bool scan_keyword(char *@!s) /*look for a given string*/
+{@+
+pointer p; /*tail of the backup list*/
+pointer @!q; /*new node being added to the token list via |store_new_token|*/
+p=backup_head;link(p)=null;
+while (*s!=0)
+ {@+get_x_token(); /*recursion is possible here*/
+@^recursion@>
+ if ((cur_cs==0)&&@|
+ ((cur_chr==so(*s))||(cur_chr==so(*s)-'a'+'A')))
+ {@+store_new_token(cur_tok);incr(s);
+ }
+ else if ((cur_cmd!=spacer)||(p!=backup_head))
+ {@+back_input();
+ if (p!=backup_head) back_list(link(backup_head));
+ return false;
+ }
+ }
+flush_list(link(backup_head));return true;
+}
+
+@ Here is a procedure that sounds an alarm when mu and non-mu units
+are being switched.
+
+@p static void mu_error(void)
+{@+print_err("Incompatible glue units");
+@.Incompatible glue units@>
+help1("I'm going to assume that 1mu=1pt when they're mixed.");
+error();
+}
+
+@ The next routine `|scan_something_internal|' is used to fetch internal
+numeric quantities like `\.{\\hsize}', and also to handle the `\.{\\the}'
+when expanding constructions like `\.{\\the\\toks0}' and
+`\.{\\the\\baselineskip}'. Soon we will be considering the |scan_int|
+procedure, which calls |scan_something_internal|; on the other hand,
+|scan_something_internal| also calls |scan_int|, for constructions like
+`\.{\\catcode\`\\\$}' or `\.{\\fontdimen} \.3 \.{\\ff}'. So we
+have to declare |scan_int| as a |forward| procedure. A few other
+procedures are also declared at this point.
+
+@p static void scan_int(void); /*scans an integer value*/
+@t\4\4@>@<Declare procedures that scan restricted classes of integers@>@;
+@t\4\4@>@<Declare \eTeX\ procedures for scanning@>@;
+@t\4\4@>@<Declare procedures that scan font-related stuff@>@;
+
+@ \TeX\ doesn't know exactly what to expect when |scan_something_internal|
+begins. For example, an integer or dimension or glue value could occur
+immediately after `\.{\\hskip}'; and one can even say \.{\\the} with
+respect to token lists in constructions like
+`\.{\\xdef\\o\{\\the\\output\}}'. On the other hand, only integers are
+allowed after a construction like `\.{\\count}'. To handle the various
+possibilities, |scan_something_internal| has a |level| parameter, which
+tells the ``highest'' kind of quantity that |scan_something_internal| is
+allowed to produce. Six levels are distinguished, namely |int_val|,
+|dimen_val|, |glue_val|, |mu_val|, |ident_val|, and |tok_val|.
+
+The output of |scan_something_internal| (and of the other routines
+|scan_int|, |scan_dimen|, and |scan_glue| below) is put into the global
+variable |cur_val|, and its level is put into |cur_val_level|. The highest
+values of |cur_val_level| are special: |mu_val| is used only when
+|cur_val| points to something in a ``muskip'' register, or to one of the
+three parameters \.{\\thinmuskip}, \.{\\medmuskip}, \.{\\thickmuskip};
+|ident_val| is used only when |cur_val| points to a font identifier;
+|tok_val| is used only when |cur_val| points to |null| or to the reference
+count of a token list. The last two cases are allowed only when
+|scan_something_internal| is called with |level==tok_val|.
+
+If the output is glue, |cur_val| will point to a glue specification, and
+the reference count of that glue will have been updated to reflect this
+reference; if the output is a nonempty token list, |cur_val| will point to
+its reference count, but in this case the count will not have been updated.
+Otherwise |cur_val| will contain the integer or scaled value in question.
+
+@d int_val 0 /*integer values*/
+@d dimen_val 1 /*dimension values*/
+@d glue_val 2 /*glue specifications*/
+@d mu_val 3 /*math glue specifications*/
+@d ident_val 4 /*font identifier*/
+@d tok_val 5 /*token lists*/
+
+@<Glob...@>=
+static int @!cur_val; /*value returned by numeric scanners*/
+static int @!cur_val_level; /*the ``level'' of this value*/
+
+@ The hash table is initialized with `\.{\\count}', `\.{\\dimen}', `\.{\\skip}',
+and `\.{\\muskip}' all having |internal_register| as their command code; they are
+distinguished by the |chr_code|, which is either |int_val|, |dimen_val|,
+|glue_val|, or |mu_val| more than |mem_bot| (dynamic variable-size nodes
+cannot have these values)
+
+@<Put each...@>=
+primitive("count", internal_register, mem_bot+int_val);
+@!@:count\_}{\.{\\count} primitive@>
+primitive("dimen", internal_register, mem_bot+dimen_val);
+@!@:dimen\_}{\.{\\dimen} primitive@>
+primitive("skip", internal_register, mem_bot+glue_val);
+@!@:skip\_}{\.{\\skip} primitive@>
+primitive("muskip", internal_register, mem_bot+mu_val);
+@!@:mu\_skip\_}{\.{\\muskip} primitive@>
+
+@ @<Cases of |print_cmd_chr|...@>=
+case internal_register: @<Cases of |register| for |print_cmd_chr|@>@;@+break;
+
+@ OK, we're ready for |scan_something_internal| itself. A second parameter,
+|negative|, is set |true| if the value that is found should be negated.
+It is assumed that |cur_cmd| and |cur_chr| represent the first token of
+the internal quantity to be scanned; an error will be signalled if
+|cur_cmd < min_internal| or |cur_cmd > max_internal|.
+
+@d scanned_result(A, B) @+{@+cur_val=A;cur_val_level=B;@+}
+
+@p static void scan_something_internal(small_number @!level, bool @!negative)
+ /*fetch an internal parameter*/
+{@+
+halfword m; /*|chr_code| part of the operand token*/
+pointer @!q, @!r; /*general purpose indices*/
+pointer @!tx; /*effective tail node*/
+four_quarters @!i; /*character info*/
+int @!p; /*index into |nest|*/
+m=cur_chr;
+switch (cur_cmd) {
+case def_code: @<Fetch a character code from some table@>@;@+break;
+case toks_register: case assign_toks:
+ case def_family: case set_font: case def_font: @<Fetch a token list or font
+identifier, provided that |level=tok_val|@>@;@+break;
+case assign_int: scanned_result(eqtb[m].i, int_val)@;@+break;
+case assign_dimen: scanned_result(eqtb[m].sc, dimen_val)@;@+break;
+case assign_glue: scanned_result(equiv(m), glue_val)@;@+break;
+case assign_mu_glue: scanned_result(equiv(m), mu_val)@;@+break;
+case set_aux: @<Fetch the |space_factor| or the |prev_depth|@>@;@+break;
+case set_prev_graf: @<Fetch the |prev_graf|@>@;@+break;
+case set_page_int: @<Fetch the |dead_cycles| or the |insert_penalties|@>@;@+break;
+case set_page_dimen: @<Fetch something on the |page_so_far|@>@;@+break;
+case set_shape: @<Fetch the |par_shape| size@>@;@+break;
+case set_box_dimen: @<Fetch a box dimension@>@;@+break;
+case char_given: case math_given: scanned_result(cur_chr, int_val)@;@+break;
+case assign_font_dimen: @<Fetch a font dimension@>@;@+break;
+case assign_font_int: @<Fetch a font integer@>@;@+break;
+case internal_register: @<Fetch a register@>@;@+break;
+case last_item: @<Fetch an item in the current node, if appropriate@>@;@+break;
+default:@<Complain that \.{\\the} can't do this; give zero result@>@;
+} @/
+while (cur_val_level > level) @<Convert \(c)|cur_val| to a lower level@>;
+@<Fix the reference count, if any, and negate |cur_val| if |negative|@>;
+}
+
+@ @<Fetch a character code from some table@>=
+{@+scan_char_num();
+if (m==math_code_base) scanned_result(ho(math_code(cur_val)), int_val)@;
+else if (m < math_code_base) scanned_result(equiv(m+cur_val), int_val)@;
+else scanned_result(eqtb[m+cur_val].i, int_val);
+}
+
+@ @<Fetch a token list...@>=
+if (level!=tok_val)
+ {@+print_err("Missing number, treated as zero");
+@.Missing number...@>
+ help3("A number should have been here; I inserted `0'.",@/
+ "(If you can't figure out why I needed to see a number,",@/
+ "look up `weird error' in the index to The TeXbook.)");
+@:TeXbook}{\sl The \TeX book@>
+ back_error();scanned_result(0, dimen_val);
+ }
+else if (cur_cmd <= assign_toks)
+ {@+if (cur_cmd < assign_toks) /*|cur_cmd==toks_register|*/
+ if (m==mem_bot)
+ {@+scan_register_num();
+ if (cur_val < 256) cur_val=equiv(toks_base+cur_val);
+ else{@+find_sa_element(tok_val, cur_val, false);
+ if (cur_ptr==null) cur_val=null;
+ else cur_val=sa_ptr(cur_ptr);
+ }
+ }
+ else cur_val=sa_ptr(m);
+ else cur_val=equiv(m);
+ cur_val_level=tok_val;
+ }
+else{@+back_input();scan_font_ident();
+ scanned_result(font_id_base+cur_val, ident_val);
+ }
+
+@ Users refer to `\.{\\the\\spacefactor}' only in horizontal
+mode, and to `\.{\\the\\prevdepth}' only in vertical mode; so we put the
+associated mode in the modifier part of the |set_aux| command.
+The |set_page_int| command has modifier 0 or 1, for `\.{\\deadcycles}' and
+`\.{\\insertpenalties}', respectively. The |set_box_dimen| command is
+modified by either |width_offset|, |height_offset|, or |depth_offset|.
+And the |last_item| command is modified by either |int_val|, |dimen_val|,
+|glue_val|, |input_line_no_code|, or |badness_code|.
+\eTeX\ inserts |last_node_type_code| after |glue_val| and adds
+the codes for its extensions: |eTeX_version_code|, \dots\ .
+
+@d last_node_type_code (glue_val+1) /*code for \.{\\lastnodetype}*/
+@d input_line_no_code (glue_val+2) /*code for \.{\\inputlineno}*/
+@d badness_code (input_line_no_code+1) /*code for \.{\\badness}*/
+@#
+@d eTeX_int (pdftex_last_item_codes+1) /*first of \eTeX\ codes for integers*/
+@d eTeX_dim (eTeX_int+8) /*first of \eTeX\ codes for dimensions*/
+@d eTeX_glue (eTeX_dim+9) /*first of \eTeX\ codes for glue*/
+@d eTeX_mu (eTeX_glue+1) /*first of \eTeX\ codes for muglue*/
+@d eTeX_expr (eTeX_mu+1) /*first of \eTeX\ codes for expressions*/
+@d eTeX_last_last_item_cmd_mod (eTeX_expr-int_val+mu_val) /*\.{\\muexpr}*/
+
+@<Put each...@>=
+primitive("spacefactor", set_aux, hmode);
+@!@:space\_factor\_}{\.{\\spacefactor} primitive@>
+primitive("prevdepth", set_aux, vmode);@/
+@!@:prev\_depth\_}{\.{\\prevdepth} primitive@>
+primitive("deadcycles", set_page_int, 0);
+@!@:dead\_cycles\_}{\.{\\deadcycles} primitive@>
+primitive("insertpenalties", set_page_int, 1);
+@!@:insert\_penalties\_}{\.{\\insertpenalties} primitive@>
+primitive("wd", set_box_dimen, width_offset);
+@!@:wd\_}{\.{\\wd} primitive@>
+primitive("ht", set_box_dimen, height_offset);
+@!@:ht\_}{\.{\\ht} primitive@>
+primitive("dp", set_box_dimen, depth_offset);
+@!@:dp\_}{\.{\\dp} primitive@>
+primitive("lastpenalty", last_item, int_val);
+@!@:last\_penalty\_}{\.{\\lastpenalty} primitive@>
+primitive("lastkern", last_item, dimen_val);
+@!@:last\_kern\_}{\.{\\lastkern} primitive@>
+primitive("lastskip", last_item, glue_val);
+@!@:last\_skip\_}{\.{\\lastskip} primitive@>
+primitive("inputlineno", last_item, input_line_no_code);
+@!@:input\_line\_no\_}{\.{\\inputlineno} primitive@>
+primitive("badness", last_item, badness_code);
+@!@:badness\_}{\.{\\badness} primitive@>
+
+@ @<Cases of |print_cmd_chr|...@>=
+case set_aux: if (chr_code==vmode) print_esc("prevdepth");
+@+else print_esc("spacefactor");@+break;
+case set_page_int: if (chr_code==0) print_esc("deadcycles")
+@/@<Cases of |set_page_int| for |print_cmd_chr|@>;@/
+@+else print_esc("insertpenalties");@+break;
+case set_box_dimen: if (chr_code==width_offset) print_esc("wd");
+else if (chr_code==height_offset) print_esc("ht");
+else print_esc("dp");@+break;
+case last_item: switch (chr_code) {
+ case int_val: print_esc("lastpenalty");@+break;
+ case dimen_val: print_esc("lastkern");@+break;
+ case glue_val: print_esc("lastskip");@+break;
+ case input_line_no_code: print_esc("inputlineno");@+break;
+ @/@<Cases of |last_item| for |print_cmd_chr|@>@/
+ default:print_esc("badness");
+ } @+break;
+
+@ @<Fetch the |space_factor| or the |prev_depth|@>=
+if (abs(mode)!=m)
+ {@+print_err("Improper ");print_cmd_chr(set_aux, m);
+@.Improper \\spacefactor@>
+@.Improper \\prevdepth@>
+ help4("You can refer to \\spacefactor only in horizontal mode;",@/
+ "you can refer to \\prevdepth only in vertical mode; and",@/
+ "neither of these is meaningful inside \\write. So",@/
+ "I'm forgetting what you said and using zero instead.");
+ error();
+ if (level!=tok_val) scanned_result(0, dimen_val)@;
+ else scanned_result(0, int_val);
+ }
+else if (m==vmode) scanned_result(prev_depth, dimen_val)@;
+else scanned_result(space_factor, int_val)
+
+@ @<Fetch the |dead_cycles| or the |insert_penalties|@>=
+{@+if (m==0) cur_val=dead_cycles
+@/@<Cases for `Fetch the |dead_cycles| or the |insert_penalties|'@>;@/
+else cur_val=insert_penalties;
+cur_val_level=int_val;
+}
+
+@ @<Fetch a box dimension@>=
+{@+scan_register_num();fetch_box(q);
+if (q==null) cur_val=0;@+else cur_val=mem[q+m].sc;
+cur_val_level=dimen_val;
+}
+
+@ Inside an \.{\\output} routine, a user may wish to look at the page totals
+that were present at the moment when output was triggered.
+
+@d max_dimen 07777777777 /*$2^{30}-1$*/
+
+@<Fetch something on the |page_so_far|@>=
+{@+if ((page_contents==empty)&&(!output_active))
+ if (m==0) cur_val=max_dimen;@+else cur_val=0;
+else cur_val=page_so_far[m];
+cur_val_level=dimen_val;
+}
+
+@ @<Fetch the |prev_graf|@>=
+if (mode==0) scanned_result(0, int_val)@; /*|prev_graf==0| within \.{\\write}*/
+else{@+nest[nest_ptr]=cur_list;p=nest_ptr;
+ while (abs(nest[p].mode_field)!=vmode) decr(p);
+ scanned_result(nest[p].pg_field, int_val);
+ }
+
+@ @<Fetch the |par_shape| size@>=
+{@+if (m > par_shape_loc) @<Fetch a penalties array element@>@;
+else if (par_shape_ptr==null) cur_val=0;
+else cur_val=info(par_shape_ptr);
+cur_val_level=int_val;
+}
+
+@ Here is where \.{\\lastpenalty}, \.{\\lastkern}, \.{\\lastskip}, and
+\.{\\lastnodetype} are
+implemented. The reference count for \.{\\lastskip} will be updated later.
+
+We also handle \.{\\inputlineno} and \.{\\badness} here, because they are
+legal in similar contexts.
+
+@<Fetch an item in the current node...@>=
+if (m > eTeX_last_last_item_cmd_mod)
+{@+switch (m) {
+ @/@<Cases for fetching a \Prote\ int value@>@/
+ } /*there are no other cases*/
+ cur_val_level=int_val;
+}
+else if (m >= input_line_no_code)
+{if (m >= eTeX_glue) @<Process an expression and |return|@>@;
+ else if (m >= eTeX_dim)
+ {@+switch (m) {
+ @/@<Cases for fetching a dimension value@>@/
+ } /*there are no other cases*/
+ cur_val_level=dimen_val;
+ }
+ else
+ {@+switch (m) {
+ case input_line_no_code: cur_val=line;@+break;
+ case badness_code: cur_val=last_badness;@+break;
+ @/@<Cases for fetching an integer value@>@/
+ } /*there are no other cases*/
+ cur_val_level=int_val;
+ }
+}
+else{@+if (cur_chr==glue_val) cur_val=zero_glue;@+else cur_val=0;
+ tx=tail;
+ if (cur_chr==last_node_type_code)
+ {@+cur_val_level=int_val;
+ if ((tx==head)||(mode==0)) cur_val=-1;
+ }
+ else cur_val_level=cur_chr;
+ if (!is_char_node(tx)&&(mode!=0))
+ switch (cur_chr) {
+ case int_val: if (type(tx)==penalty_node) cur_val=penalty(tx);@+break;
+ case dimen_val: if (type(tx)==kern_node) cur_val=width(tx);@+break;
+ case glue_val: if (type(tx)==glue_node)
+ {@+cur_val=glue_ptr(tx);
+ if (subtype(tx)==mu_glue) cur_val_level=mu_val;
+ } @+break;
+ case last_node_type_code: if (type(tx) <= unset_node) cur_val=type(tx)+1;
+ else cur_val=unset_node+2;
+ } /*there are no other cases*/
+ else if ((mode==vmode)&&(tx==head))
+ switch (cur_chr) {
+ case int_val: cur_val=last_penalty;@+break;
+ case dimen_val: cur_val=last_kern;@+break;
+ case glue_val: if (last_glue!=max_halfword) cur_val=last_glue;@+break;
+ case last_node_type_code: cur_val=last_node_type;
+ } /*there are no other cases*/
+ }
+
+@ @<Fetch a font dimension@>=
+{@+find_font_dimen(false);font_info[fmem_ptr].sc=0;
+scanned_result(font_info[cur_val].sc, dimen_val);
+}
+
+@ @<Fetch a font integer@>=
+{@+scan_font_ident();
+if (m==0) scanned_result(hyphen_char[cur_val], int_val)@;
+else scanned_result(skew_char[cur_val], int_val);
+}
+
+@ @<Fetch a register@>=
+{@+if ((m < mem_bot)||(m > lo_mem_stat_max))
+ {@+cur_val_level=sa_type(m);
+ if (cur_val_level < glue_val) cur_val=sa_int(m);
+ else cur_val=sa_ptr(m);
+ }
+else{@+scan_register_num();cur_val_level=m-mem_bot;
+ if (cur_val > 255)
+ {@+find_sa_element(cur_val_level, cur_val, false);
+ if (cur_ptr==null)
+ if (cur_val_level < glue_val) cur_val=0;
+ else cur_val=zero_glue;
+ else if (cur_val_level < glue_val) cur_val=sa_int(cur_ptr);
+ else cur_val=sa_ptr(cur_ptr);
+ }
+ else
+ switch (cur_val_level) {
+case int_val: cur_val=count(cur_val);@+break;
+case dimen_val: cur_val=dimen(cur_val);@+break;
+case glue_val: cur_val=skip(cur_val);@+break;
+case mu_val: cur_val=mu_skip(cur_val);
+} /*there are no other cases*/
+ }
+}
+
+@ @<Complain that \.{\\the} can't do this; give zero result@>=
+{@+print_err("You can't use `");print_cmd_chr(cur_cmd, cur_chr);
+@.You can't use x after ...@>
+print("' after ");print_esc("the");
+help1("I'm forgetting what you said and using zero instead.");
+error();
+if (level!=tok_val) scanned_result(0, dimen_val)@;
+else scanned_result(0, int_val);
+}
+
+@ When a |glue_val| changes to a |dimen_val|, we use the width component
+of the glue; there is no need to decrease the reference count, since it
+has not yet been increased. When a |dimen_val| changes to an |int_val|,
+we use scaled points so that the value doesn't actually change. And when a
+|mu_val| changes to a |glue_val|, the value doesn't change either.
+
+@<Convert \(c)|cur_val| to a lower level@>=
+{@+if (cur_val_level==glue_val) cur_val=width(cur_val);
+else if (cur_val_level==mu_val) mu_error();
+decr(cur_val_level);
+}
+
+@ If |cur_val| points to a glue specification at this point, the reference
+count for the glue does not yet include the reference by |cur_val|.
+If |negative| is |true|, |cur_val_level| is known to be | <= mu_val|.
+
+@<Fix the reference count, if any,...@>=
+if (negative)
+ if (cur_val_level >= glue_val)
+ {@+cur_val=new_spec(cur_val);
+ @<Negate all three glue components of |cur_val|@>;
+ }
+ else negate(cur_val);
+else if ((cur_val_level >= glue_val)&&(cur_val_level <= mu_val))
+ add_glue_ref(cur_val)
+
+@ @<Negate all three...@>=
+{@+negate(width(cur_val));
+negate(stretch(cur_val));
+negate(shrink(cur_val));
+}
+
+@ Our next goal is to write the |scan_int| procedure, which scans anything that
+\TeX\ treats as an integer. But first we might as well look at some simple
+applications of |scan_int| that have already been made inside of
+|scan_something_internal|.
+
+@ @<Declare procedures that scan restricted classes of integers@>=
+static void scan_eight_bit_int(void)
+{@+scan_int();
+if ((cur_val < 0)||(cur_val > 255))
+ {@+print_err("Bad register code");
+@.Bad register code@>
+ help2("A register number must be between 0 and 255.",@/
+ "I changed this one to zero.");int_error(cur_val);cur_val=0;
+ }
+}
+
+@ @<Declare procedures that scan restricted classes of integers@>=
+static void scan_char_num(void)
+{@+scan_int();
+if ((cur_val < 0)||(cur_val > 255))
+ {@+print_err("Bad character code");
+@.Bad character code@>
+ help2("A character number must be between 0 and 255.",@/
+ "I changed this one to zero.");int_error(cur_val);cur_val=0;
+ }
+}
+
+@ While we're at it, we might as well deal with similar routines that
+will be needed later.
+
+@<Declare procedures that scan restricted classes of integers@>=
+static void scan_four_bit_int(void)
+{@+scan_int();
+if ((cur_val < 0)||(cur_val > 15))
+ {@+print_err("Bad number");
+@.Bad number@>
+ help2("Since I expected to read a number between 0 and 15,",@/
+ "I changed this one to zero.");int_error(cur_val);cur_val=0;
+ }
+}
+
+@ @<Declare procedures that scan restricted classes of integers@>=
+static void scan_fifteen_bit_int(void)
+{@+scan_int();
+if ((cur_val < 0)||(cur_val > 077777))
+ {@+print_err("Bad mathchar");
+@.Bad mathchar@>
+ help2("A mathchar number must be between 0 and 32767.",@/
+ "I changed this one to zero.");int_error(cur_val);cur_val=0;
+ }
+}
+
+@ @<Declare procedures that scan restricted classes of integers@>=
+static void scan_twenty_seven_bit_int(void)
+{@+scan_int();
+if ((cur_val < 0)||(cur_val > 0777777777))
+ {@+print_err("Bad delimiter code");
+@.Bad delimiter code@>
+ help2("A numeric delimiter code must be between 0 and 2^{27}-1.",@/
+ "I changed this one to zero.");int_error(cur_val);cur_val=0;
+ }
+}
+
+@ An integer number can be preceded by any number of spaces and `\.+' or
+`\.-' signs. Then comes either a decimal constant (i.e., radix 10), an
+octal constant (i.e., radix 8, preceded by~\.\'), a hexadecimal constant
+(radix 16, preceded by~\."), an alphabetic constant (preceded by~\.\`), or
+an internal variable. After scanning is complete,
+|cur_val| will contain the answer, which must be at most
+$2^{31}-1=2147483647$ in absolute value. The value of |radix| is set to
+10, 8, or 16 in the cases of decimal, octal, or hexadecimal constants,
+otherwise |radix| is set to zero. An optional space follows a constant.
+
+@d octal_token (other_token+'\'') /*apostrophe, indicates an octal constant*/
+@d hex_token (other_token+'"') /*double quote, indicates a hex constant*/
+@d alpha_token (other_token+'`') /*reverse apostrophe, precedes alpha constants*/
+@d point_token (other_token+'.') /*decimal point*/
+@d continental_point_token (other_token+',') /*decimal point, Eurostyle*/
+
+@<Glob...@>=
+static small_number @!radix; /*|scan_int| sets this to 8, 10, 16, or zero*/
+
+@ We initialize the following global variables just in case |expand|
+comes into action before any of the basic scanning routines has assigned
+them a value.
+
+@<Set init...@>=
+cur_val=0;cur_val_level=int_val;radix=0;cur_order=normal;
+
+@ The |scan_int| routine is used also to scan the integer part of a
+fraction; for example, the `\.3' in `\.{3.14159}' will be found by
+|scan_int|. The |scan_dimen| routine assumes that |cur_tok==point_token|
+after the integer part of such a fraction has been scanned by |scan_int|,
+and that the decimal point has been backed up to be scanned again.
+
+@p static void scan_int(void) /*sets |cur_val| to an integer*/
+{@+
+bool negative; /*should the answer be negated?*/
+int @!m; /*|@t$2^{31}$@>/radix|, the threshold of danger*/
+small_number @!d; /*the digit just scanned*/
+bool @!vacuous; /*have no digits appeared?*/
+bool @!OK_so_far; /*has an error message been issued?*/
+radix=0;OK_so_far=true;@/
+@<Get the next non-blank non-sign token; set |negative| appropriately@>;
+if (cur_tok==alpha_token) @<Scan an alphabetic character code into |cur_val|@>@;
+else if ((cur_cmd >= min_internal)&&(cur_cmd <= max_internal))
+ scan_something_internal(int_val, false);
+else@<Scan a numeric constant@>;
+if (negative) negate(cur_val);
+}
+
+@ @<Get the next non-blank non-sign token...@>=
+negative=false;
+@/do@+{@<Get the next non-blank non-call token@>;
+if (cur_tok==other_token+'-')
+ {@+negative=!negative;cur_tok=other_token+'+';
+ }
+}@+ while (!(cur_tok!=other_token+'+'))
+
+@ A space is ignored after an alphabetic character constant, so that
+such constants behave like numeric ones.
+
+@<Scan an alphabetic character code into |cur_val|@>=
+{@+get_token(); /*suppress macro expansion*/
+if (cur_tok < cs_token_flag)
+ {@+cur_val=cur_chr;
+ if (cur_cmd <= right_brace)
+ if (cur_cmd==right_brace) incr(align_state);
+ else decr(align_state);
+ }
+else if (cur_tok < cs_token_flag+single_base)
+ cur_val=cur_tok-cs_token_flag-active_base;
+else cur_val=cur_tok-cs_token_flag-single_base;
+if (cur_val > 255)
+ {@+print_err("Improper alphabetic constant");
+@.Improper alphabetic constant@>
+ help2("A one-character control sequence belongs after a ` mark.",@/
+ "So I'm essentially inserting \\0 here.");
+ cur_val='0';back_error();
+ }
+else@<Scan an optional space@>;
+}
+
+@ @<Scan an optional space@>=
+{@+get_x_token();if (cur_cmd!=spacer) back_input();
+}
+
+@ @<Scan a numeric constant@>=
+{@+radix=10;m=214748364;
+if (cur_tok==octal_token)
+ {@+radix=8;m=02000000000;get_x_token();
+ }
+else if (cur_tok==hex_token)
+ {@+radix=16;m=01000000000;get_x_token();
+ }
+vacuous=true;cur_val=0;@/
+@<Accumulate the constant until |cur_tok| is not a suitable digit@>;
+if (vacuous) @<Express astonishment that no number was here@>@;
+else if (cur_cmd!=spacer) back_input();
+}
+
+@ @d infinity 017777777777 /*the largest positive value that \TeX\ knows*/
+@d zero_token (other_token+'0') /*zero, the smallest digit*/
+@d A_token (letter_token+'A') /*the smallest special hex digit*/
+@d other_A_token (other_token+'A') /*special hex digit of type |other_char|*/
+
+@<Accumulate the constant...@>=
+loop@+{@+if ((cur_tok < zero_token+radix)&&(cur_tok >= zero_token)&&
+ (cur_tok <= zero_token+9)) d=cur_tok-zero_token;
+ else if (radix==16)
+ if ((cur_tok <= A_token+5)&&(cur_tok >= A_token)) d=cur_tok-A_token+10;
+ else if ((cur_tok <= other_A_token+5)&&(cur_tok >= other_A_token))
+ d=cur_tok-other_A_token+10;
+ else goto done;
+ else goto done;
+ vacuous=false;
+ if ((cur_val >= m)&&((cur_val > m)||(d > 7)||(radix!=10)))
+ {@+if (OK_so_far)
+ {@+print_err("Number too big");
+@.Number too big@>
+ help2("I can only go up to 2147483647='17777777777=\"7FFFFFFF,",@/
+ "so I'm using that number instead of yours.");
+ error();cur_val=infinity;OK_so_far=false;
+ }
+ }
+ else cur_val=cur_val*radix+d;
+ get_x_token();
+ }
+done:
+
+@ @<Express astonishment...@>=
+{@+print_err("Missing number, treated as zero");
+@.Missing number...@>
+help3("A number should have been here; I inserted `0'.",@/
+ "(If you can't figure out why I needed to see a number,",@/
+ "look up `weird error' in the index to The TeXbook.)");
+@:TeXbook}{\sl The \TeX book@>
+back_error();
+}
+
+@ The |scan_dimen| routine is similar to |scan_int|, but it sets |cur_val| to
+a |scaled| value, i.e., an integral number of sp. One of its main tasks
+is therefore to interpret the abbreviations for various kinds of units and
+to convert measurements to scaled points.
+
+There are three parameters: |mu| is |true| if the finite units must be
+`\.{mu}', while |mu| is |false| if `\.{mu}' units are disallowed;
+|inf| is |true| if the infinite units `\.{fil}', `\.{fill}', `\.{filll}'
+are permitted; and |shortcut| is |true| if |cur_val| already contains
+an integer and only the units need to be considered.
+
+The order of infinity that was found in the case of infinite glue is returned
+in the global variable |cur_order|.
+
+@<Glob...@>=
+static glue_ord @!cur_order; /*order of infinity found by |scan_dimen|*/
+
+@ Constructions like `\.{-\'77 pt}' are legal dimensions, so |scan_dimen|
+may begin with |scan_int|. This explains why it is convenient to use
+|scan_int| also for the integer part of a decimal fraction.
+
+Several branches of |scan_dimen| work with |cur_val| as an integer and
+with an auxiliary fraction |f|, so that the actual quantity of interest is
+$|cur_val|+|f|/2^{16}$. At the end of the routine, this ``unpacked''
+representation is put into the single word |cur_val|, which suddenly
+switches significance from |int| to |scaled|.
+
+@d scan_normal_dimen scan_dimen(false, false, false)
+
+@p static void scan_dimen(bool @!mu, bool @!inf, bool @!shortcut)
+ /*sets |cur_val| to a dimension*/
+{@+
+bool negative; /*should the answer be negated?*/
+int @!f; /*numerator of a fraction whose denominator is $2^{16}$*/
+@<Local variables for dimension calculations@>@;
+f=0;arith_error=false;cur_order=normal;negative=false;
+if (!shortcut)
+ {@+@<Get the next non-blank non-sign...@>;
+ if ((cur_cmd >= min_internal)&&(cur_cmd <= max_internal))
+ @<Fetch an internal dimension and |goto attach_sign|, or fetch an internal
+integer@>@;
+ else{@+back_input();
+ if (cur_tok==continental_point_token) cur_tok=point_token;
+ if (cur_tok!=point_token) scan_int();
+ else{@+radix=10;cur_val=0;
+ }
+ if (cur_tok==continental_point_token) cur_tok=point_token;
+ if ((radix==10)&&(cur_tok==point_token)) @<Scan decimal fraction@>;
+ }
+ }
+if (cur_val < 0) /*in this case |f==0|*/
+ {@+negative=!negative;negate(cur_val);
+ }
+@<Scan units and set |cur_val| to $x\cdot(|cur_val|+f/2^{16})$, where there
+are |x| sp per unit; |goto attach_sign| if the units are internal@>;
+@<Scan an optional space@>;
+attach_sign: if (arith_error||(abs(cur_val) >= 010000000000))
+ @<Report that this dimension is out of range@>;
+if (negative) negate(cur_val);
+}
+
+@ @<Fetch an internal dimension and |goto attach_sign|...@>=
+if (mu)
+ {@+scan_something_internal(mu_val, false);
+ @<Coerce glue to a dimension@>;
+ if (cur_val_level==mu_val) goto attach_sign;
+ if (cur_val_level!=int_val) mu_error();
+ }
+else{@+scan_something_internal(dimen_val, false);
+ if (cur_val_level==dimen_val) goto attach_sign;
+ }
+
+@ @<Local variables for dimension calculations@>=
+int @!num, @!denom; /*conversion ratio for the scanned units*/
+int @!k, @!kk; /*number of digits in a decimal fraction*/
+pointer @!p, @!q; /*top of decimal digit stack*/
+scaled @!v; /*an internal dimension*/
+int @!save_cur_val; /*temporary storage of |cur_val|*/
+
+@ The following code is executed when |scan_something_internal| was
+called asking for |mu_val|, when we really wanted a ``mudimen'' instead
+of ``muglue.''
+
+@<Coerce glue to a dimension@>=
+if (cur_val_level >= glue_val)
+ {@+v=width(cur_val);delete_glue_ref(cur_val);cur_val=v;
+ }
+
+@ When the following code is executed, we have |cur_tok==point_token|, but this
+token has been backed up using |back_input|; we must first discard it.
+
+It turns out that a decimal point all by itself is equivalent to `\.{0.0}'.
+Let's hope people don't use that fact.
+
+@<Scan decimal fraction@>=
+{@+k=0;p=null;get_token(); /*|point_token| is being re-scanned*/
+loop@+{@+get_x_token();
+ if ((cur_tok > zero_token+9)||(cur_tok < zero_token)) goto done1;
+ if (k < 17) /*digits for |k >= 17| cannot affect the result*/
+ {@+q=get_avail();link(q)=p;info(q)=cur_tok-zero_token;
+ p=q;incr(k);
+ }
+ }
+done1: for (kk=k; kk>=1; kk--)
+ {@+dig[kk-1]=info(p);q=p;p=link(p);free_avail(q);
+ }
+f=round_decimals(k);
+if (cur_cmd!=spacer) back_input();
+}
+
+@ Now comes the harder part: At this point in the program, |cur_val| is a
+nonnegative integer and $f/2^{16}$ is a nonnegative fraction less than 1;
+we want to multiply the sum of these two quantities by the appropriate
+factor, based on the specified units, in order to produce a |scaled|
+result, and we want to do the calculation with fixed point arithmetic that
+does not overflow.
+
+@<Scan units and set |cur_val| to $x\cdot(|cur_val|+f/2^{16})$...@>=
+if (inf) @<Scan for \(f)\.{fil} units; |goto attach_fraction| if found@>;
+@<Scan for \(u)units that are internal dimensions; |goto attach_sign| with
+|cur_val| set if found@>;
+if (mu) @<Scan for \(m)\.{mu} units and |goto attach_fraction|@>;
+if (scan_keyword("true")) @<Adjust \(f)for the magnification ratio@>;
+@.true@>
+if (scan_keyword("pt")) goto attach_fraction; /*the easy case*/
+@.pt@>
+@<Scan for \(a)all other units and adjust |cur_val| and |f| accordingly; |goto
+done| in the case of scaled points@>;
+attach_fraction: if (cur_val >= 040000) arith_error=true;
+else cur_val=cur_val*unity+f;
+done:
+
+@ A specification like `\.{filllll}' or `\.{fill L L L}' will lead to two
+error messages (one for each additional keyword \.{"l"}).
+
+@<Scan for \(f)\.{fil} units...@>=
+if (scan_keyword("fil"))
+@.fil@>
+ {@+cur_order=fil;
+ while (scan_keyword("l"))
+ {@+if (cur_order==filll)
+ {@+print_err("Illegal unit of measure (");
+@.Illegal unit of measure@>
+ print("replaced by filll)");
+ help1("I dddon't go any higher than filll.");error();
+ }
+ else incr(cur_order);
+ }
+ goto attach_fraction;
+ }
+
+@ @<Scan for \(u)units that are internal dimensions...@>=
+save_cur_val=cur_val;
+@<Get the next non-blank non-call...@>;
+if ((cur_cmd < min_internal)||(cur_cmd > max_internal)) back_input();
+else{@+if (mu)
+ {@+scan_something_internal(mu_val, false);@<Coerce glue...@>;
+ if (cur_val_level!=mu_val) mu_error();
+ }
+ else scan_something_internal(dimen_val, false);
+ v=cur_val;goto found;
+ }
+if (mu) goto not_found;
+if (scan_keyword("em")) v=(@<The em width for |cur_font|@>);
+@.em@>
+else if (scan_keyword("ex")) v=(@<The x-height for |cur_font|@>);
+@.ex@>
+else goto not_found;
+@<Scan an optional space@>;
+found: cur_val=nx_plus_y(save_cur_val, v, xn_over_d(v, f, 0200000));
+goto attach_sign;
+not_found:
+
+@ @<Scan for \(m)\.{mu} units and |goto attach_fraction|@>=
+if (scan_keyword("mu")) goto attach_fraction;
+@.mu@>
+else{@+print_err("Illegal unit of measure (");print("mu inserted)");
+@.Illegal unit of measure@>
+ help4("The unit of measurement in math glue must be mu.",@/
+ "To recover gracefully from this error, it's best to",@/
+ "delete the erroneous units; e.g., type `2' to delete",@/
+ "two letters. (See Chapter 27 of The TeXbook.)");
+@:TeXbook}{\sl The \TeX book@>
+ error();goto attach_fraction;
+ }
+
+@ @<Adjust \(f)for the magnification ratio@>=
+{@+prepare_mag();
+if (mag!=1000)
+ {@+cur_val=xn_over_d(cur_val, 1000, mag);
+ f=(1000*f+0200000*rem)/mag;
+ cur_val=cur_val+(f/0200000);f=f%0200000;
+ }
+}
+
+@ The necessary conversion factors can all be specified exactly as
+fractions whose numerator and denominator sum to 32768 or less.
+According to the definitions here, $\rm2660\,dd\approx1000.33297\,mm$;
+this agrees well with the value $\rm1000.333\,mm$ cited by Bosshard
+@^Bosshard, Hans Rudolf@>
+in {\sl Technische Grundlagen zur Satzherstellung\/} (Bern, 1980).
+
+@d set_conversion(A, B) @+{@+num=A;denom=B;}
+
+@<Scan for \(a)all other units and adjust |cur_val| and |f|...@>=
+if (scan_keyword("in")) set_conversion(7227, 100)@;
+@.in@>
+else if (scan_keyword("pc")) set_conversion(12, 1)@;
+@.pc@>
+else if (scan_keyword("cm")) set_conversion(7227, 254)@;
+@.cm@>
+else if (scan_keyword("mm")) set_conversion(7227, 2540)@;
+@.mm@>
+else if (scan_keyword("bp")) set_conversion(7227, 7200)@;
+@.bp@>
+else if (scan_keyword("dd")) set_conversion(1238, 1157)@;
+@.dd@>
+else if (scan_keyword("cc")) set_conversion(14856, 1157)@;
+@.cc@>
+else if (scan_keyword("sp")) goto done;
+@.sp@>
+else@<Complain about unknown unit and |goto done2|@>;
+cur_val=xn_over_d(cur_val, num, denom);
+f=(num*f+0200000*rem)/denom;@/
+cur_val=cur_val+(f/0200000);f=f%0200000;
+done2:
+
+@ @<Complain about unknown unit...@>=
+{@+print_err("Illegal unit of measure (");print("pt inserted)");
+@.Illegal unit of measure@>
+help6("Dimensions can be in units of em, ex, in, pt, pc,",@/
+ "cm, mm, dd, cc, bp, or sp; but yours is a new one!",@/
+ "I'll assume that you meant to say pt, for printer's points.",@/
+ "To recover gracefully from this error, it's best to",@/
+ "delete the erroneous units; e.g., type `2' to delete",@/
+ "two letters. (See Chapter 27 of The TeXbook.)");
+@:TeXbook}{\sl The \TeX book@>
+error();goto done2;
+}
+
+
+@ @<Report that this dimension is out of range@>=
+{@+print_err("Dimension too large");
+@.Dimension too large@>
+help2("I can't work with sizes bigger than about 19 feet.",@/
+ "Continue and I'll use the largest value I can.");@/
+error();cur_val=max_dimen;arith_error=false;
+}
+
+@ The final member of \TeX's value-scanning trio is |scan_glue|, which
+makes |cur_val| point to a glue specification. The reference count of that
+glue spec will take account of the fact that |cur_val| is pointing to~it.
+
+The |level| parameter should be either |glue_val| or |mu_val|.
+
+Since |scan_dimen| was so much more complex than |scan_int|, we might expect
+|scan_glue| to be even worse. But fortunately, it is very simple, since
+most of the work has already been done.
+
+@p static void scan_glue(small_number @!level)
+ /*sets |cur_val| to a glue spec pointer*/
+{@+
+bool negative; /*should the answer be negated?*/
+pointer @!q; /*new glue specification*/
+bool @!mu; /*does |level==mu_val|?*/
+mu=(level==mu_val);@<Get the next non-blank non-sign...@>;
+if ((cur_cmd >= min_internal)&&(cur_cmd <= max_internal))
+ {@+scan_something_internal(level, negative);
+ if (cur_val_level >= glue_val)
+ {@+if (cur_val_level!=level) mu_error();
+ return;
+ }
+ if (cur_val_level==int_val) scan_dimen(mu, false, true);
+ else if (level==mu_val) mu_error();
+ }
+else{@+back_input();scan_dimen(mu, false, false);
+ if (negative) negate(cur_val);
+ }
+@<Create a new glue specification whose width is |cur_val|; scan for its stretch
+and shrink components@>;
+}
+@#
+@<Declare procedures needed for expressions@>@;
+
+@ @<Create a new glue specification whose width is |cur_val|...@>=
+q=new_spec(zero_glue);width(q)=cur_val;
+if (scan_keyword("plus"))
+@.plus@>
+ {@+scan_dimen(mu, true, false);
+ stretch(q)=cur_val;stretch_order(q)=cur_order;
+ }
+if (scan_keyword("minus"))
+@.minus@>
+ {@+scan_dimen(mu, true, false);
+ shrink(q)=cur_val;shrink_order(q)=cur_order;
+ }
+cur_val=q
+
+@ Here's a similar procedure that returns a pointer to a rule node. This
+routine is called just after \TeX\ has seen \.{\\hrule} or \.{\\vrule};
+therefore |cur_cmd| will be either |hrule| or |vrule|. The idea is to store
+the default rule dimensions in the node, then to override them if
+`\.{height}' or `\.{width}' or `\.{depth}' specifications are
+found (in any order).
+
+@d default_rule 26214 /*0.4\thinspace pt*/
+
+@p static pointer scan_rule_spec(void)
+{@+
+pointer q; /*the rule node being created*/
+q=new_rule(); /*|width|, |depth|, and |height| all equal |null_flag| now*/
+if (cur_cmd==vrule) width(q)=default_rule;
+else{@+height(q)=default_rule;depth(q)=0;
+ }
+reswitch: if (scan_keyword("width"))
+@.width@>
+ {@+scan_normal_dimen;width(q)=cur_val;goto reswitch;
+ }
+if (scan_keyword("height"))
+@.height@>
+ {@+scan_normal_dimen;height(q)=cur_val;goto reswitch;
+ }
+if (scan_keyword("depth"))
+@.depth@>
+ {@+scan_normal_dimen;depth(q)=cur_val;goto reswitch;
+ }
+return q;
+}
+
+@* Building token lists.
+The token lists for macros and for other things like \.{\\mark} and \.{\\output}
+and \.{\\write} are produced by a procedure called |scan_toks|.
+
+Before we get into the details of |scan_toks|, let's consider a much
+simpler task, that of converting the current string into a token list.
+The |str_toks| function does this; it classifies spaces as type |spacer|
+and everything else as type |other_char|.
+
+The token list created by |str_toks| begins at |link(temp_head)| and ends
+at the value |p| that is returned. (If |p==temp_head|, the list is empty.)
+
+@p @t\4@>@<Declare \eTeX\ procedures for token lists@>@;@/
+static pointer str_toks(pool_pointer @!b)
+ /*converts |str_pool[b dotdot pool_ptr-1]| to a token list*/
+{@+pointer p; /*tail of the token list*/
+pointer @!q; /*new node being added to the token list via |store_new_token|*/
+halfword @!t; /*token being appended*/
+pool_pointer @!k; /*index into |str_pool|*/
+str_room(1);
+p=temp_head;link(p)=null;k=b;
+while (k < pool_ptr)
+ {@+t=so(str_pool[k]);
+ if (t==' ') t=space_token;
+ else t=other_token+t;
+ fast_store_new_token(t);
+ incr(k);
+ }
+pool_ptr=b;return p;
+}
+
+@ The main reason for wanting |str_toks| is the next function,
+|the_toks|, which has similar input/output characteristics.
+
+This procedure is supposed to scan something like `\.{\\skip\\count12}',
+i.e., whatever can follow `\.{\\the}', and it constructs a token list
+containing something like `\.{-3.0pt minus 0.5fill}'.
+
+@p static pointer the_toks(void)
+{@+
+int old_setting; /*holds |selector| setting*/
+pointer @!p, @!q, @!r; /*used for copying a token list*/
+pool_pointer @!b; /*base of temporary string*/
+small_number @!c; /*value of |cur_chr|*/
+@<Handle \.{\\unexpanded} or \.{\\detokenize} and |return|@>;@/
+get_x_token();scan_something_internal(tok_val, false);
+if (cur_val_level >= ident_val) @<Copy the token list@>@;
+else{@+old_setting=selector;selector=new_string;b=pool_ptr;
+ switch (cur_val_level) {
+ case int_val: print_int(cur_val);@+break;
+ case dimen_val: {@+print_scaled(cur_val);print("pt");
+ } @+break;
+ case glue_val: {@+print_spec(cur_val,"pt");delete_glue_ref(cur_val);
+ } @+break;
+ case mu_val: {@+print_spec(cur_val,"mu");delete_glue_ref(cur_val);
+ }
+ } /*there are no other cases*/
+ selector=old_setting;return str_toks(b);
+ }
+}
+
+@ @<Copy the token list@>=
+{@+p=temp_head;link(p)=null;
+if (cur_val_level==ident_val) store_new_token(cs_token_flag+cur_val)@;
+else if (cur_val!=null)
+ {@+r=link(cur_val); /*do not copy the reference count*/
+ while (r!=null)
+ {@+fast_store_new_token(info(r));r=link(r);
+ }
+ }
+return p;
+}
+
+@ Here's part of the |expand| subroutine that we are now ready to complete:
+
+@p static void ins_the_toks(void)
+{@+link(garbage)=the_toks();ins_list(link(temp_head));
+}
+
+@ The primitives \.{\\number}, \.{\\romannumeral}, \.{\\string}, \.{\\meaning},
+\.{\\fontname}, and \.{\\jobname} are defined as follows.
+
+@d number_code 0 /*command code for \.{\\number}*/
+@d roman_numeral_code 1 /*command code for \.{\\romannumeral}*/
+@d string_code 2 /*command code for \.{\\string}*/
+@d meaning_code 3 /*command code for \.{\\meaning}*/
+@d font_name_code 4 /*command code for \.{\\fontname}*/
+@d etex_convert_base 5 /*base for \eTeX's command codes*/
+@d eTeX_revision_code etex_convert_base /*command code for \.{\\eTeXrevision}*/
+@d etex_convert_codes (etex_convert_base+1) /*end of \eTeX's command codes*/
+@d eTeX_last_convert_cmd_mod etex_convert_codes /* prote codes follow */
+
+
+@#
+@d job_name_code pdftex_convert_codes /*command code for \.{\\jobname}*/
+
+@<Put each...@>=
+primitive("number", convert, number_code);@/
+@!@:number\_}{\.{\\number} primitive@>
+primitive("romannumeral", convert, roman_numeral_code);@/
+@!@:roman\_numeral\_}{\.{\\romannumeral} primitive@>
+primitive("string", convert, string_code);@/
+@!@:string\_}{\.{\\string} primitive@>
+primitive("meaning", convert, meaning_code);@/
+@!@:meaning\_}{\.{\\meaning} primitive@>
+primitive("fontname", convert, font_name_code);@/
+@!@:font\_name\_}{\.{\\fontname} primitive@>
+primitive("jobname", convert, job_name_code);@/
+@!@:job\_name\_}{\.{\\jobname} primitive@>
+
+@ @<Cases of |print_cmd_chr|...@>=
+case convert: switch (chr_code) {
+ case number_code: print_esc("number");@+break;
+ case roman_numeral_code: print_esc("romannumeral");@+break;
+ case string_code: print_esc("string");@+break;
+ case meaning_code: print_esc("meaning");@+break;
+ case font_name_code: print_esc("fontname");@+break;
+ case job_name_code: print_esc("jobname");@+break;
+ case eTeX_revision_code: print_esc("eTeXrevision");@+break;
+ @/@<Cases of |convert| for |print_cmd_chr|@>@/
+ } @+break;
+
+@ The procedure |conv_toks| uses |str_toks| to insert the token list
+for |convert| functions into the scanner; `\.{\\outer}' control sequences
+are allowed to follow `\.{\\string}' and `\.{\\meaning}'.
+
+@p static void scan_pdf_ext_toks(void);
+static void conv_toks(void)
+{@+int old_setting; /*holds |selector| setting*/
+int @!c; /*desired type of conversion*/
+small_number @!save_scanner_status; /*|scanner_status| upon entry*/
+pool_pointer @!b; /*base of temporary string*/
+int @!i, @!k, @!l; /*general purpose index*/
+pool_pointer @!m, @!n; /*general purpose pool pointer*/
+bool @!r; /*general purpose refraction i.e. changing the way*/
+str_number @!s, @!t; /*general purpose; de dicto*/
+c=cur_chr;@<Scan the argument for command |c|@>;
+old_setting=selector;selector=new_string;b=pool_ptr;
+@<Print the result of command |c|@>;
+selector=old_setting;link(garbage)=str_toks(b);ins_list(link(temp_head));
+}
+
+@ @<Scan the argument for command |c|@>=
+switch (c) {
+case number_code: case roman_numeral_code: scan_int();@+break;
+case string_code: case meaning_code: {@+save_scanner_status=scanner_status;
+ scanner_status=normal;get_token();scanner_status=save_scanner_status;
+ } @+break;
+case font_name_code: scan_font_ident();@+break;
+case job_name_code: if (job_name==0) open_log_file();@+break;
+case eTeX_revision_code: do_nothing;@+break;
+@/@<Cases of `Scan the argument for command |c|'@>@/
+} /*there are no other cases*/
+
+@ @<Print the result of command |c|@>=
+switch (c) {
+case number_code: print_int(cur_val);@+break;
+case roman_numeral_code: print_roman_int(cur_val);@+break;
+case string_code: if (cur_cs!=0) sprint_cs(cur_cs);
+ else print_char(cur_chr);@+break;
+case meaning_code: print_meaning();@+break;
+case font_name_code: {@+printn(font_name[cur_val]);
+ if (font_size[cur_val]!=font_dsize[cur_val])
+ {@+print(" at ");print_scaled(font_size[cur_val]);
+ print("pt");
+ }
+ } @+break;
+case eTeX_revision_code: print(eTeX_revision);@+break;
+case job_name_code: printn(job_name);@+break;
+@/@<Cases of `Print the result of command |c|'@>@/
+} /*there are no other cases*/
+
+@ Now we can't postpone the difficulties any longer; we must bravely tackle
+|scan_toks|. This function returns a pointer to the tail of a new token
+list, and it also makes |def_ref| point to the reference count at the
+head of that list.
+
+There are two boolean parameters, |macro_def| and |xpand|. If |macro_def|
+is true, the goal is to create the token list for a macro definition;
+otherwise the goal is to create the token list for some other \TeX\
+primitive: \.{\\mark}, \.{\\output}, \.{\\everypar}, \.{\\lowercase},
+\.{\\uppercase}, \.{\\message}, \.{\\errmessage}, \.{\\write}, or
+\.{\\special}. In the latter cases a left brace must be scanned next; this
+left brace will not be part of the token list, nor will the matching right
+brace that comes at the end. If |xpand| is false, the token list will
+simply be copied from the input using |get_token|. Otherwise all expandable
+tokens will be expanded until unexpandable tokens are left, except that
+the results of expanding `\.{\\the}' are not expanded further.
+If both |macro_def| and |xpand| are true, the expansion applies
+only to the macro body (i.e., to the material following the first
+|left_brace| character).
+
+The value of |cur_cs| when |scan_toks| begins should be the |eqtb|
+address of the control sequence to display in ``runaway'' error
+messages.
+
+@p static pointer scan_toks(bool @!macro_def, bool @!xpand)
+{@+
+halfword t; /*token representing the highest parameter number*/
+halfword @!s; /*saved token*/
+pointer @!p; /*tail of the token list being built*/
+pointer @!q; /*new node being added to the token list via |store_new_token|*/
+halfword @!unbalance; /*number of unmatched left braces*/
+halfword @!hash_brace; /*possible `\.{\#\{}' token*/
+if (macro_def) scanner_status=defining;
+@+else scanner_status=absorbing;
+warning_index=cur_cs;def_ref=get_avail();token_ref_count(def_ref)=null;
+p=def_ref;hash_brace=0;t=zero_token;
+if (macro_def) @<Scan and build the parameter part of the macro definition@>@;
+else scan_left_brace(); /*remove the compulsory left brace*/
+@<Scan and build the body of the token list; |goto found| when finished@>;
+found: scanner_status=normal;
+if (hash_brace!=0) store_new_token(hash_brace);
+return p;
+}
+@t\4@>@<Declare \Prote\ procedures for token lists@>@;@/
+
+@ @<Scan and build the parameter part...@>=
+{@+loop{@+resume: get_token(); /*set |cur_cmd|, |cur_chr|, |cur_tok|*/
+ if (cur_tok < right_brace_limit) goto done1;
+ if (cur_cmd==mac_param)
+ @<If the next character is a parameter number, make |cur_tok| a |match|
+token; but if it is a left brace, store `|left_brace|, |end_match|', set |hash_brace|,
+and |goto done|@>;
+ store_new_token(cur_tok);
+ }
+done1: store_new_token(end_match_token);
+if (cur_cmd==right_brace)
+ @<Express shock at the missing left brace; |goto found|@>;
+done: ;}
+
+@ @<Express shock...@>=
+{@+print_err("Missing { inserted");incr(align_state);
+@.Missing \{ inserted@>
+help2("Where was the left brace? You said something like `\\def\\a}',",@/
+ "which I'm going to interpret as `\\def\\a{}'.");error();goto found;
+}
+
+@ @<If the next character is a parameter number...@>=
+{@+s=match_token+cur_chr;get_token();
+if (cur_tok < left_brace_limit)
+ {@+hash_brace=cur_tok;
+ store_new_token(cur_tok);store_new_token(end_match_token);
+ goto done;
+ }
+if (t==zero_token+9)
+ {@+print_err("You already have nine parameters");
+@.You already have nine...@>
+ help2("I'm going to ignore the # sign you just used,",@/
+ "as well as the token that followed it.");error();goto resume;
+ }
+else{@+incr(t);
+ if (cur_tok!=t)
+ {@+print_err("Parameters must be numbered consecutively");
+@.Parameters...consecutively@>
+ help2("I've inserted the digit you should have used after the #.",@/
+ "Type `1' to delete what you did use.");back_error();
+ }
+ cur_tok=s;
+ }
+}
+
+@ @<Scan and build the body of the token list; |goto found| when finished@>=
+unbalance=1;
+loop@+{@+if (xpand) @<Expand the next part of the input@>@;
+ else get_token();
+ if (cur_tok < right_brace_limit)
+ if (cur_cmd < right_brace) incr(unbalance);
+ else{@+decr(unbalance);
+ if (unbalance==0) goto found;
+ }
+ else if (cur_cmd==mac_param)
+ if (macro_def) @<Look for parameter number or \.{\#\#}@>;
+ store_new_token(cur_tok);
+ }
+
+@ Here we insert an entire token list created by |the_toks| without
+expanding it further.
+
+@<Expand the next part of the input@>=
+{@+loop{@+get_next();
+ if (cur_cmd >= call)
+ if (info(link(cur_chr))==protected_token)
+ {@+cur_cmd=relax;cur_chr=no_expand_flag;
+ }
+ if (cur_cmd <= max_command) goto done2;
+ if (cur_cmd!=the) expand();
+ else{@+q=the_toks();
+ if (link(temp_head)!=null)
+ {@+link(p)=link(temp_head);p=q;
+ }
+ }
+ }
+done2: x_token();
+}
+
+@ @<Look for parameter number...@>=
+{@+s=cur_tok;
+if (xpand) get_x_token();else get_token();
+if (cur_cmd!=mac_param)
+ if ((cur_tok <= zero_token)||(cur_tok > t))
+ {@+print_err("Illegal parameter number in definition of ");
+@.Illegal parameter number...@>
+ sprint_cs(warning_index);
+ help3("You meant to type ## instead of #, right?",@/
+ "Or maybe a } was forgotten somewhere earlier, and things",@/
+ "are all screwed up? I'm going to assume that you meant ##.");
+ back_error();cur_tok=s;
+ }
+ else cur_tok=out_param_token-'0'+cur_chr;
+}
+
+@ Another way to create a token list is via the \.{\\read} command. The
+sixteen files potentially usable for reading appear in the following
+global variables. The value of |read_open[n]| will be |closed| if
+stream number |n| has not been opened or if it has been fully read;
+|just_open| if an \.{\\openin} but not a \.{\\read} has been done;
+and |normal| if it is open and ready to read the next line.
+
+@d closed 2 /*not open, or at end of file*/
+@d just_open 1 /*newly opened, first line not yet read*/
+
+@<Glob...@>=
+static alpha_file @!read_file[16]; /*used for \.{\\read}*/
+static int8_t @!read_open[17]; /*state of |read_file[n]|*/
+
+@ @<Set init...@>=
+for (k=0; k<=16; k++) read_open[k]=closed;
+
+@ The |read_toks| procedure constructs a token list like that for any
+macro definition, and makes |cur_val| point to it. Parameter |r| points
+to the control sequence that will receive this token list.
+
+@p static void read_toks(int @!n, pointer @!r, halfword @!j)
+{@+
+pointer p; /*tail of the token list*/
+pointer @!q; /*new node being added to the token list via |store_new_token|*/
+int @!s; /*saved value of |align_state|*/
+small_number @!m; /*stream number*/
+scanner_status=defining;warning_index=r;
+def_ref=get_avail();token_ref_count(def_ref)=null;
+p=def_ref; /*the reference count*/
+store_new_token(end_match_token);
+if ((n < 0)||(n > 15)) m=16;@+else m=n;
+s=align_state;align_state=1000000; /*disable tab marks, etc.*/
+@/do@+{@<Input and store tokens from the next line of the file@>;
+}@+ while (!(align_state==1000000));
+cur_val=def_ref;scanner_status=normal;align_state=s;
+}
+
+@ @<Input and store tokens from the next line of the file@>=
+begin_file_reading();name=m+1;
+if (read_open[m]==closed) @<Input for \.{\\read} from the terminal@>;
+else if (read_open[m]==just_open) @<Input the first line of |read_file[m]|@>@;
+else@<Input the next line of |read_file[m]|@>;
+limit=last;
+if (end_line_char_inactive) decr(limit);
+else buffer[limit]=end_line_char;
+first=limit+1;loc=start;state=new_line;@/
+@<Handle \.{\\readline} and |goto done|@>;@/
+loop@+{@+get_token();
+ if (cur_tok==0) goto done;
+ /*|cur_cmd==cur_chr==0| will occur at the end of the line*/
+ if (align_state < 1000000) /*unmatched `\.\}' aborts the line*/
+ {@+@/do@+{get_token();}@+ while (!(cur_tok==0));
+ align_state=1000000;goto done;
+ }
+ store_new_token(cur_tok);
+ }
+done: end_file_reading()
+
+@ Here we input on-line into the |buffer| array, prompting the user explicitly
+if |n >= 0|. The value of |n| is set negative so that additional prompts
+will not be given in the case of multi-line input.
+
+@<Input for \.{\\read} from the terminal@>=
+if (interaction > nonstop_mode)
+ if (n < 0) prompt_input("")@;
+ else{@+wake_up_terminal;
+ print_ln();sprint_cs(r);prompt_input("=");n=-1;
+ }
+else fatal_error("*** (cannot \\read from terminal in nonstop modes)")
+@.cannot \\read@>
+
+@ The first line of a file must be treated specially, since |input_ln|
+must be told not to start with |get|.
+@^system dependencies@>
+
+@<Input the first line of |read_file[m]|@>=
+if (input_ln(&read_file[m], false)) read_open[m]=normal;
+else{@+a_close(&read_file[m]);read_open[m]=closed;
+ }
+
+@ An empty line is appended at the end of a |read_file|.
+@^empty line at end of file@>
+
+@<Input the next line of |read_file[m]|@>=
+{@+if (!input_ln(&read_file[m], true))
+ {@+a_close(&read_file[m]);read_open[m]=closed;
+ if (align_state!=1000000)
+ {@+runaway();
+ print_err("File ended within ");print_esc("read");
+@.File ended within \\read@>
+ help1("This \\read has unbalanced braces.");
+ align_state=1000000;limit=0;error();
+ }
+ }
+}
+
+@* Conditional processing.
+We consider now the way \TeX\ handles various kinds of \.{\\if} commands.
+
+@d unless_code 32 /*amount added for `\.{\\unless}' prefix*/
+@#
+@d if_char_code 0 /* `\.{\\if}' */
+@d if_cat_code 1 /* `\.{\\ifcat}' */
+@d if_int_code 2 /* `\.{\\ifnum}' */
+@d if_dim_code 3 /* `\.{\\ifdim}' */
+@d if_odd_code 4 /* `\.{\\ifodd}' */
+@d if_vmode_code 5 /* `\.{\\ifvmode}' */
+@d if_hmode_code 6 /* `\.{\\ifhmode}' */
+@d if_mmode_code 7 /* `\.{\\ifmmode}' */
+@d if_inner_code 8 /* `\.{\\ifinner}' */
+@d if_void_code 9 /* `\.{\\ifvoid}' */
+@d if_hbox_code 10 /* `\.{\\ifhbox}' */
+@d if_vbox_code 11 /* `\.{\\ifvbox}' */
+@d ifx_code 12 /* `\.{\\ifx}' */
+@d if_eof_code 13 /* `\.{\\ifeof}' */
+@d if_true_code 14 /* `\.{\\iftrue}' */
+@d if_false_code 15 /* `\.{\\iffalse}' */
+@d if_case_code 16 /* `\.{\\ifcase}' */
+
+@<Put each...@>=
+primitive("if", if_test, if_char_code);
+@!@:if\_char\_}{\.{\\if} primitive@>
+primitive("ifcat", if_test, if_cat_code);
+@!@:if\_cat\_code\_}{\.{\\ifcat} primitive@>
+primitive("ifnum", if_test, if_int_code);
+@!@:if\_int\_}{\.{\\ifnum} primitive@>
+primitive("ifdim", if_test, if_dim_code);
+@!@:if\_dim\_}{\.{\\ifdim} primitive@>
+primitive("ifodd", if_test, if_odd_code);
+@!@:if\_odd\_}{\.{\\ifodd} primitive@>
+primitive("ifvmode", if_test, if_vmode_code);
+@!@:if\_vmode\_}{\.{\\ifvmode} primitive@>
+primitive("ifhmode", if_test, if_hmode_code);
+@!@:if\_hmode\_}{\.{\\ifhmode} primitive@>
+primitive("ifmmode", if_test, if_mmode_code);
+@!@:if\_mmode\_}{\.{\\ifmmode} primitive@>
+primitive("ifinner", if_test, if_inner_code);
+@!@:if\_inner\_}{\.{\\ifinner} primitive@>
+primitive("ifvoid", if_test, if_void_code);
+@!@:if\_void\_}{\.{\\ifvoid} primitive@>
+primitive("ifhbox", if_test, if_hbox_code);
+@!@:if\_hbox\_}{\.{\\ifhbox} primitive@>
+primitive("ifvbox", if_test, if_vbox_code);
+@!@:if\_vbox\_}{\.{\\ifvbox} primitive@>
+primitive("ifx", if_test, ifx_code);
+@!@:ifx\_}{\.{\\ifx} primitive@>
+primitive("ifeof", if_test, if_eof_code);
+@!@:if\_eof\_}{\.{\\ifeof} primitive@>
+primitive("iftrue", if_test, if_true_code);
+@!@:if\_true\_}{\.{\\iftrue} primitive@>
+primitive("iffalse", if_test, if_false_code);
+@!@:if\_false\_}{\.{\\iffalse} primitive@>
+primitive("ifcase", if_test, if_case_code);
+@!@:if\_case\_}{\.{\\ifcase} primitive@>
+
+@ @<Cases of |print_cmd_chr|...@>=
+case if_test: {@+if (chr_code >= unless_code) print_esc("unless");
+switch (chr_code%unless_code) {
+ case if_cat_code: print_esc("ifcat");@+break;
+ case if_int_code: print_esc("ifnum");@+break;
+ case if_dim_code: print_esc("ifdim");@+break;
+ case if_odd_code: print_esc("ifodd");@+break;
+ case if_vmode_code: print_esc("ifvmode");@+break;
+ case if_hmode_code: print_esc("ifhmode");@+break;
+ case if_mmode_code: print_esc("ifmmode");@+break;
+ case if_inner_code: print_esc("ifinner");@+break;
+ case if_void_code: print_esc("ifvoid");@+break;
+ case if_hbox_code: print_esc("ifhbox");@+break;
+ case if_vbox_code: print_esc("ifvbox");@+break;
+ case ifx_code: print_esc("ifx");@+break;
+ case if_eof_code: print_esc("ifeof");@+break;
+ case if_true_code: print_esc("iftrue");@+break;
+ case if_false_code: print_esc("iffalse");@+break;
+ case if_case_code: print_esc("ifcase");@+break;
+ @/@<Cases of |if_test| for |print_cmd_chr|@>@/
+ default:print_esc("if");
+ }
+} @+break;
+
+@ Conditions can be inside conditions, and this nesting has a stack
+that is independent of the |save_stack|.
+
+Four global variables represent the top of the condition stack:
+|cond_ptr| points to pushed-down entries, if any; |if_limit| specifies
+the largest code of a |fi_or_else| command that is syntactically legal;
+|cur_if| is the name of the current type of conditional; and |if_line|
+is the line number at which it began.
+
+If no conditions are currently in progress, the condition stack has the
+special state |cond_ptr==null|, |if_limit==normal|, |cur_if==0|, |if_line==0|.
+Otherwise |cond_ptr| points to a two-word node; the |type|, |subtype|, and
+|link| fields of the first word contain |if_limit|, |cur_if|, and
+|cond_ptr| at the next level, and the second word contains the
+corresponding |if_line|.
+
+@d if_node_size 2 /*number of words in stack entry for conditionals*/
+@d if_line_field(A) mem[A+1].i
+@d if_code 1 /*code for \.{\\if...} being evaluated*/
+@d fi_code 2 /*code for \.{\\fi}*/
+@d else_code 3 /*code for \.{\\else}*/
+@d or_code 4 /*code for \.{\\or}*/
+
+@<Glob...@>=
+static pointer @!cond_ptr; /*top of the condition stack*/
+static int @!if_limit; /*upper bound on |fi_or_else| codes*/
+static small_number @!cur_if; /*type of conditional being worked on*/
+static int @!if_line; /*line where that conditional began*/
+
+@ @<Set init...@>=
+cond_ptr=null;if_limit=normal;cur_if=0;if_line=0;
+
+@ @<Put each...@>=
+primitive("fi", fi_or_else, fi_code);
+@!@:fi\_}{\.{\\fi} primitive@>
+text(frozen_fi)=text(cur_val);eqtb[frozen_fi]=eqtb[cur_val];
+primitive("or", fi_or_else, or_code);
+@!@:or\_}{\.{\\or} primitive@>
+primitive("else", fi_or_else, else_code);
+@!@:else\_}{\.{\\else} primitive@>
+
+@ @<Cases of |print_cmd_chr|...@>=
+case fi_or_else: if (chr_code==fi_code) print_esc("fi");
+ else if (chr_code==or_code) print_esc("or");
+ else print_esc("else");@+break;
+
+@ When we skip conditional text, we keep track of the line number
+where skipping began, for use in error messages.
+
+@<Glob...@>=
+static int @!skip_line; /*skipping began here*/
+
+@ Here is a procedure that ignores text until coming to an \.{\\or},
+\.{\\else}, or \.{\\fi} at the current level of $\.{\\if}\ldots\.{\\fi}$
+nesting. After it has acted, |cur_chr| will indicate the token that
+was found, but |cur_tok| will not be set (because this makes the
+procedure run faster).
+
+@p static void pass_text(void)
+{@+
+int l; /*level of $\.{\\if}\ldots\.{\\fi}$ nesting*/
+small_number @!save_scanner_status; /*|scanner_status| upon entry*/
+save_scanner_status=scanner_status;scanner_status=skipping;l=0;
+skip_line=line;
+loop@+{@+get_next();
+ if (cur_cmd==fi_or_else)
+ {@+if (l==0) goto done;
+ if (cur_chr==fi_code) decr(l);
+ }
+ else if (cur_cmd==if_test) incr(l);
+ }
+done: scanner_status=save_scanner_status;
+if (tracing_ifs > 0) show_cur_cmd_chr();
+}
+
+@ When we begin to process a new \.{\\if}, we set |if_limit=if_code|; then
+if\/ \.{\\or} or \.{\\else} or \.{\\fi} occurs before the current \.{\\if}
+condition has been evaluated, \.{\\relax} will be inserted.
+For example, a sequence of commands like `\.{\\ifvoid1\\else...\\fi}'
+would otherwise require something after the `\.1'.
+
+@<Push the condition stack@>=
+{@+p=get_node(if_node_size);link(p)=cond_ptr;type(p)=if_limit;
+subtype(p)=cur_if;if_line_field(p)=if_line;
+cond_ptr=p;cur_if=cur_chr;if_limit=if_code;if_line=line;
+}
+
+@ @<Pop the condition stack@>=
+{@+if (if_stack[in_open]==cond_ptr) if_warning();
+ /*conditionals possibly not properly nested with files*/
+p=cond_ptr;if_line=if_line_field(p);
+cur_if=subtype(p);if_limit=type(p);cond_ptr=link(p);
+free_node(p, if_node_size);
+}
+
+@ Here's a procedure that changes the |if_limit| code corresponding to
+a given value of |cond_ptr|.
+
+@p static void change_if_limit(small_number @!l, pointer @!p)
+{@+
+pointer q;
+if (p==cond_ptr) if_limit=l; /*that's the easy case*/
+else{@+q=cond_ptr;
+ loop@+{@+if (q==null) confusion("if");
+@:this can't happen if}{\quad if@>
+ if (link(q)==p)
+ {@+type(q)=l;return;
+ }
+ q=link(q);
+ }
+ }
+}
+
+@ A condition is started when the |expand| procedure encounters
+an |if_test| command; in that case |expand| reduces to |conditional|,
+which is a recursive procedure.
+@^recursion@>
+
+@p static void conditional(void)
+{@+
+bool b; /*is the condition true?*/
+int @!r; /*relation to be evaluated*/
+int @!m, @!n; /*to be tested against the second operand*/
+pointer @!p, @!q; /*for traversing token lists in \.{\\ifx} tests*/
+small_number @!save_scanner_status; /*|scanner_status| upon entry*/
+pointer @!save_cond_ptr; /*|cond_ptr| corresponding to this conditional*/
+small_number @!this_if; /*type of this conditional*/
+bool @!is_unless; /*was this if preceded by `\.{\\unless}' ?*/
+if (tracing_ifs > 0) if (tracing_commands <= 1) show_cur_cmd_chr();
+@<Push the condition stack@>;@+save_cond_ptr=cond_ptr;
+is_unless=(cur_chr >= unless_code);this_if=cur_chr%unless_code;@/
+@<Either process \.{\\ifcase} or set |b| to the value of a boolean condition@>;
+if (is_unless) b=!b;
+if (tracing_commands > 1) @<Display the value of |b|@>;
+if (b)
+ {@+change_if_limit(else_code, save_cond_ptr);
+ return; /*wait for \.{\\else} or \.{\\fi}*/
+ }
+@<Skip to \.{\\else} or \.{\\fi}, then |goto common_ending|@>;
+common_ending: if (cur_chr==fi_code) @<Pop the condition stack@>@;
+else if_limit=fi_code; /*wait for \.{\\fi}*/
+}
+
+@ In a construction like `\.{\\if\\iftrue abc\\else d\\fi}', the first
+\.{\\else} that we come to after learning that the \.{\\if} is false is
+not the \.{\\else} we're looking for. Hence the following curious
+logic is needed.
+
+@ @<Skip to \.{\\else} or \.{\\fi}...@>=
+loop@+{@+pass_text();
+ if (cond_ptr==save_cond_ptr)
+ {@+if (cur_chr!=or_code) goto common_ending;
+ print_err("Extra ");print_esc("or");
+@.Extra \\or@>
+ help1("I'm ignoring this; it doesn't match any \\if.");
+ error();
+ }
+ else if (cur_chr==fi_code) @<Pop the condition stack@>;
+ }
+
+@ @<Either process \.{\\ifcase} or set |b|...@>=
+switch (this_if) {
+case if_char_code: case if_cat_code: @<Test if two characters match@>@;@+break;
+case if_int_code: case if_dim_code: @<Test relation between integers or dimensions@>@;@+break;
+case if_odd_code: @<Test if an integer is odd@>@;@+break;
+case if_vmode_code: b=(abs(mode)==vmode);@+break;
+case if_hmode_code: b=(abs(mode)==hmode);@+break;
+case if_mmode_code: b=(abs(mode)==mmode);@+break;
+case if_inner_code: b=(mode < 0);@+break;
+case if_void_code: case if_hbox_code:
+ case if_vbox_code: @<Test box register status@>@;@+break;
+case ifx_code: @<Test if two tokens match@>@;@+break;
+case if_eof_code: {@+scan_four_bit_int();b=(read_open[cur_val]==closed);
+ } @+break;
+case if_true_code: b=true;@+break;
+case if_false_code: b=false;@+break;
+@/@<Cases for |conditional|@>@/
+case if_case_code: @<Select the appropriate case and |return| or |goto common_ending|@>;
+} /*there are no other cases*/
+
+@ @<Display the value of |b|@>=
+{@+begin_diagnostic();
+if (b) print("{true}");@+else print("{false}");
+end_diagnostic(false);
+}
+
+@ Here we use the fact that |'<'|, |'='|, and |'>'| are consecutive ASCII
+codes.
+@^ASCII code@>
+
+@<Test relation between integers or dimensions@>=
+{@+if (this_if==if_int_code) scan_int();@+else scan_normal_dimen;
+n=cur_val;@<Get the next non-blank non-call...@>;
+if ((cur_tok >= other_token+'<')&&(cur_tok <= other_token+'>'))
+ r=cur_tok-other_token;
+else{@+print_err("Missing = inserted for ");
+@.Missing = inserted@>
+ print_cmd_chr(if_test, this_if);
+ help1("I was expecting to see `<', `=', or `>'. Didn't.");
+ back_error();r='=';
+ }
+if (this_if==if_int_code) scan_int();@+else scan_normal_dimen;
+switch (r) {
+case '<': b=(n < cur_val);@+break;
+case '=': b=(n==cur_val);@+break;
+case '>': b=(n > cur_val);
+}
+}
+
+@ @<Test if an integer is odd@>=
+{@+scan_int();b=odd(cur_val);
+}
+
+@ @<Test box register status@>=
+{@+scan_register_num();fetch_box(p);
+if (this_if==if_void_code) b=(p==null);
+else if (p==null) b=false;
+else if (this_if==if_hbox_code) b=(type(p)==hlist_node);
+else b=(type(p)==vlist_node);
+}
+
+@ An active character will be treated as category 13 following
+\.{\\if\\noexpand} or following \.{\\ifcat\\noexpand}. We use the fact that
+active characters have the smallest tokens, among all control sequences.
+
+@d get_x_token_or_active_char @t@>@;
+ {@+get_x_token();
+ if (cur_cmd==relax) if (cur_chr==no_expand_flag)
+ {@+cur_cmd=active_char;
+ cur_chr=cur_tok-cs_token_flag-active_base;
+ }
+ }
+
+@<Test if two characters match@>=
+{@+get_x_token_or_active_char;
+if ((cur_cmd > active_char)||(cur_chr > 255)) /*not a character*/
+ {@+m=relax;n=256;
+ }
+else{@+m=cur_cmd;n=cur_chr;
+ }
+get_x_token_or_active_char;
+if ((cur_cmd > active_char)||(cur_chr > 255))
+ {@+cur_cmd=relax;cur_chr=256;
+ }
+if (this_if==if_char_code) b=(n==cur_chr);@+else b=(m==cur_cmd);
+}
+
+@ Note that `\.{\\ifx}' will declare two macros different if one is \\{long}
+or \\{outer} and the other isn't, even though the texts of the macros are
+the same.
+
+We need to reset |scanner_status|, since \.{\\outer} control sequences
+are allowed, but we might be scanning a macro definition or preamble.
+
+@<Test if two tokens match@>=
+{@+save_scanner_status=scanner_status;scanner_status=normal;
+get_next();n=cur_cs;p=cur_cmd;q=cur_chr;
+get_next();if (cur_cmd!=p) b=false;
+else if (cur_cmd < call) b=(cur_chr==q);
+else@<Test if two macro texts match@>;
+scanner_status=save_scanner_status;
+}
+
+@ Note also that `\.{\\ifx}' decides that macros \.{\\a} and \.{\\b} are
+different in examples like this:
+$$\vbox{\halign{\.{#}\hfil&\qquad\.{#}\hfil\cr
+ {}\\def\\a\{\\c\}&
+ {}\\def\\c\{\}\cr
+ {}\\def\\b\{\\d\}&
+ {}\\def\\d\{\}\cr}}$$
+
+@<Test if two macro texts match@>=
+{@+p=link(cur_chr);q=link(equiv(n)); /*omit reference counts*/
+if (p==q) b=true;
+else{@+while ((p!=null)&&(q!=null))
+ if (info(p)!=info(q)) p=null;
+ else{@+p=link(p);q=link(q);
+ }
+ b=((p==null)&&(q==null));
+ }
+}
+
+@ @<Select the appropriate case and |return| or |goto common_ending|@>=
+{@+scan_int();n=cur_val; /*|n| is the number of cases to pass*/
+if (tracing_commands > 1)
+ {@+begin_diagnostic();print("{case ");print_int(n);print_char('}');
+ end_diagnostic(false);
+ }
+while (n!=0)
+ {@+pass_text();
+ if (cond_ptr==save_cond_ptr)
+ if (cur_chr==or_code) decr(n);
+ else goto common_ending;
+ else if (cur_chr==fi_code) @<Pop the condition stack@>;
+ }
+change_if_limit(or_code, save_cond_ptr);
+return; /*wait for \.{\\or}, \.{\\else}, or \.{\\fi}*/
+}
+
+@ The processing of conditionals is complete except for the following
+code, which is actually part of |expand|. It comes into play when
+\.{\\or}, \.{\\else}, or \.{\\fi} is scanned.
+
+@<Terminate the current conditional and skip to \.{\\fi}@>=
+{@+if (tracing_ifs > 0) if (tracing_commands <= 1) show_cur_cmd_chr();
+if (cur_chr > if_limit)
+ if (if_limit==if_code) insert_relax(); /*condition not yet evaluated*/
+ else{@+print_err("Extra ");print_cmd_chr(fi_or_else, cur_chr);
+@.Extra \\or@>
+@.Extra \\else@>
+@.Extra \\fi@>
+ help1("I'm ignoring this; it doesn't match any \\if.");
+ error();
+ }
+else{@+while (cur_chr!=fi_code) pass_text(); /*skip to \.{\\fi}*/
+ @<Pop the condition stack@>;
+ }
+}
+
+@* File names.
+It's time now to fret about file names. Besides the fact that different
+operating systems treat files in different ways, we must cope with the
+fact that completely different naming conventions are used by different
+groups of people. The following programs show what is required for one
+particular operating system; similar routines for other systems are not
+difficult to devise.
+@^fingers@>
+@^system dependencies@>
+
+\TeX\ assumes that a file name has three parts: the name proper; its
+``extension''; and a ``file area'' where it is found in an external file
+system. The extension of an input file or a write file is assumed to be
+`\.{.tex}' unless otherwise specified; it is `\.{.log}' on the
+transcript file that records each run of \TeX; it is `\.{.tfm}' on the font
+metric files that describe characters in the fonts \TeX\ uses; it is
+`\.{.dvi}' on the output files that specify typesetting information; and it
+is `\.{.fmt}' on the format files written by \.{INITEX} to initialize \TeX.
+The file area can be arbitrary on input files, but files are usually
+output to the user's current area. If an input file cannot be
+found on the specified area, \TeX\ will look for it on a special system
+area; this special area is intended for commonly used input files like
+\.{webmac.tex}.
+
+Simple uses of \TeX\ refer only to file names that have no explicit
+extension or area. For example, a person usually says `\.{\\input} \.{paper}'
+or `\.{\\font\\tenrm} \.= \.{helvetica}' instead of `\.{\\input}
+\.{paper.new}' or `\.{\\font\\tenrm} \.= \.{<csd.knuth>test}'. Simple file
+names are best, because they make the \TeX\ source files portable;
+whenever a file name consists entirely of letters and digits, it should be
+treated in the same way by all implementations of \TeX. However, users
+need the ability to refer to other files in their environment, especially
+when responding to error messages concerning unopenable files; therefore
+we want to let them use the syntax that appears in their favorite
+operating system.
+
+The following procedures don't allow spaces to be part of
+file names; but some users seem to like names that are spaced-out.
+System-dependent changes to allow such things should probably
+be made with reluctance, and only when an entire file name that
+includes spaces is ``quoted'' somehow.
+
+@ In order to isolate the system-dependent aspects of file names, the
+@^system dependencies@>
+system-independent parts of \TeX\ are expressed in terms
+of three system-dependent
+procedures called |begin_name|, |more_name|, and |end_name|. In
+essence, if the user-specified characters of the file name are $c_1\ldots c_n$,
+the system-independent driver program does the operations
+$$|begin_name|;\,|more_name|(c_1);\,\ldots\,;\,|more_name|(c_n);
+\,|end_name|.$$
+These three procedures communicate with each other via global variables.
+Afterwards the file name will appear in the string pool as three strings
+called |cur_name|\penalty10000\hskip-.05em,
+|cur_area|, and |cur_ext|; the latter two are null (i.e.,
+|""|), unless they were explicitly specified by the user.
+
+Actually the situation is slightly more complicated, because \TeX\ needs
+to know when the file name ends. The |more_name| routine is a function
+(with side effects) that returns |true| on the calls |more_name|$(c_1)$,
+\dots, |more_name|$(c_{n-1})$. The final call |more_name|$(c_n)$
+returns |false|; or, it returns |true| and the token following $c_n$ is
+something like `\.{\\hbox}' (i.e., not a character). In other words,
+|more_name| is supposed to return |true| unless it is sure that the
+file name has been completely scanned; and |end_name| is supposed to be able
+to finish the assembly of |cur_name|, |cur_area|, and |cur_ext| regardless of
+whether $|more_name|(c_n)$ returned |true| or |false|.
+
+@<Glob...@>=
+static str_number @!cur_name; /*name of file just scanned*/
+static str_number @!cur_area; /*file area just scanned, or \.{""}*/
+static str_number @!cur_ext; /*file extension just scanned, or \.{""}*/
+
+@ The file names we shall deal with for illustrative purposes have the
+following structure: If the name contains `\.>' or `\.:', the file area
+consists of all characters up to and including the final such character;
+otherwise the file area is null. If the remaining file name contains
+`\..', the file extension consists of all such characters from the first
+remaining `\..' to the end, otherwise the file extension is null.
+@^system dependencies@>
+
+We can scan such file names easily by using two global variables that keep track
+of the occurrences of area and extension delimiters:
+
+@<Glob...@>=
+static pool_pointer @!area_delimiter; /*the most recent `\.>' or `\.:', if any*/
+static pool_pointer @!ext_delimiter; /*the relevant `\..', if any*/
+
+@ Input files that can't be found in the user's area may appear in a standard
+system area called |TEX_area|. Font metric files whose areas are not given
+explicitly are assumed to appear in a standard system area called
+|TEX_font_area|. These system area names will, of course, vary from place
+to place.
+@^system dependencies@>
+
+@d TEX_area "TeXinputs/"
+@.TeXinputs@>
+@d TEX_font_area "TeXfonts/"
+@.TeXfonts@>
+
+@ Here now is the first of the system-dependent routines for file name scanning.
+@^system dependencies@>
+
+@p static bool quoted_filename;
+static void begin_name(void)
+{@+area_delimiter=0;ext_delimiter=0; quoted_filename=false;
+}
+
+@ And here's the second. The string pool might change as the file name is
+being scanned, since a new \.{\\csname} might be entered; therefore we keep
+|area_delimiter| and |ext_delimiter| relative to the beginning of the current
+string, instead of assigning an absolute address like |pool_ptr| to them.
+@^system dependencies@>
+
+@p static bool more_name(ASCII_code @!c)
+{@+if (c==' ' && !quoted_filename) return false;
+else if (c=='"') {@+quoted_filename=!quoted_filename; return true; }
+else {@+str_room(1);append_char(c); /*contribute |c| to the current string*/
+ if (IS_DIR_SEP(c)) {@+area_delimiter=cur_length;ext_delimiter=0; }
+ else if (c=='.') ext_delimiter=cur_length;
+ return true;
+ }
+}
+
+@ The third.
+@^system dependencies@>
+
+@p static void end_name(void)
+{@+if (str_ptr+3 > max_strings)
+ overflow("number of strings", max_strings-init_str_ptr);
+@:TeX capacity exceeded number of strings}{\quad number of strings@>
+if (area_delimiter==0) cur_area=empty_string;
+else{@+cur_area=str_ptr;
+ str_start[str_ptr+1]=str_start[str_ptr]+area_delimiter;incr(str_ptr);
+ }
+if (ext_delimiter==0)
+ {@+cur_ext=empty_string;cur_name=make_string();
+ }
+else{@+cur_name=str_ptr;
+ str_start[str_ptr+1]=str_start[str_ptr]+ext_delimiter-area_delimiter-1;
+ incr(str_ptr);cur_ext=make_string();
+ }
+}
+
+@ Conversely, here is a routine that takes three strings and prints a file
+name that might have produced them. (The routine is system dependent, because
+some operating systems put the file area last instead of first.)
+@^system dependencies@>
+
+@<Basic printing...@>=
+static void print_file_name(int @!n, int @!a, int @!e)
+{@+slow_print(a);slow_print(n);slow_print(e);
+}
+
+@ Another system-dependent routine is needed to convert three internal
+\TeX\ strings
+into the |name_of_file| value that is used to open files. The present code
+allows both lowercase and uppercase letters in the file name.
+@^system dependencies@>
+
+@d append_to_name(A) {@+c=A;incr(k);
+ if (k <= file_name_size) name_of_file[k]=xchr[c];
+ }
+
+@p static void pack_file_name(str_number @!n, str_number @!a, str_number @!e, char *@!f)
+{@+int k; /*number of positions filled in |name_of_file|*/
+ASCII_code @!c; /*character being packed*/
+int @!j; /*index into |str_pool|*/
+k=0;
+for (j=str_start[a]; j<=str_start[a+1]-1; j++) append_to_name(so(str_pool[j]))@;
+for (j=str_start[n]; j<=str_start[n+1]-1; j++) append_to_name(so(str_pool[j]))@;
+if (f==NULL)
+ for (j=str_start[e]; j<=str_start[e+1]-1; j++) append_to_name(so(str_pool[j]))@;
+else
+ while(*f!=0) append_to_name(so(*f++))@;
+if (k <= file_name_size) name_length=k;@+else name_length=file_name_size;
+name_of_file[name_length+1]=0;
+}
+
+
+
+@ \TeX\ Live does not use the global variable |TEX_format_default|. It is no longer needed
+to supply the text for default system areas
+and extensions related to format files.
+
+@ Consequently \TeX\ Live does not need the initialization of |TEX_format_default| either.
+
+@ And \TeX\ Live does not check the length of |TEX_format_default|.
+
+@ The |format_extension|, however, is needed by \TeX\ Live
+to create the format name from the job name.
+
+@d format_extension ".fmt"
+
+@ This part of the program
+becomes active when a ``virgin'' \TeX\ is trying to get going, just after
+the preliminary initialization, or when the user is substituting another
+format file by typing `\.\&' after the initial `\.{**}' prompt. The buffer
+contains the first line of input in |buffer[loc dotdot(last-1)]|, where
+|loc < last| and |buffer[loc]!=' '|.
+
+\TeX\ Live uses the {\tt kpathsearch} library to implement access to files.
+|open_fmt_file| is declared here and the
+actual implementation is in the section on \TeX\ Live Integration.
+
+@<Declare the function called |open_fmt_file|@>=
+static bool open_fmt_file(void);
+
+@ Operating systems often make it possible to determine the exact name (and
+possible version number) of a file that has been opened. The following routine,
+which simply makes a \TeX\ string from the value of |name_of_file|, should
+ideally be changed to deduce the full name of file~|f|, which is the file
+most recently opened, if it is possible to do this in a \PASCAL\ program.
+@^system dependencies@>
+
+This routine might be called after string memory has overflowed, hence
+we dare not use `|str_room|'.
+
+@p static str_number make_name_string(void)
+{@+int k; /*index into |name_of_file|*/
+if ((pool_ptr+name_length > pool_size)||(str_ptr==max_strings)||
+ (cur_length > 0))
+ return'?';
+else{@+for (k=1; k<=name_length; k++) append_char(xord[name_of_file[k]]);
+ return make_string();
+ }
+}
+static str_number a_make_name_string(alpha_file *f)
+{@+return make_name_string();
+}
+static str_number b_make_name_string(byte_file *f)
+{@+return make_name_string();
+}
+#ifdef @!INIT
+static str_number w_make_name_string(word_file *f)
+{@+return make_name_string();
+}
+#endif
+
+@ Now let's consider the ``driver''
+routines by which \TeX\ deals with file names
+in a system-independent manner. First comes a procedure that looks for a
+file name.
+There are two ways to specify the file name: as a general text argument
+or as a token (after expansion).
+The traditional token delimiter is the space. For a file name, however,
+a double quote is used as the token delimiter if the token starts
+with a double quote.
+
+Once the |area_delimiter| and the |ext_delimiter| are defined,
+the final processing is shared for all variants.
+
+When starting, \.{\\relax} is skipped as well as blanks and non-calls.
+Then a test for the |left_brace| will branch to the code for scanning
+a general text.
+% consider the case where a double quote is made an active character
+% equivalent to a left brace!
+
+@p static void scan_file_name(void)
+{@+
+pool_pointer @!j, k; /*index into |str_pool|*/
+int @!old_setting; /*holds |selector| setting*/
+name_in_progress=true;begin_name();
+@<Get the next non-blank non-relax...@>;
+if (cur_cmd==left_brace)
+ @<Define a general text file name and |goto done|@>@;
+loop@+{@+if ((cur_cmd > other_char)||(cur_chr > 255)) /*not a character*/
+ {@+back_input();goto done;
+ }
+#if 0
+ /* This is from pdftex-final.ch. I don't know these `some cases',
+ and I am not sure whether the name should end even if quoting is on.*/
+ /*If |cur_chr| is a space and we're not scanning a token list, check
+ whether we're at the end of the buffer. Otherwise we end up adding
+ spurious spaces to file names in some cases.*/
+ if (cur_chr==' ' && state!=token_list && loc>limit) goto done;
+#endif
+ if (!more_name(cur_chr)) goto done;
+ get_x_token();
+ }
+done: end_name();name_in_progress=false;
+}
+
+@ The global variable |name_in_progress| is used to prevent recursive
+use of |scan_file_name|, since the |begin_name| and other procedures
+communicate via global variables. Recursion would arise only by
+devious tricks like `\.{\\input\\input f}'; such attempts at sabotage
+must be thwarted. Furthermore, |name_in_progress| prevents \.{\\input}
+@^recursion@>
+from being initiated when a font size specification is being scanned.
+
+Another global variable, |job_name|, contains the file name that was first
+\.{\\input} by the user. This name is extended by `\.{.log}' and `\.{.dvi}'
+and `\.{.fmt}' in the names of \TeX's output files.
+
+@<Glob...@>=
+static bool @!name_in_progress; /*is a file name being scanned?*/
+static str_number @!job_name; /*principal file name*/
+static bool @!log_opened; /*has the transcript file been opened?*/
+
+@ Initially |job_name==0|; it becomes nonzero as soon as the true name is known.
+We have |job_name==0| if and only if the `\.{log}' file has not been opened,
+except of course for a short time just after |job_name| has become nonzero.
+
+@<Initialize the output...@>=
+job_name=0;name_in_progress=false;log_opened=false;
+
+@ Here is a routine that manufactures the output file names, assuming that
+|job_name!=0|. It ignores and changes the current settings of |cur_area|
+and |cur_ext|.
+
+@d pack_cur_name(A) if (cur_ext==empty_string) pack_file_name(cur_name, cur_area, cur_ext,A);
+ else pack_file_name(cur_name, cur_area, cur_ext,NULL)
+
+@p static void pack_job_name(char *@!s) /*|s==".log"|, |".dvi"|, or
+ |format_extension|*/
+{@+cur_area=empty_string;cur_ext=empty_string;
+cur_name=job_name;pack_cur_name(s);
+}
+
+@ If some trouble arises when \TeX\ tries to open a file, the following
+routine calls upon the user to supply another file name. Parameter~|s|
+is used in the error message to identify the type of file; parameter~|e|
+is the default extension if none is given.
+We handle the specification of a file name with possibly spaces in
+double quotes (the last one is optional if this is the end of line
+i.e. the end of the buffer).
+Upon exit from the routine,
+variables |cur_name|, |cur_area|, |cur_ext|, and |name_of_file| are
+ready for another attempt at file opening.
+
+@p static void prompt_file_name(char *@!s, char *@!e)
+{@+
+int k; /*index into |buffer|*/
+if (interaction==scroll_mode) wake_up_terminal;
+if (strcmp(s,"input file name")==0) print_err("I can't find file `");
+@.I can't find file x@>
+else print_err("I can't write on file `");
+@.I can't write on file x@>
+print_file_name(cur_name, cur_area, cur_ext);print("'.");
+if (strcmp(e,".tex")==0) show_context();
+print_nl("Please type another ");print(s);
+@.Please type...@>
+if (interaction < scroll_mode)
+ fatal_error("*** (job aborted, file error in nonstop mode)");
+@.job aborted, file error...@>
+clear_terminal;prompt_input(": ");@<Scan file name in the buffer@>;
+pack_cur_name(e);
+}
+
+@ @<Scan file name in the buffer@>=
+{@+
+begin_name();k=first;
+while ((buffer[k]==' ')&&(k < last)) incr(k);
+loop@+{@+if (k==last) goto done;
+ if (!more_name(buffer[k])) goto done;
+ incr(k);
+ }
+done: end_name();
+}
+
+@ Here's an example of how these conventions are used. Whenever it is time to
+ship out a box of stuff, we shall use the macro |ensure_dvi_open|.
+
+@d ensure_dvi_open if (output_file_name==0)
+ {@+if (job_name==0) open_log_file();
+ pack_job_name(".dvi");
+ while (!b_open_out(&dvi_file))
+ prompt_file_name("file name for output",".dvi");
+ output_file_name=b_make_name_string(&dvi_file);
+ }
+
+@<Glob...@>=
+static byte_file @!dvi_file; /*the device-independent output goes here*/
+static str_number @!output_file_name; /*full name of the output file*/
+static str_number @!log_name; /*full name of the log file*/
+
+@ @<Initialize the output...@>=output_file_name=0;
+
+@ The |open_log_file| routine is used to open the transcript file and to help
+it catch up to what has previously been printed on the terminal.
+
+@p static void open_log_file(void)
+{@+int old_setting; /*previous |selector| setting*/
+int @!k; /*index into |months| and |buffer|*/
+int @!l; /*end of first input line*/
+char @!months[]=" JANFEBMARAPRMAYJUNJULAUGSEPOCTNOVDEC"; /*abbreviations of month names*/
+old_setting=selector;
+if (job_name==0) job_name=s_no(c_job_name?c_job_name:"texput"); /* \TeX\ Live */
+pack_job_name(".fls");
+recorder_change_filename((char *)name_of_file+1);
+@.texput@>
+pack_job_name(".log");
+while (!a_open_out(&log_file)) @<Try to get a different log file name@>;
+log_name=a_make_name_string(&log_file);
+selector=log_only;log_opened=true;
+@<Print the banner line, including the date and time@>;
+input_stack[input_ptr]=cur_input; /*make sure bottom level is in memory*/
+print_nl("**");
+@.**@>
+l=input_stack[0].limit_field; /*last position of first line*/
+if (buffer[l]==end_line_char) decr(l);
+for (k=1; k<=l; k++) printn(buffer[k]);
+print_ln(); /*now the transcript file contains the first line of input*/
+selector=old_setting+2; /*|log_only| or |term_and_log|*/
+}
+
+@ Sometimes |open_log_file| is called at awkward moments when \TeX\ is
+unable to print error messages or even to |show_context|.
+The |prompt_file_name| routine can result in a |fatal_error|, but the |error|
+routine will not be invoked because |log_opened| will be false.
+
+The normal idea of |batch_mode| is that nothing at all should be written
+on the terminal. However, in the unusual case that
+no log file could be opened, we make an exception and allow
+an explanatory message to be seen.
+
+Incidentally, the program always refers to the log file as a `\.{transcript
+file}', because some systems cannot use the extension `\.{.log}' for
+this file.
+
+@<Try to get a different log file name@>=
+{@+selector=term_only;
+prompt_file_name("transcript file name",".log");
+}
+
+@ @<Print the banner...@>=
+{@+wlog("%s",banner);
+slow_print(format_ident);print(" ");
+print_int(sys_day);print_char(' ');
+for (k=3*sys_month-2; k<=3*sys_month; k++) wlog("%c",months[k]);
+print_char(' ');print_int(sys_year);print_char(' ');
+print_two(sys_time/60);print_char(':');print_two(sys_time%60);
+if (eTeX_ex)
+ {@+;wlog_cr;wlog("entering extended mode");
+ }
+if (Prote_ex)
+ {@+;wlog_cr;wlog("entering Prote mode");
+ }
+}
+
+@ Let's turn now to the procedure that is used to initiate file reading
+when an `\.{\\input}' command is being processed.
+Beware: For historic reasons, this code foolishly conserves a tiny bit
+of string pool space; but that can confuse the interactive `\.E' option.
+@^system dependencies@>
+
+@p static void start_input(void) /*\TeX\ will \.{\\input} something*/
+{@+
+scan_file_name(); /*set |cur_name| to desired file name*/
+pack_cur_name("");
+loop@+{@+begin_file_reading(); /*set up |cur_file| and new level of input*/
+ if (kpse_in_name_ok((char*)name_of_file+1) && a_open_in(&cur_file)) goto done;
+ end_file_reading(); /*remove the level that didn't work*/
+ prompt_file_name("input file name",".tex");
+ }
+done: name=a_make_name_string(&cur_file);@/
+if (source_filename_stack[in_open]!=NULL)
+ free(source_filename_stack[in_open]);
+source_filename_stack[in_open]=strdup((char *)name_of_file+1); /*\TeX\ Live*/
+if (full_source_filename_stack[in_open]!=NULL)
+ free(full_source_filename_stack[in_open]);
+full_source_filename_stack[in_open]=strdup(full_name_of_file);
+@<Set new |cur_file_num|@>@;
+/* new entry on the macro stack */
+{ @<additional local variables for |start_input|@>
+ @<update the macro stack@>@;
+}
+if (job_name==0)
+ {@+if (c_job_name==NULL) job_name=cur_name;
+ else job_name=s_no(c_job_name); open_log_file(); /* \TeX\ Live*/
+ } /*|open_log_file| doesn't |show_context|, so |limit|
+ and |loc| needn't be set to meaningful values yet*/
+if (term_offset+strlen(full_source_filename_stack[in_open]) > max_print_line-2)
+ print_ln();
+else if ((term_offset > 0)||(file_offset > 0)) print_char(' ');
+print_char('(');incr(open_parens);
+print(full_source_filename_stack[in_open]);update_terminal;
+state=new_line;
+if (name==str_ptr-1) /*conserve string pool space (but see note above)*/
+ {@+flush_string;name=cur_name;
+ }
+@<Read the first line of the new file@>;
+}
+
+@ Here we have to remember to tell the |input_ln| routine not to
+start with a |get|. If the file is empty, it is considered to
+contain a single blank line.
+@^system dependencies@>
+@^empty line at end of file@>
+
+@<Read the first line...@>=
+{@+line=1;
+if (input_ln(&cur_file, false)) do_nothing;
+firm_up_the_line();
+if (end_line_char_inactive) decr(limit);
+else buffer[limit]=end_line_char;
+first=limit+1;loc=start;
+}
+
+@* Font metric data.
+\TeX\ gets its knowledge about fonts from font metric files, also called
+\.{TFM} files; the `\.T' in `\.{TFM}' stands for \TeX,
+but other programs know about them too.
+@:TFM files}{\.{TFM} files@>
+@^font metric files@>
+
+The information in a \.{TFM} file appears in a sequence of 8-bit bytes.
+Since the number of bytes is always a multiple of 4, we could
+also regard the file as a sequence of 32-bit words, but \TeX\ uses the
+byte interpretation. The format of \.{TFM} files was designed by
+Lyle Ramshaw in 1980. The intent is to convey a lot of different kinds
+@^Ramshaw, Lyle Harold@>
+of information in a compact but useful form.
+
+@<Glob...@>=
+static byte_file @!tfm_file;
+
+@ The first 24 bytes (6 words) of a \.{TFM} file contain twelve 16-bit
+integers that give the lengths of the various subsequent portions
+of the file. These twelve integers are, in order:
+$$\vbox{\halign{\hfil#&$\null=\null$#\hfil\cr
+|lf|&length of the entire file, in words;\cr
+|lh|&length of the header data, in words;\cr
+|bc|&smallest character code in the font;\cr
+|ec|&largest character code in the font;\cr
+|nw|&number of words in the width table;\cr
+|nh|&number of words in the height table;\cr
+|nd|&number of words in the depth table;\cr
+|ni|&number of words in the italic correction table;\cr
+|nl|&number of words in the lig/kern table;\cr
+|nk|&number of words in the kern table;\cr
+|ne|&number of words in the extensible character table;\cr
+|np|&number of font parameter words.\cr}}$$
+They are all nonnegative and less than $2^{15}$. We must have |bc-1 <= ec <= 255|,
+and
+$$\hbox{|lf==6+lh+(ec-bc+1)+nw+nh+nd+ni+nl+nk+ne+np|.}$$
+Note that a font may contain as many as 256 characters (if |bc==0| and |ec==255|),
+and as few as 0 characters (if |bc==ec+1|).
+
+Incidentally, when two or more 8-bit bytes are combined to form an integer of
+16 or more bits, the most significant bytes appear first in the file.
+This is called BigEndian order.
+@!@^BigEndian order@>
+
+@ The rest of the \.{TFM} file may be regarded as a sequence of ten data
+arrays having the informal specification
+$$\def\arr$[#1]#2${\&{array} $[#1]$ \&{of} #2}
+\def\PB#1{\arr#1}
+\vbox{\halign{\hfil\\{#}&$\,:\,$#\hfil\cr
+header&|[0 dotdot lh-1]@t\\{stuff}@>|\cr
+char\_info&|[bc dotdot ec]char_info_word|\cr
+width&|[0 dotdot nw-1]fix_word|\cr
+height&|[0 dotdot nh-1]fix_word|\cr
+depth&|[0 dotdot nd-1]fix_word|\cr
+italic&|[0 dotdot ni-1]fix_word|\cr
+lig\_kern&|[0 dotdot nl-1]lig_kern_command|\cr
+kern&|[0 dotdot nk-1]fix_word|\cr
+exten&|[0 dotdot ne-1]extensible_recipe|\cr
+param&|[1 dotdot np]fix_word|\cr}}$$
+The most important data type used here is a |@!fix_word|, which is
+a 32-bit representation of a binary fraction. A |fix_word| is a signed
+quantity, with the two's complement of the entire word used to represent
+negation. Of the 32 bits in a |fix_word|, exactly 12 are to the left of the
+binary point; thus, the largest |fix_word| value is $2048-2^{-20}$, and
+the smallest is $-2048$. We will see below, however, that all but two of
+the |fix_word| values must lie between $-16$ and $+16$.
+
+@ The first data array is a block of header information, which contains
+general facts about the font. The header must contain at least two words,
+|header[0]| and |header[1]|, whose meaning is explained below.
+Additional header information of use to other software routines might
+also be included, but \TeX82 does not need to know about such details.
+For example, 16 more words of header information are in use at the Xerox
+Palo Alto Research Center; the first ten specify the character coding
+scheme used (e.g., `\.{XEROX text}' or `\.{TeX math symbols}'), the next five
+give the font identifier (e.g., `\.{HELVETICA}' or `\.{CMSY}'), and the
+last gives the ``face byte.'' The program that converts \.{DVI} files
+to Xerox printing format gets this information by looking at the \.{TFM}
+file, which it needs to read anyway because of other information that
+is not explicitly repeated in \.{DVI}~format.
+
+\yskip\hang|header[0]| is a 32-bit check sum that \TeX\ will copy into
+the \.{DVI} output file. Later on when the \.{DVI} file is printed,
+possibly on another computer, the actual font that gets used is supposed
+to have a check sum that agrees with the one in the \.{TFM} file used by
+\TeX. In this way, users will be warned about potential incompatibilities.
+(However, if the check sum is zero in either the font file or the \.{TFM}
+file, no check is made.) The actual relation between this check sum and
+the rest of the \.{TFM} file is not important; the check sum is simply an
+identification number with the property that incompatible fonts almost
+always have distinct check sums.
+@^check sum@>
+
+\yskip\hang|header[1]| is a |fix_word| containing the design size of
+the font, in units of \TeX\ points. This number must be at least 1.0; it is
+fairly arbitrary, but usually the design size is 10.0 for a ``10 point''
+font, i.e., a font that was designed to look best at a 10-point size,
+whatever that really means. When a \TeX\ user asks for a font
+`\.{at} $\delta$ \.{pt}', the effect is to override the design size
+and replace it by $\delta$, and to multiply the $x$ and~$y$ coordinates
+of the points in the font image by a factor of $\delta$ divided by the
+design size. {\sl All other dimensions in the\/ \.{TFM} file are
+|fix_word|\kern-1pt\ numbers in design-size units}, with the exception of
+|param[1]| (which denotes the slant ratio). Thus, for example, the value
+of |param[6]|, which defines the \.{em} unit, is often the |fix_word| value
+$2^{20}=1.0$, since many fonts have a design size equal to one em.
+The other dimensions must be less than 16 design-size units in absolute
+value; thus, |header[1]| and |param[1]| are the only |fix_word|
+entries in the whole \.{TFM} file whose first byte might be something
+besides 0 or 255.
+
+@ Next comes the |char_info| array, which contains one |@!char_info_word|
+per character. Each word in this part of the file contains six fields
+packed into four bytes as follows.
+
+\yskip\hang first byte: |@!width_index| (8 bits)\par
+\hang second byte: |@!height_index| (4 bits) times 16, plus |@!depth_index|
+ (4~bits)\par
+\hang third byte: |@!italic_index| (6 bits) times 4, plus |@!tag|
+ (2~bits)\par
+\hang fourth byte: |@!rem| (8 bits)\par
+\yskip\noindent
+The actual width of a character is \\{width}|[width_index]|, in design-size
+units; this is a device for compressing information, since many characters
+have the same width. Since it is quite common for many characters
+to have the same height, depth, or italic correction, the \.{TFM} format
+imposes a limit of 16 different heights, 16 different depths, and
+64 different italic corrections.
+
+@!@^italic correction@>
+The italic correction of a character has two different uses.
+(a)~In ordinary text, the italic correction is added to the width only if
+the \TeX\ user specifies `\.{\\/}' after the character.
+(b)~In math formulas, the italic correction is always added to the width,
+except with respect to the positioning of subscripts.
+
+Incidentally, the relation $\\{width}[0]=\\{height}[0]=\\{depth}[0]=
+\\{italic}[0]=0$ should always hold, so that an index of zero implies a
+value of zero. The |width_index| should never be zero unless the
+character does not exist in the font, since a character is valid if and
+only if it lies between |bc| and |ec| and has a nonzero |width_index|.
+
+@ The |tag| field in a |char_info_word| has four values that explain how to
+interpret the |rem| field.
+
+\yskip\hangg|tag==0| (|no_tag|) means that |rem| is unused.\par
+\hangg|tag==1| (|lig_tag|) means that this character has a ligature/kerning
+program starting at position |rem| in the |lig_kern| array.\par
+\hangg|tag==2| (|list_tag|) means that this character is part of a chain of
+characters of ascending sizes, and not the largest in the chain. The
+|rem| field gives the character code of the next larger character.\par
+\hangg|tag==3| (|ext_tag|) means that this character code represents an
+extensible character, i.e., a character that is built up of smaller pieces
+so that it can be made arbitrarily large. The pieces are specified in
+|@!exten[rem]|.\par
+\yskip\noindent
+Characters with |tag==2| and |tag==3| are treated as characters with |tag==0|
+unless they are used in special circumstances in math formulas. For example,
+the \.{\\sum} operation looks for a |list_tag|, and the \.{\\left}
+operation looks for both |list_tag| and |ext_tag|.
+
+@d no_tag 0 /*vanilla character*/
+@d lig_tag 1 /*character has a ligature/kerning program*/
+@d list_tag 2 /*character has a successor in a charlist*/
+@d ext_tag 3 /*character is extensible*/
+
+@ The |lig_kern| array contains instructions in a simple programming language
+that explains what to do for special letter pairs. Each word in this array is a
+|@!lig_kern_command| of four bytes.
+
+\yskip\hang first byte: |skip_byte|, indicates that this is the final program
+ step if the byte is 128 or more, otherwise the next step is obtained by
+ skipping this number of intervening steps.\par
+\hang second byte: |next_char|, ``if |next_char| follows the current character,
+ then perform the operation and stop, otherwise continue.''\par
+\hang third byte: |op_byte|, indicates a ligature step if less than~128,
+ a kern step otherwise.\par
+\hang fourth byte: |rem|.\par
+\yskip\noindent
+In a kern step, an
+additional space equal to |kern[256*(op_byte-128)+rem]| is inserted
+between the current character and |next_char|. This amount is
+often negative, so that the characters are brought closer together
+by kerning; but it might be positive.
+
+There are eight kinds of ligature steps, having |op_byte| codes $4a+2b+c$ where
+$0\le a\le b+c$ and $0\le b,c\le1$. The character whose code is
+|rem| is inserted between the current character and |next_char|;
+then the current character is deleted if $b=0$, and |next_char| is
+deleted if $c=0$; then we pass over $a$~characters to reach the next
+current character (which may have a ligature/kerning program of its own).
+
+If the very first instruction of the |lig_kern| array has |skip_byte==255|,
+the |next_char| byte is the so-called boundary character of this font;
+the value of |next_char| need not lie between |bc| and~|ec|.
+If the very last instruction of the |lig_kern| array has |skip_byte==255|,
+there is a special ligature/kerning program for a boundary character at the
+left, beginning at location |256*op_byte+rem|.
+The interpretation is that \TeX\ puts implicit boundary characters
+before and after each consecutive string of characters from the same font.
+These implicit characters do not appear in the output, but they can affect
+ligatures and kerning.
+
+If the very first instruction of a character's |lig_kern| program has
+|skip_byte > 128|, the program actually begins in location
+|256*op_byte+rem|. This feature allows access to large |lig_kern|
+arrays, because the first instruction must otherwise
+appear in a location | <= 255|.
+
+Any instruction with |skip_byte > 128| in the |lig_kern| array must satisfy
+the condition
+$$\hbox{|256*op_byte+rem < nl|.}$$
+If such an instruction is encountered during
+normal program execution, it denotes an unconditional halt; no ligature
+or kerning command is performed.
+
+@d stop_flag qi(128) /*value indicating `\.{STOP}' in a lig/kern program*/
+@d kern_flag qi(128) /*op code for a kern step*/
+@d skip_byte(A) A.b0
+@d next_char(A) A.b1
+@d op_byte(A) A.b2
+@d rem_byte(A) A.b3
+
+@ Extensible characters are specified by an |@!extensible_recipe|, which
+consists of four bytes called |@!top|, |@!mid|, |@!bot|, and |@!rep| (in this
+order). These bytes are the character codes of individual pieces used to
+build up a large symbol. If |top|, |mid|, or |bot| are zero, they are not
+present in the built-up result. For example, an extensible vertical line is
+like an extensible bracket, except that the top and bottom pieces are missing.
+
+Let $T$, $M$, $B$, and $R$ denote the respective pieces, or an empty box
+if the piece isn't present. Then the extensible characters have the form
+$TR^kMR^kB$ from top to bottom, for some |k >= 0|, unless $M$ is absent;
+in the latter case we can have $TR^kB$ for both even and odd values of~|k|.
+The width of the extensible character is the width of $R$; and the
+height-plus-depth is the sum of the individual height-plus-depths of the
+components used, since the pieces are butted together in a vertical list.
+
+@d ext_top(A) A.b0 /*|top| piece in a recipe*/
+@d ext_mid(A) A.b1 /*|mid| piece in a recipe*/
+@d ext_bot(A) A.b2 /*|bot| piece in a recipe*/
+@d ext_rep(A) A.b3 /*|rep| piece in a recipe*/
+
+@ The final portion of a \.{TFM} file is the |param| array, which is another
+sequence of |fix_word| values.
+
+\yskip\hang|param[1]==slant| is the amount of italic slant, which is used
+to help position accents. For example, |slant==.25| means that when you go
+up one unit, you also go .25 units to the right. The |slant| is a pure
+number; it's the only |fix_word| other than the design size itself that is
+not scaled by the design size.
+
+\hang|param[2]==space| is the normal spacing between words in text.
+Note that character |' '| in the font need not have anything to do with
+blank spaces.
+
+\hang|param[3]==space_stretch| is the amount of glue stretching between words.
+
+\hang|param[4]==space_shrink| is the amount of glue shrinking between words.
+
+\hang|param[5]==x_height| is the size of one ex in the font; it is also
+the height of letters for which accents don't have to be raised or lowered.
+
+\hang|param[6]==quad| is the size of one em in the font.
+
+\hang|param[7]==extra_space| is the amount added to |param[2]| at the
+ends of sentences.
+
+\yskip\noindent
+If fewer than seven parameters are present, \TeX\ sets the missing parameters
+to zero. Fonts used for math symbols are required to have
+additional parameter information, which is explained later.
+
+@d slant_code 1
+@d space_code 2
+@d space_stretch_code 3
+@d space_shrink_code 4
+@d x_height_code 5
+@d quad_code 6
+@d extra_space_code 7
+
+@ So that is what \.{TFM} files hold. Since \TeX\ has to absorb such information
+about lots of fonts, it stores most of the data in a large array called
+|font_info|. Each item of |font_info| is a |memory_word|; the |fix_word|
+data gets converted into |scaled| entries, while everything else goes into
+words of type |four_quarters|.
+
+When the user defines \.{\\font\\f}, say, \TeX\ assigns an internal number
+to the user's font~\.{\\f}. Adding this number to |font_id_base| gives the
+|eqtb| location of a ``frozen'' control sequence that will always select
+the font.
+
+@<Types...@>=
+typedef uint8_t internal_font_number; /*|font| in a |char_node|*/
+typedef int32_t font_index; /*index into |font_info|*/
+
+@ Here now is the (rather formidable) array of font arrays.
+
+@d non_char qi(256) /*a |halfword| code that can't match a real character*/
+@d non_address 0 /*a spurious |bchar_label|*/
+
+@<Glob...@>=
+static memory_word @!font_info[font_mem_size+1];
+ /*the big collection of font data*/
+static font_index @!fmem_ptr; /*first unused word of |font_info|*/
+static internal_font_number @!font_ptr; /*largest internal font number in use*/
+static four_quarters @!font_check0[font_max-font_base+1],
+ *const @!font_check = @!font_check0-font_base; /*check sum*/
+static scaled @!font_size0[font_max-font_base+1],
+ *const @!font_size = @!font_size0-font_base; /*``at'' size*/
+static scaled @!font_dsize0[font_max-font_base+1],
+ *const @!font_dsize = @!font_dsize0-font_base; /*``design'' size*/
+static font_index @!font_params0[font_max-font_base+1],
+ *const @!font_params = @!font_params0-font_base; /*how many font
+ parameters are present*/
+static str_number @!font_name0[font_max-font_base+1],
+ *const @!font_name = @!font_name0-font_base; /*name of the font*/
+static str_number @!font_area0[font_max-font_base+1],
+ *const @!font_area = @!font_area0-font_base; /*area of the font*/
+static eight_bits @!font_bc0[font_max-font_base+1],
+ *const @!font_bc = @!font_bc0-font_base;
+ /*beginning (smallest) character code*/
+static eight_bits @!font_ec0[font_max-font_base+1],
+ *const @!font_ec = @!font_ec0-font_base;
+ /*ending (largest) character code*/
+static pointer @!font_glue0[font_max-font_base+1],
+ *const @!font_glue = @!font_glue0-font_base;
+ /*glue specification for interword space, |null| if not allocated*/
+static bool @!font_used0[font_max-font_base+1],
+ *const @!font_used = @!font_used0-font_base;
+ /*has a character from this font actually appeared in the output?*/
+static int @!hyphen_char0[font_max-font_base+1],
+ *const @!hyphen_char = @!hyphen_char0-font_base;
+ /*current \.{\\hyphenchar} values*/
+static int @!skew_char0[font_max-font_base+1],
+ *const @!skew_char = @!skew_char0-font_base;
+ /*current \.{\\skewchar} values*/
+static font_index @!bchar_label0[font_max-font_base+1],
+ *const @!bchar_label = @!bchar_label0-font_base;
+ /*start of |lig_kern| program for left boundary character,
+ |non_address| if there is none*/
+static int16_t @!font_bchar0[font_max-font_base+1],
+ *const @!font_bchar = @!font_bchar0-font_base;
+ /*boundary character, |non_char| if there is none*/
+static int16_t @!font_false_bchar0[font_max-font_base+1],
+ *const @!font_false_bchar = @!font_false_bchar0-font_base;
+ /*|font_bchar| if it doesn't exist in the font, otherwise |non_char|*/
+
+@ Besides the arrays just enumerated, we have directory arrays that make it
+easy to get at the individual entries in |font_info|. For example, the
+|char_info| data for character |c| in font |f| will be in
+|font_info[char_base[f]+c].qqqq|; and if |w| is the |width_index|
+part of this word (the |b0| field), the width of the character is
+|font_info[width_base[f]+w].sc|. (These formulas assume that
+|min_quarterword| has already been added to |c| and to |w|, since \TeX\
+stores its quarterwords that way.)
+
+@<Glob...@>=
+static int @!char_base0[font_max-font_base+1],
+ *const @!char_base = @!char_base0-font_base;
+ /*base addresses for |char_info|*/
+static int @!width_base0[font_max-font_base+1],
+ *const @!width_base = @!width_base0-font_base;
+ /*base addresses for widths*/
+static int @!height_base0[font_max-font_base+1],
+ *const @!height_base = @!height_base0-font_base;
+ /*base addresses for heights*/
+static int @!depth_base0[font_max-font_base+1],
+ *const @!depth_base = @!depth_base0-font_base;
+ /*base addresses for depths*/
+static int @!italic_base0[font_max-font_base+1],
+ *const @!italic_base = @!italic_base0-font_base;
+ /*base addresses for italic corrections*/
+static int @!lig_kern_base0[font_max-font_base+1],
+ *const @!lig_kern_base = @!lig_kern_base0-font_base;
+ /*base addresses for ligature/kerning programs*/
+static int @!kern_base0[font_max-font_base+1],
+ *const @!kern_base = @!kern_base0-font_base;
+ /*base addresses for kerns*/
+static int @!exten_base0[font_max-font_base+1],
+ *const @!exten_base = @!exten_base0-font_base;
+ /*base addresses for extensible recipes*/
+static int @!param_base0[font_max-font_base+1],
+ *const @!param_base = @!param_base0-font_base;
+ /*base addresses for font parameters*/
+
+@ @<Set init...@>=
+for (k=font_base; k<=font_max; k++) font_used[k]=false;
+
+@ \TeX\ always knows at least one font, namely the null font. It has no
+characters, and its seven parameters are all equal to zero.
+
+@<Initialize table...@>=
+font_ptr=null_font;fmem_ptr=7;
+font_name[null_font]=s_no("nullfont");font_area[null_font]=empty_string;
+hyphen_char[null_font]='-';skew_char[null_font]=-1;
+bchar_label[null_font]=non_address;
+font_bchar[null_font]=non_char;font_false_bchar[null_font]=non_char;
+font_bc[null_font]=1;font_ec[null_font]=0;
+font_size[null_font]=0;font_dsize[null_font]=0;
+char_base[null_font]=0;width_base[null_font]=0;
+height_base[null_font]=0;depth_base[null_font]=0;
+italic_base[null_font]=0;lig_kern_base[null_font]=0;
+kern_base[null_font]=0;exten_base[null_font]=0;
+font_glue[null_font]=null;font_params[null_font]=7;
+param_base[null_font]=-1;
+for (k=0; k<=6; k++) font_info[k].sc=0;
+
+@ @<Put each...@>=
+primitive("nullfont", set_font, null_font);
+@!@:null\_font\_}{\.{\\nullfont} primitive@>
+text(frozen_null_font)=text(cur_val);eqtb[frozen_null_font]=eqtb[cur_val];
+
+@ Of course we want to define macros that suppress the detail of how font
+information is actually packed, so that we don't have to write things like
+$$\hbox{|font_info[width_base[f]+font_info[char_base[f]+c].qqqq.b0].sc|}$$
+too often. The \.{WEB} definitions here make |char_info(f)(c)| the
+|four_quarters| word of font information corresponding to character
+|c| of font |f|. If |q| is such a word, |char_width(f)(q)| will be
+the character's width; hence the long formula above is at least
+abbreviated to
+$$\hbox{|char_width(f)(char_info(f)(c))|.}$$
+Usually, of course, we will fetch |q| first and look at several of its
+fields at the same time.
+
+The italic correction of a character will be denoted by
+|char_italic(f)(q)|, so it is analogous to |char_width|. But we will get
+at the height and depth in a slightly different way, since we usually want
+to compute both height and depth if we want either one. The value of
+|height_depth(q)| will be the 8-bit quantity
+$$b=|height_index|\times16+|depth_index|,$$ and if |b| is such a byte we
+will write |char_height(f)(b)| and |char_depth(f)(b)| for the height and
+depth of the character |c| for which |q==char_info(f)(c)|. Got that?
+
+The tag field will be called |char_tag(q)|; the remainder byte will be
+called |rem_byte(q)|, using a macro that we have already defined above.
+
+Access to a character's |width|, |height|, |depth|, and |tag| fields is
+part of \TeX's inner loop, so we want these macros to produce code that is
+as fast as possible under the circumstances.
+@^inner loop@>
+
+@d char_info(A, B) font_info[char_base[A]+B].qqqq
+@d char_width(A, B) font_info[width_base[A]+B.b0].sc
+@d char_exists(A) (A.b0 > min_quarterword)
+@d char_italic(A, B) font_info[italic_base[A]+(qo(B.b2))/4].sc
+@d height_depth(A) qo(A.b1)
+@d char_height(A, B) font_info[height_base[A]+(B)/16].sc
+@d char_depth(A, B) font_info[depth_base[A]+(B)%16].sc
+@d char_tag(A) ((qo(A.b2))%4)
+
+@ The global variable |null_character| is set up to be a word of
+|char_info| for a character that doesn't exist. Such a word provides a
+convenient way to deal with erroneous situations.
+
+@<Glob...@>=
+static four_quarters @!null_character; /*nonexistent character information*/
+
+@ @<Set init...@>=
+null_character.b0=min_quarterword;null_character.b1=min_quarterword;
+null_character.b2=min_quarterword;null_character.b3=min_quarterword;
+
+@ Here are some macros that help process ligatures and kerns.
+We write |char_kern(f)(j)| to find the amount of kerning specified by
+kerning command~|j| in font~|f|. If |j| is the |char_info| for a character
+with a ligature/kern program, the first instruction of that program is either
+|i==font_info[lig_kern_start(f)(j)]| or |font_info[lig_kern_restart(f)(i)]|,
+depending on whether or not |skip_byte(i) <= stop_flag|.
+
+The constant |kern_base_offset| should be simplified, for \PASCAL\ compilers
+that do not do local optimization.
+@^system dependencies@>
+
+@d char_kern(A, B) font_info[kern_base[A]+256*op_byte(B)+rem_byte(B)].sc
+@d kern_base_offset 256*(128+min_quarterword)
+@d lig_kern_start(A, B) lig_kern_base[A]+B.b3 /*beginning of lig/kern program*/
+@d lig_kern_restart(A, B) lig_kern_base[A]+256*op_byte(B)+rem_byte(B)+32768-kern_base_offset
+
+@ Font parameters are referred to as |slant(f)|, |space(f)|, etc.
+
+@d param_end(A) param_base[A]].sc
+@d param(A) font_info[A+param_end
+@d slant param(slant_code) /*slant to the right, per unit distance upward*/
+@d space param(space_code) /*normal space between words*/
+@d space_stretch param(space_stretch_code) /*stretch between words*/
+@d space_shrink param(space_shrink_code) /*shrink between words*/
+@d x_height param(x_height_code) /*one ex*/
+@d quad param(quad_code) /*one em*/
+@d extra_space param(extra_space_code) /*additional space at end of sentence*/
+
+@<The em width for |cur_font|@>=quad(cur_font)
+
+@ @<The x-height for |cur_font|@>=x_height(cur_font)
+
+@ \TeX\ checks the information of a \.{TFM} file for validity as the
+file is being read in, so that no further checks will be needed when
+typesetting is going on. The somewhat tedious subroutine that does this
+is called |read_font_info|. It has four parameters: the user font
+identifier~|u|, the file name and area strings |nom| and |aire|, and the
+``at'' size~|s|. If |s|~is negative, it's the negative of a scale factor
+to be applied to the design size; |s==-1000| is the normal case.
+Otherwise |s| will be substituted for the design size; in this
+case, |s| must be positive and less than $2048\rm\,pt$
+(i.e., it must be less than $2^{27}$ when considered as an integer).
+
+The subroutine opens and closes a global file variable called |tfm_file|.
+It returns the value of the internal font number that was just loaded.
+If an error is detected, an error message is issued and no font
+information is stored; |null_font| is returned in this case.
+
+@d abort goto bad_tfm /*do this when the \.{TFM} data is wrong*/
+
+@p static internal_font_number read_font_info(pointer @!u, str_number @!nom, str_number @!aire,
+ scaled @!s) /*input a \.{TFM} file*/
+{@+
+int k; /*index into |font_info|*/
+bool @!file_opened; /*was |tfm_file| successfully opened?*/
+halfword @!lf, @!lh, @!bc, @!ec, @!nw, @!nh, @!nd, @!ni, @!nl, @!nk, @!ne, @!np;
+ /*sizes of subfiles*/
+internal_font_number @!f; /*the new font's number*/
+internal_font_number @!g; /*the number to return*/
+eight_bits @!a, @!b, @!c, @!d; /*byte variables*/
+four_quarters @!qw;scaled @!sw; /*accumulators*/
+int @!bch_label; /*left boundary start location, or infinity*/
+int @!bchar; /*boundary character, or 256*/
+scaled @!z; /*the design size or the ``at'' size*/
+int @!alpha;int @!beta;
+ /*auxiliary quantities used in fixed-point multiplication*/
+g=null_font;@/
+@<Read and check the font data; |abort| if the \.{TFM} file is malformed;
+if there's no room for this font, say so and |goto done|; otherwise |incr(font_ptr)|
+and |goto done|@>;
+bad_tfm: @<Report that the font won't be loaded@>;
+done: if (file_opened) b_close(&tfm_file);
+return g;
+}
+
+@ There are programs called \.{TFtoPL} and \.{PLtoTF} that convert
+between the \.{TFM} format and a symbolic property-list format
+that can be easily edited. These programs contain extensive
+diagnostic information, so \TeX\ does not have to bother giving
+precise details about why it rejects a particular \.{TFM} file.
+@.TFtoPL@> @.PLtoTF@>
+
+@d start_font_error_message print_err("Font ");sprint_cs(u);
+ print_char('=');print_file_name(nom, aire, empty_string);
+ if (s >= 0)
+ {@+print(" at ");print_scaled(s);print("pt");
+ }
+ else if (s!=-1000)
+ {@+print(" scaled ");print_int(-s);
+ }
+
+@<Report that the font won't be loaded@>=
+start_font_error_message;
+@.Font x=xx not loadable...@>
+if (file_opened) print(" not loadable: Bad metric (TFM) file");
+else print(" not loadable: Metric (TFM) file not found");
+help5("I wasn't able to read the size data for this font,",@/
+"so I will ignore the font specification.",@/
+"[Wizards can fix TFM files using TFtoPL/PLtoTF.]",@/
+"You might try inserting a different font spec;",@/
+"e.g., type `I\\font<same font id>=<substitute font name>'.");
+error()
+
+@ @<Read and check...@>=
+@<Open |tfm_file| for input@>;
+@<Read the {\.{TFM}} size fields@>;
+@<Use size fields to allocate font information@>;
+@<Read the {\.{TFM}} header@>;
+@<Read character data@>;
+@<Read box dimensions@>;
+@<Read ligature/kern program@>;
+@<Read extensible character recipes@>;
+@<Read font parameters@>;
+@<Make final adjustments and |goto done|@>@;
+
+@ @<Open |tfm_file| for input@>=
+file_opened=false;
+pack_file_name(nom, empty_string,empty_string,".tfm"); /* \TeX\ Live */
+if (!b_open_in(&tfm_file)) abort;
+file_opened=true
+
+@ Note: A malformed \.{TFM} file might be shorter than it claims to be;
+thus |eof(tfm_file)| might be true when |read_font_info| refers to
+|tfm_file.d| or when it says |get(tfm_file)|. If such circumstances
+cause system error messages, you will have to defeat them somehow,
+for example by defining |fget| to be `\ignorespaces|{@+get(tfm_file);|
+|if (eof(tfm_file)) abort;} |\unskip'.
+@^system dependencies@>
+
+@d fget get(tfm_file)
+@d fbyte tfm_file.d
+@d read_sixteen(A) {@+A=fbyte;
+ if (A > 127) abort;
+ fget;A=A*0400+fbyte;
+ }
+@d store_four_quarters(A) {@+fget;a=fbyte;qw.b0=qi(a);
+ fget;b=fbyte;qw.b1=qi(b);
+ fget;c=fbyte;qw.b2=qi(c);
+ fget;d=fbyte;qw.b3=qi(d);
+ A=qw;
+ }
+
+@ @<Read the {\.{TFM}} size fields@>=
+{@+read_sixteen(lf);
+fget;read_sixteen(lh);
+fget;read_sixteen(bc);
+fget;read_sixteen(ec);
+if ((bc > ec+1)||(ec > 255)) abort;
+if (bc > 255) /*|bc==256| and |ec==255|*/
+ {@+bc=1;ec=0;
+ }
+fget;read_sixteen(nw);
+fget;read_sixteen(nh);
+fget;read_sixteen(nd);
+fget;read_sixteen(ni);
+fget;read_sixteen(nl);
+fget;read_sixteen(nk);
+fget;read_sixteen(ne);
+fget;read_sixteen(np);
+if (lf!=6+lh+(ec-bc+1)+nw+nh+nd+ni+nl+nk+ne+np) abort;
+if ((nw==0)||(nh==0)||(nd==0)||(ni==0)) abort;
+}
+
+@ The preliminary settings of the index-offset variables |char_base|,
+|width_base|, |lig_kern_base|, |kern_base|, and |exten_base| will be
+corrected later by subtracting |min_quarterword| from them; and we will
+subtract 1 from |param_base| too. It's best to forget about such anomalies
+until later.
+
+@<Use size fields to allocate font information@>=
+lf=lf-6-lh; /*|lf| words should be loaded into |font_info|*/
+if (np < 7) lf=lf+7-np; /*at least seven parameters will appear*/
+if ((font_ptr==font_max)||(fmem_ptr+lf > font_mem_size))
+ @<Apologize for not loading the font, |goto done|@>;
+f=font_ptr+1;
+char_base[f]=fmem_ptr-bc;
+width_base[f]=char_base[f]+ec+1;
+height_base[f]=width_base[f]+nw;
+depth_base[f]=height_base[f]+nh;
+italic_base[f]=depth_base[f]+nd;
+lig_kern_base[f]=italic_base[f]+ni;
+kern_base[f]=lig_kern_base[f]+nl-kern_base_offset;
+exten_base[f]=kern_base[f]+kern_base_offset+nk;
+param_base[f]=exten_base[f]+ne
+
+@ @<Apologize for not loading...@>=
+{@+start_font_error_message;
+print(" not loaded: Not enough room left");
+@.Font x=xx not loaded...@>
+help4("I'm afraid I won't be able to make use of this font,",@/
+"because my memory for character-size data is too small.",@/
+"If you're really stuck, ask a wizard to enlarge me.",@/
+"Or maybe try `I\\font<same font id>=<name of loaded font>'.");
+error();goto done;
+}
+
+@ Only the first two words of the header are needed by \TeX82.
+
+@<Read the {\.{TFM}} header@>=
+{@+if (lh < 2) abort;
+store_four_quarters(font_check[f]);
+fget;read_sixteen(z); /*this rejects a negative design size*/
+fget;z=z*0400+fbyte;fget;z=(z*020)+(fbyte/020);
+if (z < unity) abort;
+while (lh > 2)
+ {@+fget;fget;fget;fget;decr(lh); /*ignore the rest of the header*/
+ }
+font_dsize[f]=z;
+if (s!=-1000)
+ if (s >= 0) z=s;
+ else z=xn_over_d(z,-s, 1000);
+font_size[f]=z;
+}
+
+@ @<Read character data@>=
+for (k=fmem_ptr; k<=width_base[f]-1; k++)
+ {@+store_four_quarters(font_info[k].qqqq);
+ if ((a >= nw)||(b/020 >= nh)||(b%020 >= nd)||
+ (c/4 >= ni)) abort;
+ switch (c%4) {
+ case lig_tag: if (d >= nl) abort;@+break;
+ case ext_tag: if (d >= ne) abort;@+break;
+ case list_tag: @<Check for charlist cycle@>@;@+break;
+ default:do_nothing; /*|no_tag|*/
+ }
+ }
+
+@ We want to make sure that there is no cycle of characters linked together
+by |list_tag| entries, since such a cycle would get \TeX\ into an endless
+loop. If such a cycle exists, the routine here detects it when processing
+the largest character code in the cycle.
+
+@d check_byte_range(A) {@+if ((A < bc)||(A > ec)) abort;@+}
+@d current_character_being_worked_on k+bc-fmem_ptr
+
+@<Check for charlist cycle@>=
+{@+check_byte_range(d);
+while (d < current_character_being_worked_on)
+ {@+qw=char_info(f, d);
+ /*N.B.: not |qi(d)|, since |char_base[f]| hasn't been adjusted yet*/
+ if (char_tag(qw)!=list_tag) goto not_found;
+ d=qo(rem_byte(qw)); /*next character on the list*/
+ }
+if (d==current_character_being_worked_on) abort; /*yes, there's a cycle*/
+not_found: ;}
+
+@ A |fix_word| whose four bytes are $(a,b,c,d)$ from left to right represents
+the number
+$$x=\left\{\vcenter{\halign{$#$,\hfil\qquad&if $#$\hfil\cr
+b\cdot2^{-4}+c\cdot2^{-12}+d\cdot2^{-20}&a=0;\cr
+-16+b\cdot2^{-4}+c\cdot2^{-12}+d\cdot2^{-20}&a=255.\cr}}\right.$$
+(No other choices of |a| are allowed, since the magnitude of a number in
+design-size units must be less than 16.) We want to multiply this
+quantity by the integer~|z|, which is known to be less than $2^{27}$.
+If $|z|<2^{23}$, the individual multiplications $b\cdot z$,
+$c\cdot z$, $d\cdot z$ cannot overflow; otherwise we will divide |z| by 2,
+4, 8, or 16, to obtain a multiplier less than $2^{23}$, and we can
+compensate for this later. If |z| has thereby been replaced by
+$|z|^\prime=|z|/2^e$, let $\beta=2^{4-e}$; we shall compute
+$$\lfloor(b+c\cdot2^{-8}+d\cdot2^{-16})\,z^\prime/\beta\rfloor$$
+if $a=0$, or the same quantity minus $\alpha=2^{4+e}z^\prime$ if $a=255$.
+This calculation must be done exactly, in order to guarantee portability
+of \TeX\ between computers.
+
+@d store_scaled(A) {@+fget;a=fbyte;fget;b=fbyte;
+ fget;c=fbyte;fget;d=fbyte;@/
+ sw=(((((d*z)/0400)+(c*z))/0400)+(b*z))/beta;
+ if (a==0) A=sw;@+else if (a==255) A=sw-alpha;@+else abort;
+ }
+
+@<Read box dimensions@>=
+{@+@<Replace |z| by $|z|^\prime$ and compute $\alpha,\beta$@>;
+for (k=width_base[f]; k<=lig_kern_base[f]-1; k++)
+ store_scaled(font_info[k].sc);
+if (font_info[width_base[f]].sc!=0) abort; /*\\{width}[0] must be zero*/
+if (font_info[height_base[f]].sc!=0) abort; /*\\{height}[0] must be zero*/
+if (font_info[depth_base[f]].sc!=0) abort; /*\\{depth}[0] must be zero*/
+if (font_info[italic_base[f]].sc!=0) abort; /*\\{italic}[0] must be zero*/
+}
+
+@ @<Replace |z|...@>=
+{@+alpha=16;
+while (z >= 040000000)
+ {@+z=z/2;alpha=alpha+alpha;
+ }
+beta=256/alpha;alpha=alpha*z;
+}
+
+@ @d check_existence(A) @t@>@;@/
+ {@+check_byte_range(A);
+ qw=char_info(f, A); /*N.B.: not |qi(A)|*/
+ if (!char_exists(qw)) abort;
+ }
+
+@<Read ligature/kern program@>=
+bch_label=077777;bchar=256;
+if (nl > 0)
+ {@+for (k=lig_kern_base[f]; k<=kern_base[f]+kern_base_offset-1; k++)
+ {@+store_four_quarters(font_info[k].qqqq);
+ if (a > 128)
+ {@+if (256*c+d >= nl) abort;
+ if (a==255) if (k==lig_kern_base[f]) bchar=b;
+ }
+ else{@+if (b!=bchar) check_existence(b);
+ if (c < 128) check_existence(d)@; /*check ligature*/
+ else if (256*(c-128)+d >= nk) abort; /*check kern*/
+ if (a < 128) if (k-lig_kern_base[f]+a+1 >= nl) abort;
+ }
+ }
+ if (a==255) bch_label=256*c+d;
+ }
+for (k=kern_base[f]+kern_base_offset; k<=exten_base[f]-1; k++)
+ store_scaled(font_info[k].sc);
+
+@ @<Read extensible character recipes@>=
+for (k=exten_base[f]; k<=param_base[f]-1; k++)
+ {@+store_four_quarters(font_info[k].qqqq);
+ if (a!=0) check_existence(a);
+ if (b!=0) check_existence(b);
+ if (c!=0) check_existence(c);
+ check_existence(d);
+ }
+
+@ We check to see that the \.{TFM} file doesn't end prematurely; but
+no error message is given for files having more than |lf| words.
+
+@<Read font parameters@>=
+{@+for (k=1; k<=np; k++)
+ if (k==1) /*the |slant| parameter is a pure number*/
+ {@+fget;sw=fbyte;if (sw > 127) sw=sw-256;
+ fget;sw=sw*0400+fbyte;fget;sw=sw*0400+fbyte;
+ fget;font_info[param_base[f]].sc=
+ (sw*020)+(fbyte/020);
+ }
+ else store_scaled(font_info[param_base[f]+k-1].sc);
+if (eof(tfm_file)) abort;
+for (k=np+1; k<=7; k++) font_info[param_base[f]+k-1].sc=0;
+}
+
+@ Now to wrap it up, we have checked all the necessary things about the \.{TFM}
+file, and all we need to do is put the finishing touches on the data for
+the new font.
+
+@d adjust(A) A[f]=qo(A[f])
+ /*correct for the excess |min_quarterword| that was added*/
+
+@<Make final adjustments...@>=
+if (np >= 7) font_params[f]=np;@+else font_params[f]=7;
+hyphen_char[f]=default_hyphen_char;skew_char[f]=default_skew_char;
+if (bch_label < nl) bchar_label[f]=bch_label+lig_kern_base[f];
+else bchar_label[f]=non_address;
+font_bchar[f]=qi(bchar);
+font_false_bchar[f]=qi(bchar);
+if (bchar <= ec) if (bchar >= bc)
+ {@+qw=char_info(f, bchar); /*N.B.: not |qi(bchar)|*/
+ if (char_exists(qw)) font_false_bchar[f]=non_char;
+ }
+font_name[f]=nom;
+font_area[f]=aire;
+font_bc[f]=bc;font_ec[f]=ec;font_glue[f]=null;
+adjust(char_base);adjust(width_base);adjust(lig_kern_base);
+adjust(kern_base);adjust(exten_base);
+decr(param_base[f]);
+fmem_ptr=fmem_ptr+lf;font_ptr=f;g=f;goto done
+
+@ Before we forget about the format of these tables, let's deal with two
+of \TeX's basic scanning routines related to font information.
+
+@<Declare procedures that scan font-related stuff@>=
+static void scan_font_ident(void)
+{@+internal_font_number f;
+halfword @!m;
+@<Get the next non-blank non-call...@>;
+if (cur_cmd==def_font) f=cur_font;
+else if (cur_cmd==set_font) f=cur_chr;
+else if (cur_cmd==def_family)
+ {@+m=cur_chr;scan_four_bit_int();f=equiv(m+cur_val);
+ }
+else{@+print_err("Missing font identifier");
+@.Missing font identifier@>
+ help2("I was looking for a control sequence whose",@/
+ "current meaning has been defined by \\font.");
+ back_error();f=null_font;
+ }
+cur_val=f;
+}
+
+@ The following routine is used to implement `\.{\\fontdimen} |n| |f|'.
+The boolean parameter |writing| is set |true| if the calling program
+intends to change the parameter value.
+
+@<Declare procedures that scan font-related stuff@>=
+static void find_font_dimen(bool @!writing)
+ /*sets |cur_val| to |font_info| location*/
+{@+internal_font_number f;
+int @!n; /*the parameter number*/
+scan_int();n=cur_val;scan_font_ident();f=cur_val;
+if (n <= 0) cur_val=fmem_ptr;
+else{@+if (writing&&(n <= space_shrink_code)&&@|
+ (n >= space_code)&&(font_glue[f]!=null))
+ {@+delete_glue_ref(font_glue[f]);
+ font_glue[f]=null;
+ }
+ if (n > font_params[f])
+ if (f < font_ptr) cur_val=fmem_ptr;
+ else@<Increase the number of parameters in the last font@>@;
+ else cur_val=n+param_base[f];
+ }
+@<Issue an error message if |cur_val=fmem_ptr|@>;
+}
+
+@ @<Issue an error message if |cur_val=fmem_ptr|@>=
+if (cur_val==fmem_ptr)
+ {@+print_err("Font ");printn_esc(font_id_text(f));
+ print(" has only ");print_int(font_params[f]);
+ print(" fontdimen parameters");
+@.Font x has only...@>
+ help2("To increase the number of font parameters, you must",@/
+ "use \\fontdimen immediately after the \\font is loaded.");
+ error();
+ }
+
+@ @<Increase the number of parameters...@>=
+{@+@/do@+{if (fmem_ptr==font_mem_size)
+ overflow("font memory", font_mem_size);
+@:TeX capacity exceeded font memory}{\quad font memory@>
+font_info[fmem_ptr].sc=0;incr(fmem_ptr);incr(font_params[f]);
+}@+ while (!(n==font_params[f]));
+cur_val=fmem_ptr-1; /*this equals |param_base[f]+font_params[f]|*/
+}
+
+@ When \TeX\ wants to typeset a character that doesn't exist, the
+character node is not created; thus the output routine can assume
+that characters exist when it sees them. The following procedure
+prints a warning message unless the user has suppressed it.
+
+@p static void char_warning(internal_font_number @!f, eight_bits @!c)
+{@+int old_setting; /*saved value of |tracing_online|*/
+if (tracing_lost_chars > 0)
+ {@+old_setting=tracing_online;
+ if (eTeX_ex&&(tracing_lost_chars > 1)) tracing_online=1;
+ {@+begin_diagnostic();
+ print_nl("Missing character: There is no ");
+@.Missing character@>
+ print_ASCII(c);print(" in font ");
+ slow_print(font_name[f]);print_char('!');end_diagnostic(false);
+ }
+ tracing_online=old_setting;
+ }
+}
+
+@ Here is a function that returns a pointer to a character node for a
+given character in a given font. If that character doesn't exist,
+|null| is returned instead.
+
+@p static pointer new_character(internal_font_number @!f, eight_bits @!c)
+{@+
+pointer p; /*newly allocated node*/
+if (font_bc[f] <= c) if (font_ec[f] >= c)
+ if (char_exists(char_info(f, qi(c))))
+ {@+p=get_avail();font(p)=f;character(p)=qi(c);
+ return p;
+ }
+char_warning(f, c);
+return null;
+}
+
+@* Device-independent file format.
+The most important output produced by a run of \TeX\ is the ``device
+independent'' (\.{DVI}) file that specifies where characters and rules
+are to appear on printed pages. The form of these files was designed by
+David R. Fuchs in 1979. Almost any reasonable typesetting device can be
+@^Fuchs, David Raymond@>
+@:DVI\_files}{\.{DVI} files@>
+driven by a program that takes \.{DVI} files as input, and dozens of such
+\.{DVI}-to-whatever programs have been written. Thus, it is possible to
+print the output of \TeX\ on many different kinds of equipment, using \TeX\
+as a device-independent ``front end.''
+
+A \.{DVI} file is a stream of 8-bit bytes, which may be regarded as a
+series of commands in a machine-like language. The first byte of each command
+is the operation code, and this code is followed by zero or more bytes
+that provide parameters to the command. The parameters themselves may consist
+of several consecutive bytes; for example, the `|set_rule|' command has two
+parameters, each of which is four bytes long. Parameters are usually
+regarded as nonnegative integers; but four-byte-long parameters,
+and shorter parameters that denote distances, can be
+either positive or negative. Such parameters are given in two's complement
+notation. For example, a two-byte-long distance parameter has a value between
+$-2^{15}$ and $2^{15}-1$. As in \.{TFM} files, numbers that occupy
+more than one byte position appear in BigEndian order.
+
+A \.{DVI} file consists of a ``preamble,'' followed by a sequence of one
+or more ``pages,'' followed by a ``postamble.'' The preamble is simply a
+|pre| command, with its parameters that define the dimensions used in the
+file; this must come first. Each ``page'' consists of a |bop| command,
+followed by any number of other commands that tell where characters are to
+be placed on a physical page, followed by an |eop| command. The pages
+appear in the order that \TeX\ generated them. If we ignore |nop| commands
+and \\{fnt\_def} commands (which are allowed between any two commands in
+the file), each |eop| command is immediately followed by a |bop| command,
+or by a |post| command; in the latter case, there are no more pages in the
+file, and the remaining bytes form the postamble. Further details about
+the postamble will be explained later.
+
+Some parameters in \.{DVI} commands are ``pointers.'' These are four-byte
+quantities that give the location number of some other byte in the file;
+the first byte is number~0, then comes number~1, and so on. For example,
+one of the parameters of a |bop| command points to the previous |bop|;
+this makes it feasible to read the pages in backwards order, in case the
+results are being directed to a device that stacks its output face up.
+Suppose the preamble of a \.{DVI} file occupies bytes 0 to 99. Now if the
+first page occupies bytes 100 to 999, say, and if the second
+page occupies bytes 1000 to 1999, then the |bop| that starts in byte 1000
+points to 100 and the |bop| that starts in byte 2000 points to 1000. (The
+very first |bop|, i.e., the one starting in byte 100, has a pointer of~$-1$.)
+
+@ The \.{DVI} format is intended to be both compact and easily interpreted
+by a machine. Compactness is achieved by making most of the information
+implicit instead of explicit. When a \.{DVI}-reading program reads the
+commands for a page, it keeps track of several quantities: (a)~The current
+font |f| is an integer; this value is changed only
+by \\{fnt} and \\{fnt\_num} commands. (b)~The current position on the page
+is given by two numbers called the horizontal and vertical coordinates,
+|h| and |v|. Both coordinates are zero at the upper left corner of the page;
+moving to the right corresponds to increasing the horizontal coordinate, and
+moving down corresponds to increasing the vertical coordinate. Thus, the
+coordinates are essentially Cartesian, except that vertical directions are
+flipped; the Cartesian version of |(h, v)| would be |(h,-v)|. (c)~The
+current spacing amounts are given by four numbers |w|, |x|, |y|, and |z|,
+where |w| and~|x| are used for horizontal spacing and where |y| and~|z|
+are used for vertical spacing. (d)~There is a stack containing
+|(h, v, w, x, y, z)| values; the \.{DVI} commands |push| and |pop| are used to
+change the current level of operation. Note that the current font~|f| is
+not pushed and popped; the stack contains only information about
+positioning.
+
+The values of |h|, |v|, |w|, |x|, |y|, and |z| are signed integers having up
+to 32 bits, including the sign. Since they represent physical distances,
+there is a small unit of measurement such that increasing |h| by~1 means
+moving a certain tiny distance to the right. The actual unit of
+measurement is variable, as explained below; \TeX\ sets things up so that
+its \.{DVI} output is in sp units, i.e., scaled points, in agreement with
+all the |scaled| dimensions in \TeX's data structures.
+
+@ Here is a list of all the commands that may appear in a \.{DVI} file. Each
+command is specified by its symbolic name (e.g., |bop|), its opcode byte
+(e.g., 139), and its parameters (if any). The parameters are followed
+by a bracketed number telling how many bytes they occupy; for example,
+`|p[4]|' means that parameter |p| is four bytes long.
+
+\yskip\hang|set_char_0| 0. Typeset character number~0 from font~|f|
+such that the reference point of the character is at |(h, v)|. Then
+increase |h| by the width of that character. Note that a character may
+have zero or negative width, so one cannot be sure that |h| will advance
+after this command; but |h| usually does increase.
+
+\yskip\hang\\{set\_char\_1} through \\{set\_char\_127} (opcodes 1 to 127).
+Do the operations of |set_char_0|; but use the character whose number
+matches the opcode, instead of character~0.
+
+\yskip\hang|set1| 128 |c[1]|. Same as |set_char_0|, except that character
+number~|c| is typeset. \TeX82 uses this command for characters in the
+range |128 <= c < 256|.
+
+\yskip\hang|@!set2| 129 |c[2]|. Same as |set1|, except that |c|~is two
+bytes long, so it is in the range |0 <= c < 65536|. \TeX82 never uses this
+command, but it should come in handy for extensions of \TeX\ that deal
+with oriental languages.
+@^oriental characters@>@^Chinese characters@>@^Japanese characters@>
+
+\yskip\hang|@!set3| 130 |c[3]|. Same as |set1|, except that |c|~is three
+bytes long, so it can be as large as $2^{24}-1$. Not even the Chinese
+language has this many characters, but this command might prove useful
+in some yet unforeseen extension.
+
+\yskip\hang|@!set4| 131 |c[4]|. Same as |set1|, except that |c|~is four
+bytes long. Imagine that.
+
+\yskip\hang|set_rule| 132 |a[4]| |b[4]|. Typeset a solid black rectangle
+of height~|a| and width~|b|, with its bottom left corner at |(h, v)|. Then
+set |h=h+b|. If either |a <= 0| or |b <= 0|, nothing should be typeset. Note
+that if |b < 0|, the value of |h| will decrease even though nothing else happens.
+See below for details about how to typeset rules so that consistency with
+\MF\ is guaranteed.
+
+\yskip\hang|@!put1| 133 |c[1]|. Typeset character number~|c| from font~|f|
+such that the reference point of the character is at |(h, v)|. (The `put'
+commands are exactly like the `set' commands, except that they simply put out a
+character or a rule without moving the reference point afterwards.)
+
+\yskip\hang|@!put2| 134 |c[2]|. Same as |set2|, except that |h| is not changed.
+
+\yskip\hang|@!put3| 135 |c[3]|. Same as |set3|, except that |h| is not changed.
+
+\yskip\hang|@!put4| 136 |c[4]|. Same as |set4|, except that |h| is not changed.
+
+\yskip\hang|put_rule| 137 |a[4]| |b[4]|. Same as |set_rule|, except that
+|h| is not changed.
+
+\yskip\hang|nop| 138. No operation, do nothing. Any number of |nop|'s
+may occur between \.{DVI} commands, but a |nop| cannot be inserted between
+a command and its parameters or between two parameters.
+
+\yskip\hang|bop| 139 $c_0[4]$ $c_1[4]$ $\ldots$ $c_9[4]$ $p[4]$. Beginning
+of a page: Set |(h, v, w, x, y, z)=(0, 0, 0, 0, 0, 0)| and set the stack empty. Set
+the current font |f| to an undefined value. The ten $c_i$ parameters hold
+the values of \.{\\count0} $\ldots$ \.{\\count9} in \TeX\ at the time
+\.{\\shipout} was invoked for this page; they can be used to identify
+pages, if a user wants to print only part of a \.{DVI} file. The parameter
+|p| points to the previous |bop| in the file; the first
+|bop| has $p=-1$.
+
+\yskip\hang|eop| 140. End of page: Print what you have read since the
+previous |bop|. At this point the stack should be empty. (The \.{DVI}-reading
+programs that drive most output devices will have kept a buffer of the
+material that appears on the page that has just ended. This material is
+largely, but not entirely, in order by |v| coordinate and (for fixed |v|) by
+|h|~coordinate; so it usually needs to be sorted into some order that is
+appropriate for the device in question.)
+
+\yskip\hang|push| 141. Push the current values of |(h, v, w, x, y, z)| onto the
+top of the stack; do not change any of these values. Note that |f| is
+not pushed.
+
+\yskip\hang|pop| 142. Pop the top six values off of the stack and assign
+them respectively to |(h, v, w, x, y, z)|. The number of pops should never
+exceed the number of pushes, since it would be highly embarrassing if the
+stack were empty at the time of a |pop| command.
+
+\yskip\hang|right1| 143 |b[1]|. Set |h=h+b|, i.e., move right |b| units.
+The parameter is a signed number in two's complement notation, |-128 <= b < 128|;
+if |b < 0|, the reference point moves left.
+
+\yskip\hang|@!right2| 144 |b[2]|. Same as |right1|, except that |b| is a
+two-byte quantity in the range |-32768 <= b < 32768|.
+
+\yskip\hang|@!right3| 145 |b[3]|. Same as |right1|, except that |b| is a
+three-byte quantity in the range |@t$-2^{23}$@> <= b < @t$2^{23}$@>|.
+
+\yskip\hang|@!right4| 146 |b[4]|. Same as |right1|, except that |b| is a
+four-byte quantity in the range |@t$-2^{31}$@> <= b < @t$2^{31}$@>|.
+
+\yskip\hang|w0| 147. Set |h=h+w|; i.e., move right |w| units. With luck,
+this parameterless command will usually suffice, because the same kind of motion
+will occur several times in succession; the following commands explain how
+|w| gets particular values.
+
+\yskip\hang|w1| 148 |b[1]|. Set |w=b| and |h=h+b|. The value of |b| is a
+signed quantity in two's complement notation, |-128 <= b < 128|. This command
+changes the current |w|~spacing and moves right by |b|.
+
+\yskip\hang|@!w2| 149 |b[2]|. Same as |w1|, but |b| is two bytes long,
+|-32768 <= b < 32768|.
+
+\yskip\hang|@!w3| 150 |b[3]|. Same as |w1|, but |b| is three bytes long,
+|@t$-2^{23}$@> <= b < @t$2^{23}$@>|.
+
+\yskip\hang|@!w4| 151 |b[4]|. Same as |w1|, but |b| is four bytes long,
+|@t$-2^{31}$@> <= b < @t$2^{31}$@>|.
+
+\yskip\hang|x0| 152. Set |h=h+x|; i.e., move right |x| units. The `|x|'
+commands are like the `|w|' commands except that they involve |x| instead
+of |w|.
+
+\yskip\hang|x1| 153 |b[1]|. Set |x=b| and |h=h+b|. The value of |b| is a
+signed quantity in two's complement notation, |-128 <= b < 128|. This command
+changes the current |x|~spacing and moves right by |b|.
+
+\yskip\hang|@!x2| 154 |b[2]|. Same as |x1|, but |b| is two bytes long,
+|-32768 <= b < 32768|.
+
+\yskip\hang|@!x3| 155 |b[3]|. Same as |x1|, but |b| is three bytes long,
+|@t$-2^{23}$@> <= b < @t$2^{23}$@>|.
+
+\yskip\hang|@!x4| 156 |b[4]|. Same as |x1|, but |b| is four bytes long,
+|@t$-2^{31}$@> <= b < @t$2^{31}$@>|.
+
+\yskip\hang|down1| 157 |a[1]|. Set |v=v+a|, i.e., move down |a| units.
+The parameter is a signed number in two's complement notation, |-128 <= a < 128|;
+if |a < 0|, the reference point moves up.
+
+\yskip\hang|@!down2| 158 |a[2]|. Same as |down1|, except that |a| is a
+two-byte quantity in the range |-32768 <= a < 32768|.
+
+\yskip\hang|@!down3| 159 |a[3]|. Same as |down1|, except that |a| is a
+three-byte quantity in the range |@t$-2^{23}$@> <= a < @t$2^{23}$@>|.
+
+\yskip\hang|@!down4| 160 |a[4]|. Same as |down1|, except that |a| is a
+four-byte quantity in the range |@t$-2^{31}$@> <= a < @t$2^{31}$@>|.
+
+\yskip\hang|y0| 161. Set |v=v+y|; i.e., move down |y| units. With luck,
+this parameterless command will usually suffice, because the same kind of motion
+will occur several times in succession; the following commands explain how
+|y| gets particular values.
+
+\yskip\hang|y1| 162 |a[1]|. Set |y=a| and |v=v+a|. The value of |a| is a
+signed quantity in two's complement notation, |-128 <= a < 128|. This command
+changes the current |y|~spacing and moves down by |a|.
+
+\yskip\hang|@!y2| 163 |a[2]|. Same as |y1|, but |a| is two bytes long,
+|-32768 <= a < 32768|.
+
+\yskip\hang|@!y3| 164 |a[3]|. Same as |y1|, but |a| is three bytes long,
+|@t$-2^{23}$@> <= a < @t$2^{23}$@>|.
+
+\yskip\hang|@!y4| 165 |a[4]|. Same as |y1|, but |a| is four bytes long,
+|@t$-2^{31}$@> <= a < @t$2^{31}$@>|.
+
+\yskip\hang|z0| 166. Set |v=v+z|; i.e., move down |z| units. The `|z|' commands
+are like the `|y|' commands except that they involve |z| instead of |y|.
+
+\yskip\hang|z1| 167 |a[1]|. Set |z=a| and |v=v+a|. The value of |a| is a
+signed quantity in two's complement notation, |-128 <= a < 128|. This command
+changes the current |z|~spacing and moves down by |a|.
+
+\yskip\hang|@!z2| 168 |a[2]|. Same as |z1|, but |a| is two bytes long,
+|-32768 <= a < 32768|.
+
+\yskip\hang|@!z3| 169 |a[3]|. Same as |z1|, but |a| is three bytes long,
+|@t$-2^{23}$@> <= a < @t$2^{23}$@>|.
+
+\yskip\hang|@!z4| 170 |a[4]|. Same as |z1|, but |a| is four bytes long,
+|@t$-2^{31}$@> <= a < @t$2^{31}$@>|.
+
+\yskip\hang|fnt_num_0| 171. Set |f=0|. Font 0 must previously have been
+defined by a \\{fnt\_def} instruction, as explained below.
+
+\yskip\hang\\{fnt\_num\_1} through \\{fnt\_num\_63} (opcodes 172 to 234). Set
+|f=1|, \dots, \hbox{|f=63|}, respectively.
+
+\yskip\hang|fnt1| 235 |k[1]|. Set |f=k|. \TeX82 uses this command for font
+numbers in the range |64 <= k < 256|.
+
+\yskip\hang|@!fnt2| 236 |k[2]|. Same as |fnt1|, except that |k|~is two
+bytes long, so it is in the range |0 <= k < 65536|. \TeX82 never generates this
+command, but large font numbers may prove useful for specifications of
+color or texture, or they may be used for special fonts that have fixed
+numbers in some external coding scheme.
+
+\yskip\hang|@!fnt3| 237 |k[3]|. Same as |fnt1|, except that |k|~is three
+bytes long, so it can be as large as $2^{24}-1$.
+
+\yskip\hang|@!fnt4| 238 |k[4]|. Same as |fnt1|, except that |k|~is four
+bytes long; this is for the really big font numbers (and for the negative ones).
+
+\yskip\hang|xxx1| 239 |k[1]| |x[k]|. This command is undefined in
+general; it functions as a $(k+2)$-byte |nop| unless special \.{DVI}-reading
+programs are being used. \TeX82 generates |xxx1| when a short enough
+\.{\\special} appears, setting |k| to the number of bytes being sent. It
+is recommended that |x| be a string having the form of a keyword followed
+by possible parameters relevant to that keyword.
+
+\yskip\hang|@!xxx2| 240 |k[2]| |x[k]|. Like |xxx1|, but |0 <= k < 65536|.
+
+\yskip\hang|@!xxx3| 241 |k[3]| |x[k]|. Like |xxx1|, but |0 <= k < @t$2^{24}$@>|.
+
+\yskip\hang|xxx4| 242 |k[4]| |x[k]|. Like |xxx1|, but |k| can be ridiculously
+large. \TeX82 uses |xxx4| when sending a string of length 256 or more.
+
+\yskip\hang|fnt_def1| 243 |k[1]| |c[4]| |s[4]| |d[4]| |a[1]| |l[1]| |n[a+l]|.
+Define font |k|, where |0 <= k < 256|; font definitions will be explained shortly.
+
+\yskip\hang|@!fnt_def2| 244 |k[2]| |c[4]| |s[4]| |d[4]| |a[1]| |l[1]| |n[a+l]|.
+Define font |k|, where |0 <= k < 65536|.
+
+\yskip\hang|@!fnt_def3| 245 |k[3]| |c[4]| |s[4]| |d[4]| |a[1]| |l[1]| |n[a+l]|.
+Define font |k|, where |0 <= k < @t$2^{24}$@>|.
+
+\yskip\hang|@!fnt_def4| 246 |k[4]| |c[4]| |s[4]| |d[4]| |a[1]| |l[1]| |n[a+l]|.
+Define font |k|, where |@t$-2^{31}$@> <= k < @t$2^{31}$@>|.
+
+\yskip\hang|pre| 247 |i[1]| |num[4]| |den[4]| |mag[4]| |k[1]| |x[k]|.
+Beginning of the preamble; this must come at the very beginning of the
+file. Parameters |i|, |num|, |den|, |mag|, |k|, and |x| are explained below.
+
+\yskip\hang|post| 248. Beginning of the postamble, see below.
+
+\yskip\hang|post_post| 249. Ending of the postamble, see below.
+
+\yskip\noindent Commands 250--255 are undefined at the present time.
+
+@ @d set_char_0 0 /*typeset character 0 and move right*/
+@d set1 128 /*typeset a character and move right*/
+@d set_rule 132 /*typeset a rule and move right*/
+@d put_rule 137 /*typeset a rule*/
+@d nop 138 /*no operation*/
+@d bop 139 /*beginning of page*/
+@d eop 140 /*ending of page*/
+@d push 141 /*save the current positions*/
+@d pop 142 /*restore previous positions*/
+@d right1 143 /*move right*/
+@d w0 147 /*move right by |w|*/
+@d w1 148 /*move right and set |w|*/
+@d x0 152 /*move right by |x|*/
+@d x1 153 /*move right and set |x|*/
+@d down1 157 /*move down*/
+@d y0 161 /*move down by |y|*/
+@d y1 162 /*move down and set |y|*/
+@d z0 166 /*move down by |z|*/
+@d z1 167 /*move down and set |z|*/
+@d fnt_num_0 171 /*set current font to 0*/
+@d fnt1 235 /*set current font*/
+@d xxx1 239 /*extension to \.{DVI} primitives*/
+@d xxx4 242 /*potentially long extension to \.{DVI} primitives*/
+@d fnt_def1 243 /*define the meaning of a font number*/
+@d pre 247 /*preamble*/
+@d post 248 /*postamble beginning*/
+@d post_post 249 /*postamble ending*/
+
+@ The preamble contains basic information about the file as a whole. As
+stated above, there are six parameters:
+$$\hbox{|@!i[1]| |@!num[4]| |@!den[4]| |@!mag[4]| |@!k[1]| |@!x[k]|.}$$
+The |i| byte identifies \.{DVI} format; currently this byte is always set
+to~2. (The value |i==3| is currently used for an extended format that
+allows a mixture of right-to-left and left-to-right typesetting.
+Some day we will set |i==4|, when \.{DVI} format makes another
+incompatible change---perhaps in the year 2048.)
+
+The next two parameters, |num| and |den|, are positive integers that define
+the units of measurement; they are the numerator and denominator of a
+fraction by which all dimensions in the \.{DVI} file could be multiplied
+in order to get lengths in units of $10^{-7}$ meters. Since $\rm 7227{pt} =
+254{cm}$, and since \TeX\ works with scaled points where there are $2^{16}$
+sp in a point, \TeX\ sets
+$|num|/|den|=(254\cdot10^5)/(7227\cdot2^{16})=25400000/473628672$.
+@^sp@>
+
+The |mag| parameter is what \TeX\ calls \.{\\mag}, i.e., 1000 times the
+desired magnification. The actual fraction by which dimensions are
+multiplied is therefore $|mag|\cdot|num|/1000|den|$. Note that if a \TeX\
+source document does not call for any `\.{true}' dimensions, and if you
+change it only by specifying a different \.{\\mag} setting, the \.{DVI}
+file that \TeX\ creates will be completely unchanged except for the value
+of |mag| in the preamble and postamble. (Fancy \.{DVI}-reading programs allow
+users to override the |mag|~setting when a \.{DVI} file is being printed.)
+
+Finally, |k| and |x| allow the \.{DVI} writer to include a comment, which is not
+interpreted further. The length of comment |x| is |k|, where |0 <= k < 256|.
+
+@d id_byte 2 /*identifies the kind of \.{DVI} files described here*/
+
+@ Font definitions for a given font number |k| contain further parameters
+$$\hbox{|c[4]| |s[4]| |d[4]| |a[1]| |l[1]| |n[a+l]|.}$$
+The four-byte value |c| is the check sum that \TeX\ found in the \.{TFM}
+file for this font; |c| should match the check sum of the font found by
+programs that read this \.{DVI} file.
+@^check sum@>
+
+Parameter |s| contains a fixed-point scale factor that is applied to
+the character widths in font |k|; font dimensions in \.{TFM} files and
+other font files are relative to this quantity, which is called the
+``at size'' elsewhere in this documentation. The value of |s| is
+always positive and less than $2^{27}$. It is given in the same units
+as the other \.{DVI} dimensions, i.e., in sp when \TeX82 has made the
+file. Parameter |d| is similar to |s|; it is the ``design size,'' and
+(like~|s|) it is given in \.{DVI} units. Thus, font |k| is to be used
+at $|mag|\cdot s/1000d$ times its normal size.
+
+The remaining part of a font definition gives the external name of the font,
+which is an ASCII string of length |a+l|. The number |a| is the length
+of the ``area'' or directory, and |l| is the length of the font name itself;
+the standard local system font area is supposed to be used when |a==0|.
+The |n| field contains the area in its first |a| bytes.
+
+Font definitions must appear before the first use of a particular font number.
+Once font |k| is defined, it must not be defined again; however, we
+shall see below that font definitions appear in the postamble as well as
+in the pages, so in this sense each font number is defined exactly twice,
+if at all. Like |nop| commands, font definitions can
+appear before the first |bop|, or between an |eop| and a |bop|.
+
+@ Sometimes it is desirable to make horizontal or vertical rules line up
+precisely with certain features in characters of a font. It is possible to
+guarantee the correct matching between \.{DVI} output and the characters
+generated by \MF\ by adhering to the following principles: (1)~The \MF\
+characters should be positioned so that a bottom edge or left edge that is
+supposed to line up with the bottom or left edge of a rule appears at the
+reference point, i.e., in row~0 and column~0 of the \MF\ raster. This
+ensures that the position of the rule will not be rounded differently when
+the pixel size is not a perfect multiple of the units of measurement in
+the \.{DVI} file. (2)~A typeset rule of height $a>0$ and width $b>0$
+should be equivalent to a \MF-generated character having black pixels in
+precisely those raster positions whose \MF\ coordinates satisfy
+|0 <= x < @t$\alpha$@>b| and |0 <= y < @t$\alpha$@>a|, where $\alpha$ is the number
+of pixels per \.{DVI} unit.
+@:METAFONT}{\MF@>
+@^alignment of rules with characters@>
+@^rules aligning with characters@>
+
+@ The last page in a \.{DVI} file is followed by `|post|'; this command
+introduces the postamble, which summarizes important facts that \TeX\ has
+accumulated about the file, making it possible to print subsets of the data
+with reasonable efficiency. The postamble has the form
+$$\vbox{\halign{\hbox{#\hfil}\cr
+ |post| |p[4]| |num[4]| |den[4]| |mag[4]| |l[4]| |u[4]| |s[2]| |t[2]|\cr
+ $\langle\,$font definitions$\,\rangle$\cr
+ |post_post| |q[4]| |i[1]| 223's$[{\G}4]$\cr}}$$
+Here |p| is a pointer to the final |bop| in the file. The next three
+parameters, |num|, |den|, and |mag|, are duplicates of the quantities that
+appeared in the preamble.
+
+Parameters |l| and |u| give respectively the height-plus-depth of the tallest
+page and the width of the widest page, in the same units as other dimensions
+of the file. These numbers might be used by a \.{DVI}-reading program to
+position individual ``pages'' on large sheets of film or paper; however,
+the standard convention for output on normal size paper is to position each
+page so that the upper left-hand corner is exactly one inch from the left
+and the top. Experience has shown that it is unwise to design \.{DVI}-to-printer
+software that attempts cleverly to center the output; a fixed position of
+the upper left corner is easiest for users to understand and to work with.
+Therefore |l| and~|u| are often ignored.
+
+Parameter |s| is the maximum stack depth (i.e., the largest excess of
+|push| commands over |pop| commands) needed to process this file. Then
+comes |t|, the total number of pages (|bop| commands) present.
+
+The postamble continues with font definitions, which are any number of
+\\{fnt\_def} commands as described above, possibly interspersed with |nop|
+commands. Each font number that is used in the \.{DVI} file must be defined
+exactly twice: Once before it is first selected by a \\{fnt} command, and once
+in the postamble.
+
+@ The last part of the postamble, following the |post_post| byte that
+signifies the end of the font definitions, contains |q|, a pointer to the
+|post| command that started the postamble. An identification byte, |i|,
+comes next; this currently equals~2, as in the preamble.
+
+The |i| byte is followed by four or more bytes that are all equal to
+the decimal number 223 (i.e., 0337 in octal). \TeX\ puts out four to seven of
+these trailing bytes, until the total length of the file is a multiple of
+four bytes, since this works out best on machines that pack four bytes per
+word; but any number of 223's is allowed, as long as there are at least four
+of them. In effect, 223 is a sort of signature that is added at the very end.
+@^Fuchs, David Raymond@>
+
+This curious way to finish off a \.{DVI} file makes it feasible for
+\.{DVI}-reading programs to find the postamble first, on most computers,
+even though \TeX\ wants to write the postamble last. Most operating
+systems permit random access to individual words or bytes of a file, so
+the \.{DVI} reader can start at the end and skip backwards over the 223's
+until finding the identification byte. Then it can back up four bytes, read
+|q|, and move to byte |q| of the file. This byte should, of course,
+contain the value 248 (|post|); now the postamble can be read, so the
+\.{DVI} reader can discover all the information needed for typesetting the
+pages. Note that it is also possible to skip through the \.{DVI} file at
+reasonably high speed to locate a particular page, if that proves
+desirable. This saves a lot of time, since \.{DVI} files used in production
+jobs tend to be large.
+
+Unfortunately, however, standard \PASCAL\ does not include the ability to
+@^system dependencies@>
+access a random position in a file, or even to determine the length of a file.
+Almost all systems nowadays provide the necessary capabilities, so \.{DVI}
+format has been designed to work most efficiently with modern operating systems.
+But if \.{DVI} files have to be processed under the restrictions of standard
+\PASCAL, one can simply read them from front to back, since the necessary
+header information is present in the preamble and in the font definitions.
+(The |l| and |u| and |s| and |t| parameters, which appear only in the
+postamble, are ``frills'' that are handy but not absolutely necessary.)
+
+@* Shipping pages out.
+After considering \TeX's eyes and stomach, we come now to the bowels.
+@^bowels@>
+
+The |ship_out| procedure is given a pointer to a box; its mission is
+to describe that box in \.{DVI} form, outputting a ``page'' to |dvi_file|.
+The \.{DVI} coordinates $(h,v)=(0,0)$ should correspond to the upper left
+corner of the box being shipped.
+
+Since boxes can be inside of boxes inside of boxes, the main work of
+|ship_out| is done by two mutually recursive routines, |hlist_out|
+and |vlist_out|, which traverse the hlists and vlists inside of horizontal
+and vertical boxes.
+
+As individual pages are being processed, we need to accumulate
+information about the entire set of pages, since such statistics must be
+reported in the postamble. The global variables |total_pages|, |max_v|,
+|max_h|, |max_push|, and |last_bop| are used to record this information.
+
+The variable |doing_leaders| is |true| while leaders are being output.
+The variable |dead_cycles| contains the number of times an output routine
+has been initiated since the last |ship_out|.
+
+A few additional global variables are also defined here for use in
+|vlist_out| and |hlist_out|. They could have been local variables, but
+that would waste stack space when boxes are deeply nested, since the
+values of these variables are not needed during recursive calls.
+@^recursion@>
+
+@<Glob...@>=
+static int @!total_pages; /*the number of pages that have been shipped out*/
+static scaled @!max_v; /*maximum height-plus-depth of pages shipped so far*/
+static scaled @!max_h; /*maximum width of pages shipped so far*/
+static int @!max_push; /*deepest nesting of |push| commands encountered so far*/
+static int @!last_bop; /*location of previous |bop| in the \.{DVI} output*/
+static int @!dead_cycles; /*recent outputs that didn't ship anything out*/
+static bool @!doing_leaders; /*are we inside a leader box?*/
+@#
+static quarterword @!c, @!f; /*character and font in current |char_node|*/
+static scaled @!rule_ht, @!rule_dp, @!rule_wd; /*size of current rule being output*/
+static pointer @!g; /*current glue specification*/
+static int @!lq, @!lr; /*quantities used in calculations for leaders*/
+
+@ @<Set init...@>=
+total_pages=0;max_v=0;max_h=0;max_push=0;last_bop=-1;
+doing_leaders=false;dead_cycles=0;cur_s=-1;
+
+@ The \.{DVI} bytes are output to a buffer instead of being written directly
+to the output file. This makes it possible to reduce the overhead of
+subroutine calls, thereby measurably speeding up the computation, since
+output of \.{DVI} bytes is part of \TeX's inner loop. And it has another
+advantage as well, since we can change instructions in the buffer in order to
+make the output more compact. For example, a `|down2|' command can be
+changed to a `|y2|', thereby making a subsequent `|y0|' command possible,
+saving two bytes.
+
+The output buffer is divided into two parts of equal size; the bytes found
+in |dvi_buf[0 dotdot half_buf-1]| constitute the first half, and those in
+|dvi_buf[half_buf dotdot dvi_buf_size-1]| constitute the second. The global
+variable |dvi_ptr| points to the position that will receive the next
+output byte. When |dvi_ptr| reaches |dvi_limit|, which is always equal
+to one of the two values |half_buf| or |dvi_buf_size|, the half buffer that
+is about to be invaded next is sent to the output and |dvi_limit| is
+changed to its other value. Thus, there is always at least a half buffer's
+worth of information present, except at the very beginning of the job.
+
+Bytes of the \.{DVI} file are numbered sequentially starting with 0;
+the next byte to be generated will be number |dvi_offset+dvi_ptr|.
+A byte is present in the buffer only if its number is | >= dvi_gone|.
+
+@<Types...@>=
+typedef int16_t dvi_index; /*an index into the output buffer*/
+
+@ Some systems may find it more efficient to make |dvi_buf| a ||
+array, since output of four bytes at once may be facilitated.
+@^system dependencies@>
+
+@<Glob...@>=
+static eight_bits @!dvi_buf[dvi_buf_size+1]; /*buffer for \.{DVI} output*/
+static dvi_index @!half_buf; /*half of |dvi_buf_size|*/
+static dvi_index @!dvi_limit; /*end of the current half buffer*/
+static dvi_index @!dvi_ptr; /*the next available buffer address*/
+static int @!dvi_offset; /*|dvi_buf_size| times the number of times the
+ output buffer has been fully emptied*/
+static int @!dvi_gone; /*the number of bytes already output to |dvi_file|*/
+
+@ Initially the buffer is all in one piece; we will output half of it only
+after it first fills up.
+
+@<Set init...@>=
+half_buf=dvi_buf_size/2;dvi_limit=dvi_buf_size;dvi_ptr=0;
+dvi_offset=0;dvi_gone=0;
+
+@ The actual output of |dvi_buf[a dotdot b]| to |dvi_file| is performed by calling
+|write_dvi(a, b)|. For best results, this procedure should be optimized to
+run as fast as possible on each particular system, since it is part of
+\TeX's inner loop. It is safe to assume that |a| and |b+1| will both be
+multiples of 4 when |write_dvi(a, b)| is called; therefore it is possible on
+many machines to use efficient methods to pack four bytes per word and to
+output an array of words with one system call.
+@^system dependencies@>
+@^inner loop@>
+@^defecation@>
+
+@p static void write_dvi(dvi_index @!a, dvi_index @!b)
+{@+int k;
+for (k=a; k<=b; k++) pascal_write(dvi_file, "%c", dvi_buf[k]);
+}
+
+@ To put a byte in the buffer without paying the cost of invoking a procedure
+each time, we use the macro |dvi_out|.
+
+@d dvi_out(A) @+{@+dvi_buf[dvi_ptr]=A;incr(dvi_ptr);
+ if (dvi_ptr==dvi_limit) dvi_swap();
+ }
+
+@p static void dvi_swap(void) /*outputs half of the buffer*/
+{@+if (dvi_limit==dvi_buf_size)
+ {@+write_dvi(0, half_buf-1);dvi_limit=half_buf;
+ dvi_offset=dvi_offset+dvi_buf_size;dvi_ptr=0;
+ }
+else{@+write_dvi(half_buf, dvi_buf_size-1);dvi_limit=dvi_buf_size;
+ }
+dvi_gone=dvi_gone+half_buf;
+}
+
+@ Here is how we clean out the buffer when \TeX\ is all through; |dvi_ptr|
+will be a multiple of~4.
+
+@<Empty the last bytes out of |dvi_buf|@>=
+if (dvi_limit==half_buf) write_dvi(half_buf, dvi_buf_size-1);
+if (dvi_ptr > 0) write_dvi(0, dvi_ptr-1)
+
+@ The |dvi_four| procedure outputs four bytes in two's complement notation,
+without risking arithmetic overflow.
+
+@p static void dvi_four(int @!x)
+{@+if (x >= 0) dvi_out(x/0100000000)@;
+else{@+x=x+010000000000;
+ x=x+010000000000;
+ dvi_out((x/0100000000)+128);
+ }
+x=x%0100000000;dvi_out(x/0200000);
+x=x%0200000;dvi_out(x/0400);
+dvi_out(x%0400);
+}
+
+@ A mild optimization of the output is performed by the |dvi_pop|
+routine, which issues a |pop| unless it is possible to cancel a
+`|push| |pop|' pair. The parameter to |dvi_pop| is the byte address
+following the old |push| that matches the new |pop|.
+
+@p static void dvi_pop(int @!l)
+{@+if ((l==dvi_offset+dvi_ptr)&&(dvi_ptr > 0)) decr(dvi_ptr);
+else dvi_out(pop);
+}
+
+@ Here's a procedure that outputs a font definition. Since \TeX82 uses at
+most 256 different fonts per job, |fnt_def1| is always used as the command code.
+
+@p static void dvi_font_def(internal_font_number @!f)
+{@+int k; /*index into |str_pool|*/
+dvi_out(fnt_def1);
+dvi_out(f-font_base-1);@/
+dvi_out(qo(font_check[f].b0));
+dvi_out(qo(font_check[f].b1));
+dvi_out(qo(font_check[f].b2));
+dvi_out(qo(font_check[f].b3));@/
+dvi_four(font_size[f]);
+dvi_four(font_dsize[f]);@/
+dvi_out(length(font_area[f]));
+dvi_out(length(font_name[f]));
+@<Output the font name whose internal number is |f|@>;
+}
+
+@ @<Output the font name whose internal number is |f|@>=
+for (k=str_start[font_area[f]]; k<=str_start[font_area[f]+1]-1; k++)
+ dvi_out(so(str_pool[k]));
+for (k=str_start[font_name[f]]; k<=str_start[font_name[f]+1]-1; k++)
+ dvi_out(so(str_pool[k]))
+
+@ Versions of \TeX\ intended for small computers might well choose to omit
+the ideas in the next few parts of this program, since it is not really
+necessary to optimize the \.{DVI} code by making use of the |w0|, |x0|,
+|y0|, and |z0| commands. Furthermore, the algorithm that we are about to
+describe does not pretend to give an optimum reduction in the length
+of the \.{DVI} code; after all, speed is more important than compactness.
+But the method is surprisingly effective, and it takes comparatively little
+time.
+
+We can best understand the basic idea by first considering a simpler problem
+that has the same essential characteristics. Given a sequence of digits,
+say $3\,1\,4\,1\,5\,9\,2\,6\,5\,3\,5\,8\,9$, we want to assign subscripts
+$d$, $y$, or $z$ to each digit so as to maximize the number of ``$y$-hits''
+and ``$z$-hits''; a $y$-hit is an instance of two appearances of the same
+digit with the subscript $y$, where no $y$'s intervene between the two
+appearances, and a $z$-hit is defined similarly. For example, the sequence
+above could be decorated with subscripts as follows:
+$$3_z\,1_y\,4_d\,1_y\,5_y\,9_d\,2_d\,6_d\,5_y\,3_z\,5_y\,8_d\,9_d.$$
+There are three $y$-hits ($1_y\ldots1_y$ and $5_y\ldots5_y\ldots5_y$) and
+one $z$-hit ($3_z\ldots3_z$); there are no $d$-hits, since the two appearances
+of $9_d$ have $d$'s between them, but we don't count $d$-hits so it doesn't
+matter how many there are. These subscripts are analogous to the \.{DVI}
+commands called \\{down}, $y$, and $z$, and the digits are analogous to
+different amounts of vertical motion; a $y$-hit or $z$-hit corresponds to
+the opportunity to use the one-byte commands |y0| or |z0| in a \.{DVI} file.
+
+\TeX's method of assigning subscripts works like this: Append a new digit,
+say $\delta$, to the right of the sequence. Now look back through the
+sequence until one of the following things happens: (a)~You see
+$\delta_y$ or $\delta_z$, and this was the first time you encountered a
+$y$ or $z$ subscript, respectively. Then assign $y$ or $z$ to the new
+$\delta$; you have scored a hit. (b)~You see $\delta_d$, and no $y$
+subscripts have been encountered so far during this search. Then change
+the previous $\delta_d$ to $\delta_y$ (this corresponds to changing a
+command in the output buffer), and assign $y$ to the new $\delta$; it's
+another hit. (c)~You see $\delta_d$, and a $y$ subscript has been seen
+but not a $z$. Change the previous $\delta_d$ to $\delta_z$ and assign
+$z$ to the new $\delta$. (d)~You encounter both $y$ and $z$ subscripts
+before encountering a suitable $\delta$, or you scan all the way to the
+front of the sequence. Assign $d$ to the new $\delta$; this assignment may
+be changed later.
+
+The subscripts $3_z\,1_y\,4_d\ldots\,$ in the example above were, in fact,
+produced by this procedure, as the reader can verify. (Go ahead and try it.)
+
+@ In order to implement such an idea, \TeX\ maintains a stack of pointers
+to the \\{down}, $y$, and $z$ commands that have been generated for the
+current page. And there is a similar stack for \\{right}, |w|, and |x|
+commands. These stacks are called the down stack and right stack, and their
+top elements are maintained in the variables |down_ptr| and |right_ptr|.
+
+Each entry in these stacks contains four fields: The |width| field is
+the amount of motion down or to the right; the |location| field is the
+byte number of the \.{DVI} command in question (including the appropriate
+|dvi_offset|); the |link| field points to the next item below this one
+on the stack; and the |info| field encodes the options for possible change
+in the \.{DVI} command.
+
+@d movement_node_size 3 /*number of words per entry in the down and right stacks*/
+@d location(A) mem[A+2].i /*\.{DVI} byte number for a movement command*/
+
+@<Glob...@>=
+static pointer @!down_ptr, @!right_ptr; /*heads of the down and right stacks*/
+
+@ @<Set init...@>=
+down_ptr=null;right_ptr=null;
+
+@ Here is a subroutine that produces a \.{DVI} command for some specified
+downward or rightward motion. It has two parameters: |w| is the amount
+of motion, and |o| is either |down1| or |right1|. We use the fact that
+the command codes have convenient arithmetic properties: |y1-down1==w1-right1|
+and |z1-down1==x1-right1|.
+
+@p static void movement(scaled @!w, eight_bits @!o)
+{@+
+small_number mstate; /*have we seen a |y| or |z|?*/
+pointer @!p, @!q; /*current and top nodes on the stack*/
+int @!k; /*index into |dvi_buf|, modulo |dvi_buf_size|*/
+q=get_node(movement_node_size); /*new node for the top of the stack*/
+width(q)=w;location(q)=dvi_offset+dvi_ptr;
+if (o==down1)
+ {@+link(q)=down_ptr;down_ptr=q;
+ }
+else{@+link(q)=right_ptr;right_ptr=q;
+ }
+@<Look at the other stack entries until deciding what sort of \.{DVI} command
+to generate; |goto found| if node |p| is a ``hit''@>;
+@<Generate a |down| or |right| command for |w| and |return|@>;
+found: @<Generate a |y0| or |z0| command in order to reuse a previous appearance
+of~|w|@>;
+}
+
+@ The |info| fields in the entries of the down stack or the right stack
+have six possible settings: |y_here| or |z_here| mean that the \.{DVI}
+command refers to |y| or |z|, respectively (or to |w| or |x|, in the
+case of horizontal motion); |yz_OK| means that the \.{DVI} command is
+\\{down} (or \\{right}) but can be changed to either |y| or |z| (or
+to either |w| or |x|); |y_OK| means that it is \\{down} and can be changed
+to |y| but not |z|; |z_OK| is similar; and |d_fixed| means it must stay
+\\{down}.
+
+The four settings |yz_OK|, |y_OK|, |z_OK|, |d_fixed| would not need to
+be distinguished from each other if we were simply solving the
+digit-subscripting problem mentioned above. But in \TeX's case there is
+a complication because of the nested structure of |push| and |pop|
+commands. Suppose we add parentheses to the digit-subscripting problem,
+redefining hits so that $\delta_y\ldots \delta_y$ is a hit if all $y$'s between
+the $\delta$'s are enclosed in properly nested parentheses, and if the
+parenthesis level of the right-hand $\delta_y$ is deeper than or equal to
+that of the left-hand one. Thus, `(' and `)' correspond to `|push|'
+and `|pop|'. Now if we want to assign a subscript to the final 1 in the
+sequence
+$$2_y\,7_d\,1_d\,(\,8_z\,2_y\,8_z\,)\,1$$
+we cannot change the previous $1_d$ to $1_y$, since that would invalidate
+the $2_y\ldots2_y$ hit. But we can change it to $1_z$, scoring a hit
+since the intervening $8_z$'s are enclosed in parentheses.
+
+The program below removes movement nodes that are introduced after a |push|,
+before it outputs the corresponding |pop|.
+
+@d y_here 1 /*|info| when the movement entry points to a |y| command*/
+@d z_here 2 /*|info| when the movement entry points to a |z| command*/
+@d yz_OK 3 /*|info| corresponding to an unconstrained \\{down} command*/
+@d y_OK 4 /*|info| corresponding to a \\{down} that can't become a |z|*/
+@d z_OK 5 /*|info| corresponding to a \\{down} that can't become a |y|*/
+@d d_fixed 6 /*|info| corresponding to a \\{down} that can't change*/
+
+@ When the |movement| procedure gets to the label |found|, the value of
+|info(p)| will be either |y_here| or |z_here|. If it is, say, |y_here|,
+the procedure generates a |y0| command (or a |w0| command), and marks
+all |info| fields between |q| and |p| so that |y| is not OK in that range.
+
+@<Generate a |y0| or |z0| command...@>=
+info(q)=info(p);
+if (info(q)==y_here)
+ {@+dvi_out(o+y0-down1); /*|y0| or |w0|*/
+ while (link(q)!=p)
+ {@+q=link(q);
+ switch (info(q)) {
+ case yz_OK: info(q)=z_OK;@+break;
+ case y_OK: info(q)=d_fixed;@+break;
+ default:do_nothing;
+ }
+ }
+ }
+else{@+dvi_out(o+z0-down1); /*|z0| or |x0|*/
+ while (link(q)!=p)
+ {@+q=link(q);
+ switch (info(q)) {
+ case yz_OK: info(q)=y_OK;@+break;
+ case z_OK: info(q)=d_fixed;@+break;
+ default:do_nothing;
+ }
+ }
+ }
+
+@ @<Generate a |down| or |right|...@>=
+info(q)=yz_OK;
+if (abs(w) >= 040000000)
+ {@+dvi_out(o+3); /*|down4| or |right4|*/
+ dvi_four(w);return;
+ }
+if (abs(w) >= 0100000)
+ {@+dvi_out(o+2); /*|down3| or |right3|*/
+ if (w < 0) w=w+0100000000;
+ dvi_out(w/0200000);w=w%0200000;goto label2;
+ }
+if (abs(w) >= 0200)
+ {@+dvi_out(o+1); /*|down2| or |right2|*/
+ if (w < 0) w=w+0200000;
+ goto label2;
+ }
+dvi_out(o); /*|down1| or |right1|*/
+if (w < 0) w=w+0400;
+goto label1;
+label2: dvi_out(w/0400);
+label1: dvi_out(w%0400);return
+
+@ As we search through the stack, we are in one of three states,
+|y_seen|, |z_seen|, or |none_seen|, depending on whether we have
+encountered |y_here| or |z_here| nodes. These states are encoded as
+multiples of 6, so that they can be added to the |info| fields for quick
+decision-making.
+@^inner loop@>
+
+@d none_seen 0 /*no |y_here| or |z_here| nodes have been encountered yet*/
+@d y_seen 6 /*we have seen |y_here| but not |z_here|*/
+@d z_seen 12 /*we have seen |z_here| but not |y_here|*/
+
+@<Look at the other stack entries until deciding...@>=
+p=link(q);mstate=none_seen;
+while (p!=null)
+ {@+if (width(p)==w) @<Consider a node with matching width; |goto found|
+if it's a hit@>@;
+ else switch (mstate+info(p)) {
+ case none_seen+y_here: mstate=y_seen;@+break;
+ case none_seen+z_here: mstate=z_seen;@+break;
+ case y_seen+z_here: case z_seen+y_here: goto not_found;
+ default:do_nothing;
+ }
+ p=link(p);
+ }
+not_found:
+
+@ We might find a valid hit in a |y| or |z| byte that is already gone
+from the buffer. But we can't change bytes that are gone forever; ``the
+moving finger writes, $\ldots\,\,$.''
+
+@<Consider a node with matching width...@>=
+switch (mstate+info(p)) {
+case none_seen+yz_OK: case none_seen+y_OK:
+ case z_seen+yz_OK: case z_seen+y_OK: @t@>@;@/
+ if (location(p) < dvi_gone) goto not_found;
+ else@<Change buffered instruction to |y| or |w| and |goto found|@>@;@+break;
+case none_seen+z_OK: case y_seen+yz_OK:
+ case y_seen+z_OK: @t@>@;@/
+ if (location(p) < dvi_gone) goto not_found;
+ else@<Change buffered instruction to |z| or |x| and |goto found|@>@;@+break;
+case none_seen+y_here: case none_seen+z_here:
+ case y_seen+z_here: case z_seen+y_here: goto found;
+default:do_nothing;
+}
+
+@ @<Change buffered instruction to |y| or |w| and |goto found|@>=
+{@+k=location(p)-dvi_offset;
+if (k < 0) k=k+dvi_buf_size;
+dvi_buf[k]=dvi_buf[k]+y1-down1;
+info(p)=y_here;goto found;
+}
+
+@ @<Change buffered instruction to |z| or |x| and |goto found|@>=
+{@+k=location(p)-dvi_offset;
+if (k < 0) k=k+dvi_buf_size;
+dvi_buf[k]=dvi_buf[k]+z1-down1;
+info(p)=z_here;goto found;
+}
+
+@ In case you are wondering when all the movement nodes are removed from
+\TeX's memory, the answer is that they are recycled just before
+|hlist_out| and |vlist_out| finish outputting a box. This restores the
+down and right stacks to the state they were in before the box was output,
+except that some |info|'s may have become more restrictive.
+
+@p static void prune_movements(int @!l)
+ /*delete movement nodes with |location >= l|*/
+{@+
+pointer p; /*node being deleted*/
+while (down_ptr!=null)
+ {@+if (location(down_ptr) < l) goto done;
+ p=down_ptr;down_ptr=link(p);free_node(p, movement_node_size);
+ }
+done: while (right_ptr!=null)
+ {@+if (location(right_ptr) < l) return;
+ p=right_ptr;right_ptr=link(p);free_node(p, movement_node_size);
+ }
+}
+
+@ The actual distances by which we want to move might be computed as the
+sum of several separate movements. For example, there might be several
+glue nodes in succession, or we might want to move right by the width of
+some box plus some amount of glue. More importantly, the baselineskip
+distances are computed in terms of glue together with the depth and
+height of adjacent boxes, and we want the \.{DVI} file to lump these
+three quantities together into a single motion.
+
+Therefore, \TeX\ maintains two pairs of global variables: |dvi_h| and |dvi_v|
+are the |h| and |v| coordinates corresponding to the commands actually
+output to the \.{DVI} file, while |cur_h| and |cur_v| are the coordinates
+corresponding to the current state of the output routines. Coordinate
+changes will accumulate in |cur_h| and |cur_v| without being reflected
+in the output, until such a change becomes necessary or desirable; we
+can call the |movement| procedure whenever we want to make |dvi_h==cur_h|
+or |dvi_v==cur_v|.
+
+The current font reflected in the \.{DVI} output is called |dvi_f|;
+there is no need for a `\\{cur\_f}' variable.
+
+The depth of nesting of |hlist_out| and |vlist_out| is called |cur_s|;
+this is essentially the depth of |push| commands in the \.{DVI} output.
+
+@d synch_h if (cur_h!=dvi_h)
+ {@+movement(cur_h-dvi_h, right1);dvi_h=cur_h;
+ }
+@d synch_v if (cur_v!=dvi_v)
+ {@+movement(cur_v-dvi_v, down1);dvi_v=cur_v;
+ }
+
+@<Glob...@>=
+static scaled @!dvi_h, @!dvi_v; /*a \.{DVI} reader program thinks we are here*/
+static scaled @!cur_h, @!cur_v; /*\TeX\ thinks we are here*/
+static internal_font_number @!dvi_f; /*the current font*/
+static int @!cur_s; /*current depth of output box nesting, initially $-1$*/
+
+@ @<Initialize variables as |ship_out| begins@>=
+dvi_h=0;dvi_v=0;cur_h=h_offset;dvi_f=null_font;
+ensure_dvi_open;
+if (total_pages==0)
+ {@+dvi_out(pre);dvi_out(id_byte); /*output the preamble*/
+@^preamble of \.{DVI} file@>
+ dvi_four(25400000);dvi_four(473628672); /*conversion ratio for sp*/
+ prepare_mag();dvi_four(mag); /*magnification factor is frozen*/
+ old_setting=selector;selector=new_string;
+ print(" TeX output ");print_int(year);print_char('.');
+ print_two(month);print_char('.');print_two(day);
+ print_char(':');print_two(time/60);
+ print_two(time%60);
+ selector=old_setting;dvi_out(cur_length);
+ for (s=str_start[str_ptr]; s<=pool_ptr-1; s++) dvi_out(so(str_pool[s]));
+ pool_ptr=str_start[str_ptr]; /*flush the current string*/
+ }
+
+@ When |hlist_out| is called, its duty is to output the box represented
+by the |hlist_node| pointed to by |temp_ptr|. The reference point of that
+box has coordinates |(cur_h, cur_v)|.
+
+Similarly, when |vlist_out| is called, its duty is to output the box represented
+by the |vlist_node| pointed to by |temp_ptr|. The reference point of that
+box has coordinates |(cur_h, cur_v)|.
+@^recursion@>
+
+@p static void vlist_out(void); /*|hlist_out| and |vlist_out| are mutually
+ recursive*/
+
+@ The recursive procedures |hlist_out| and |vlist_out| each have local variables
+|save_h| and |save_v| to hold the values of |dvi_h| and |dvi_v| just before
+entering a new level of recursion. In effect, the values of |save_h| and
+|save_v| on \TeX's run-time stack correspond to the values of |h| and |v|
+that a \.{DVI}-reading program will push onto its coordinate stack.
+
+@p @t\4@>@<Declare procedures needed in |hlist_out|, |vlist_out|@>@t@>@/
+static void hlist_out(void) /*output an |hlist_node| box*/
+{@+
+scaled base_line; /*the baseline coordinate for this box*/
+scaled @!left_edge; /*the left coordinate for this box*/
+scaled @!save_h, @!save_v; /*what |dvi_h| and |dvi_v| should pop to*/
+pointer @!this_box; /*pointer to containing box*/
+glue_ord @!g_order; /*applicable order of infinity for glue*/
+int @!g_sign; /*selects type of glue*/
+pointer @!p; /*current position in the hlist*/
+int @!save_loc; /*\.{DVI} byte location upon entry*/
+pointer @!leader_box; /*the leader box being replicated*/
+scaled @!leader_wd; /*width of leader box being replicated*/
+scaled @!lx; /*extra space between leader boxes*/
+bool @!outer_doing_leaders; /*were we doing leaders?*/
+scaled @!edge; /*left edge of sub-box, or right edge of leader space*/
+double @!glue_temp; /*glue value before rounding*/
+double @!cur_glue; /*glue seen so far*/
+scaled @!cur_g; /*rounded equivalent of |cur_glue| times the glue ratio*/
+cur_g=0;cur_glue=float_constant(0);
+this_box=temp_ptr;g_order=glue_order(this_box);
+g_sign=glue_sign(this_box);p=list_ptr(this_box);
+incr(cur_s);
+if (cur_s > 0) dvi_out(push);
+if (cur_s > max_push) max_push=cur_s;
+save_loc=dvi_offset+dvi_ptr;base_line=cur_v;left_edge=cur_h;
+while (p!=null) @<Output node |p| for |hlist_out| and move to the next node,
+maintaining the condition |cur_v=base_line|@>;
+prune_movements(save_loc);
+if (cur_s > 0) dvi_pop(save_loc);
+decr(cur_s);
+}
+
+@ We ought to give special care to the efficiency of one part of |hlist_out|,
+since it belongs to \TeX's inner loop. When a |char_node| is encountered,
+we save a little time by processing several nodes in succession until
+reaching a non-|char_node|. The program uses the fact that |set_char_0==0|.
+@^inner loop@>
+
+@<Output node |p| for |hlist_out|...@>=
+reswitch: if (is_char_node(p))
+ {@+synch_h;synch_v;
+ @/do@+{f=font(p);c=character(p);
+ if (f!=dvi_f) @<Change font |dvi_f| to |f|@>;
+ if (c >= qi(128)) dvi_out(set1);
+ dvi_out(qo(c));@/
+ cur_h=cur_h+char_width(f, char_info(f, c));
+ p=link(p);
+ }@+ while (!(!is_char_node(p)));
+ dvi_h=cur_h;
+ }
+else@<Output the non-|char_node| |p| for |hlist_out| and move to the next
+node@>@;
+
+@ @<Change font |dvi_f| to |f|@>=
+{@+if (!font_used[f])
+ {@+dvi_font_def(f);font_used[f]=true;
+ }
+if (f <= 64+font_base) dvi_out(f-font_base-1+fnt_num_0)@;
+else{@+dvi_out(fnt1);dvi_out(f-font_base-1);
+ }
+dvi_f=f;
+}
+
+@ @<Output the non-|char_node| |p| for |hlist_out|...@>=
+{@+switch (type(p)) {
+case hlist_node: case vlist_node: @<Output a box in an hlist@>@;@+break;
+case rule_node: {@+rule_ht=height(p);rule_dp=depth(p);rule_wd=width(p);
+ goto fin_rule;
+ }
+case whatsit_node: @<Output the whatsit node |p| in an hlist@>;@+break;
+case glue_node: @<Move right or output leaders@>@;
+case kern_node: case math_node: cur_h=cur_h+width(p);@+break;
+case ligature_node: @<Make node |p| look like a |char_node| and |goto reswitch|@>@;
+default:do_nothing;
+} @/
+goto next_p;
+fin_rule: @<Output a rule in an hlist@>;
+move_past: cur_h=cur_h+rule_wd;
+next_p: p=link(p);
+}
+
+@ @<Output a box in an hlist@>=
+if (list_ptr(p)==null) cur_h=cur_h+width(p);
+else{@+save_h=dvi_h;save_v=dvi_v;
+ cur_v=base_line+shift_amount(p); /*shift the box down*/
+ temp_ptr=p;edge=cur_h;
+ if (type(p)==vlist_node) vlist_out();@+else hlist_out();
+ dvi_h=save_h;dvi_v=save_v;
+ cur_h=edge+width(p);cur_v=base_line;
+ }
+
+@ @<Output a rule in an hlist@>=
+if (is_running(rule_ht)) rule_ht=height(this_box);
+if (is_running(rule_dp)) rule_dp=depth(this_box);
+rule_ht=rule_ht+rule_dp; /*this is the rule thickness*/
+if ((rule_ht > 0)&&(rule_wd > 0)) /*we don't output empty rules*/
+ {@+synch_h;cur_v=base_line+rule_dp;synch_v;
+ dvi_out(set_rule);dvi_four(rule_ht);dvi_four(rule_wd);
+ cur_v=base_line;dvi_h=dvi_h+rule_wd;
+ }
+
+@ @d billion float_constant(1000000000)
+@d vet_glue(A) glue_temp=A;
+ if (glue_temp > billion)
+ glue_temp=billion;
+ else if (glue_temp < -billion)
+ glue_temp=-billion
+
+@<Move right or output leaders@>=
+{@+g=glue_ptr(p);rule_wd=width(g)-cur_g;
+if (g_sign!=normal)
+ {@+if (g_sign==stretching)
+ {@+if (stretch_order(g)==g_order)
+ {@+cur_glue=cur_glue+stretch(g);
+ vet_glue(unfix(glue_set(this_box))*cur_glue);
+@^real multiplication@>
+ cur_g=round(glue_temp);
+ }
+ }
+ else if (shrink_order(g)==g_order)
+ {@+cur_glue=cur_glue-shrink(g);
+ vet_glue(unfix(glue_set(this_box))*cur_glue);
+ cur_g=round(glue_temp);
+ }
+ }
+rule_wd=rule_wd+cur_g;
+if (subtype(p) >= a_leaders)
+ @<Output leaders in an hlist, |goto fin_rule| if a rule or to |next_p| if
+done@>;
+goto move_past;
+}
+
+@ @<Output leaders in an hlist...@>=
+{@+leader_box=leader_ptr(p);
+if (type(leader_box)==rule_node)
+ {@+rule_ht=height(leader_box);rule_dp=depth(leader_box);
+ goto fin_rule;
+ }
+leader_wd=width(leader_box);
+if ((leader_wd > 0)&&(rule_wd > 0))
+ {@+rule_wd=rule_wd+10; /*compensate for floating-point rounding*/
+ edge=cur_h+rule_wd;lx=0;
+ @<Let |cur_h| be the position of the first box, and set |leader_wd+lx| to
+the spacing between corresponding parts of boxes@>;
+ while (cur_h+leader_wd <= edge)
+ @<Output a leader box at |cur_h|, then advance |cur_h| by |leader_wd+lx|@>;
+ cur_h=edge-10;goto next_p;
+ }
+}
+
+@ The calculations related to leaders require a bit of care. First, in the
+case of |a_leaders| (aligned leaders), we want to move |cur_h| to
+|left_edge| plus the smallest multiple of |leader_wd| for which the result
+is not less than the current value of |cur_h|; i.e., |cur_h| should become
+$|left_edge|+|leader_wd|\times\lceil
+(|cur_h|-|left_edge|)/|leader_wd|\rceil$. The program here should work in
+all cases even though some implementations of \PASCAL\ give nonstandard
+results for the |/| operation when |cur_h| is less than |left_edge|.
+
+In the case of |c_leaders| (centered leaders), we want to increase |cur_h|
+by half of the excess space not occupied by the leaders; and in the
+case of |x_leaders| (expanded leaders) we increase |cur_h|
+by $1/(q+1)$ of this excess space, where $q$ is the number of times the
+leader box will be replicated. Slight inaccuracies in the division might
+accumulate; half of this rounding error is placed at each end of the leaders.
+
+@<Let |cur_h| be the position of the first box,...@>=
+if (subtype(p)==a_leaders)
+ {@+save_h=cur_h;
+ cur_h=left_edge+leader_wd*((cur_h-left_edge)/leader_wd);
+ if (cur_h < save_h) cur_h=cur_h+leader_wd;
+ }
+else{@+lq=rule_wd/leader_wd; /*the number of box copies*/
+ lr=rule_wd%leader_wd; /*the remaining space*/
+ if (subtype(p)==c_leaders) cur_h=cur_h+(lr/2);
+ else{@+lx=lr/(lq+1);
+ cur_h=cur_h+((lr-(lq-1)*lx)/2);
+ }
+ }
+
+@ The `\\{synch}' operations here are intended to decrease the number of
+bytes needed to specify horizontal and vertical motion in the \.{DVI} output.
+
+@<Output a leader box at |cur_h|,...@>=
+{@+cur_v=base_line+shift_amount(leader_box);synch_v;save_v=dvi_v;@/
+synch_h;save_h=dvi_h;temp_ptr=leader_box;
+outer_doing_leaders=doing_leaders;doing_leaders=true;
+if (type(leader_box)==vlist_node) vlist_out();@+else hlist_out();
+doing_leaders=outer_doing_leaders;
+dvi_v=save_v;dvi_h=save_h;cur_v=base_line;
+cur_h=save_h+leader_wd+lx;
+}
+
+@ The |vlist_out| routine is similar to |hlist_out|, but a bit simpler.
+
+@p static void vlist_out(void) /*output a |vlist_node| box*/
+{@+
+scaled left_edge; /*the left coordinate for this box*/
+scaled @!top_edge; /*the top coordinate for this box*/
+scaled @!save_h, @!save_v; /*what |dvi_h| and |dvi_v| should pop to*/
+pointer @!this_box; /*pointer to containing box*/
+glue_ord @!g_order; /*applicable order of infinity for glue*/
+int @!g_sign; /*selects type of glue*/
+pointer @!p; /*current position in the vlist*/
+int @!save_loc; /*\.{DVI} byte location upon entry*/
+pointer @!leader_box; /*the leader box being replicated*/
+scaled @!leader_ht; /*height of leader box being replicated*/
+scaled @!lx; /*extra space between leader boxes*/
+bool @!outer_doing_leaders; /*were we doing leaders?*/
+scaled @!edge; /*bottom boundary of leader space*/
+double @!glue_temp; /*glue value before rounding*/
+double @!cur_glue; /*glue seen so far*/
+scaled @!cur_g; /*rounded equivalent of |cur_glue| times the glue ratio*/
+cur_g=0;cur_glue=float_constant(0);
+this_box=temp_ptr;g_order=glue_order(this_box);
+g_sign=glue_sign(this_box);p=list_ptr(this_box);
+incr(cur_s);
+if (cur_s > 0) dvi_out(push);
+if (cur_s > max_push) max_push=cur_s;
+save_loc=dvi_offset+dvi_ptr;left_edge=cur_h;cur_v=cur_v-height(this_box);
+top_edge=cur_v;
+while (p!=null) @<Output node |p| for |vlist_out| and move to the next node,
+maintaining the condition |cur_h=left_edge|@>;
+prune_movements(save_loc);
+if (cur_s > 0) dvi_pop(save_loc);
+decr(cur_s);
+}
+
+@ @<Output node |p| for |vlist_out|...@>=
+{@+if (is_char_node(p)) confusion("vlistout");
+@:this can't happen vlistout}{\quad vlistout@>
+else@<Output the non-|char_node| |p| for |vlist_out|@>;
+next_p: p=link(p);
+}
+
+@ @<Output the non-|char_node| |p| for |vlist_out|@>=
+{@+switch (type(p)) {
+case hlist_node: case vlist_node: @<Output a box in a vlist@>@;@+break;
+case rule_node: {@+rule_ht=height(p);rule_dp=depth(p);rule_wd=width(p);
+ goto fin_rule;
+ }
+case whatsit_node: @<Output the whatsit node |p| in a vlist@>;@+break;
+case glue_node: @<Move down or output leaders@>@;
+case kern_node: cur_v=cur_v+width(p);@+break;
+default:do_nothing;
+} @/
+goto next_p;
+fin_rule: @<Output a rule in a vlist, |goto next_p|@>;
+move_past: cur_v=cur_v+rule_ht;
+}
+
+@ The |synch_v| here allows the \.{DVI} output to use one-byte commands
+for adjusting |v| in most cases, since the baselineskip distance will
+usually be constant.
+
+@<Output a box in a vlist@>=
+if (list_ptr(p)==null) cur_v=cur_v+height(p)+depth(p);
+else{@+cur_v=cur_v+height(p);synch_v;
+ save_h=dvi_h;save_v=dvi_v;
+ cur_h=left_edge+shift_amount(p); /*shift the box right*/
+ temp_ptr=p;
+ if (type(p)==vlist_node) vlist_out();@+else hlist_out();
+ dvi_h=save_h;dvi_v=save_v;
+ cur_v=save_v+depth(p);cur_h=left_edge;
+ }
+
+@ @<Output a rule in a vlist...@>=
+if (is_running(rule_wd)) rule_wd=width(this_box);
+rule_ht=rule_ht+rule_dp; /*this is the rule thickness*/
+cur_v=cur_v+rule_ht;
+if ((rule_ht > 0)&&(rule_wd > 0)) /*we don't output empty rules*/
+ {@+synch_h;synch_v;
+ dvi_out(put_rule);dvi_four(rule_ht);dvi_four(rule_wd);
+ }
+goto next_p
+
+@ @<Move down or output leaders@>=
+{@+g=glue_ptr(p);rule_ht=width(g)-cur_g;
+if (g_sign!=normal)
+ {@+if (g_sign==stretching)
+ {@+if (stretch_order(g)==g_order)
+ {@+cur_glue=cur_glue+stretch(g);
+ vet_glue(unfix(glue_set(this_box))*cur_glue);
+@^real multiplication@>
+ cur_g=round(glue_temp);
+ }
+ }
+ else if (shrink_order(g)==g_order)
+ {@+cur_glue=cur_glue-shrink(g);
+ vet_glue(unfix(glue_set(this_box))*cur_glue);
+ cur_g=round(glue_temp);
+ }
+ }
+rule_ht=rule_ht+cur_g;
+if (subtype(p) >= a_leaders)
+ @<Output leaders in a vlist, |goto fin_rule| if a rule or to |next_p| if
+done@>;
+goto move_past;
+}
+
+@ @<Output leaders in a vlist...@>=
+{@+leader_box=leader_ptr(p);
+if (type(leader_box)==rule_node)
+ {@+rule_wd=width(leader_box);rule_dp=0;
+ goto fin_rule;
+ }
+leader_ht=height(leader_box)+depth(leader_box);
+if ((leader_ht > 0)&&(rule_ht > 0))
+ {@+rule_ht=rule_ht+10; /*compensate for floating-point rounding*/
+ edge=cur_v+rule_ht;lx=0;
+ @<Let |cur_v| be the position of the first box, and set |leader_ht+lx| to
+the spacing between corresponding parts of boxes@>;
+ while (cur_v+leader_ht <= edge)
+ @<Output a leader box at |cur_v|, then advance |cur_v| by |leader_ht+lx|@>;
+ cur_v=edge-10;goto next_p;
+ }
+}
+
+@ @<Let |cur_v| be the position of the first box,...@>=
+if (subtype(p)==a_leaders)
+ {@+save_v=cur_v;
+ cur_v=top_edge+leader_ht*((cur_v-top_edge)/leader_ht);
+ if (cur_v < save_v) cur_v=cur_v+leader_ht;
+ }
+else{@+lq=rule_ht/leader_ht; /*the number of box copies*/
+ lr=rule_ht%leader_ht; /*the remaining space*/
+ if (subtype(p)==c_leaders) cur_v=cur_v+(lr/2);
+ else{@+lx=lr/(lq+1);
+ cur_v=cur_v+((lr-(lq-1)*lx)/2);
+ }
+ }
+
+@ When we reach this part of the program, |cur_v| indicates the top of a
+leader box, not its baseline.
+
+@<Output a leader box at |cur_v|,...@>=
+{@+cur_h=left_edge+shift_amount(leader_box);synch_h;save_h=dvi_h;@/
+cur_v=cur_v+height(leader_box);synch_v;save_v=dvi_v;
+temp_ptr=leader_box;
+outer_doing_leaders=doing_leaders;doing_leaders=true;
+if (type(leader_box)==vlist_node) vlist_out();@+else hlist_out();
+doing_leaders=outer_doing_leaders;
+dvi_v=save_v;dvi_h=save_h;cur_h=left_edge;
+cur_v=save_v-height(leader_box)+leader_ht+lx;
+}
+
+@ The |hlist_out| and |vlist_out| procedures are now complete, so we are
+ready for the |ship_out| routine that gets them started in the first place.
+
+@p static void ship_out(pointer @!p) /*output the box |p|*/
+{@+
+int page_loc; /*location of the current |bop|*/
+int @!j, @!k; /*indices to first ten count registers*/
+int @!s; /*index into |str_pool|*/
+int @!old_setting; /*saved |selector| setting*/
+@<Local variables to save the profiling context@>@;
+@<Charge the time used here on |ship_out|@>@;
+if (tracing_output > 0)
+ {@+print_nl("");print_ln();
+ print("Completed box being shipped out");
+@.Completed box...@>
+ }
+if (term_offset > max_print_line-9) print_ln();
+else if ((term_offset > 0)||(file_offset > 0)) print_char(' ');
+print_char('[');j=9;
+while ((count(j)==0)&&(j > 0)) decr(j);
+for (k=0; k<=j; k++)
+ {@+print_int(count(k));
+ if (k < j) print_char('.');
+ }
+update_terminal;
+if (tracing_output > 0)
+ {@+print_char(']');
+ begin_diagnostic();show_box(p);end_diagnostic(true);
+ }
+@<Ship box |p| out@>;
+if (tracing_output <= 0) print_char(']');
+dead_cycles=0;
+update_terminal; /*progress report*/
+@<Flush the box from memory, showing statistics if requested@>;
+@<restore the previous current file, line, and command@>@;
+}
+
+@ @<Flush the box from memory, showing statistics if requested@>=
+#ifdef @!STAT
+if (tracing_stats > 1)
+ {@+print_nl("Memory usage before: ");
+@.Memory usage...@>
+ print_int(var_used);print_char('&');
+ print_int(dyn_used);print_char(';');
+ }
+#endif
+flush_node_list(p);
+#ifdef @!STAT
+if (tracing_stats > 1)
+ {@+print(" after: ");
+ print_int(var_used);print_char('&');
+ print_int(dyn_used);print("; still untouched: ");
+ print_int(hi_mem_min-lo_mem_max-1);print_ln();
+ }
+#endif
+
+@ @<Ship box |p| out@>=
+@<Update the values of |max_h| and |max_v|; but if the page is too large,
+|goto done|@>;
+@<Initialize variables as |ship_out| begins@>;
+page_loc=dvi_offset+dvi_ptr;
+dvi_out(bop);
+for (k=0; k<=9; k++) dvi_four(count(k));
+dvi_four(last_bop);last_bop=page_loc;
+cur_v=height(p)+v_offset;temp_ptr=p;
+if (type(p)==vlist_node) vlist_out();@+else hlist_out();
+dvi_out(eop);incr(total_pages);cur_s=-1;
+done:
+
+@ Sometimes the user will generate a huge page because other error messages
+are being ignored. Such pages are not output to the \.{dvi} file, since they
+may confuse the printing software.
+
+@<Update the values of |max_h| and |max_v|; but if the page is too large...@>=
+if ((height(p) > max_dimen)||@|(depth(p) > max_dimen)||@|
+ (height(p)+depth(p)+v_offset > max_dimen)||@|
+ (width(p)+h_offset > max_dimen))
+ {@+print_err("Huge page cannot be shipped out");
+@.Huge page...@>
+ help2("The page just created is more than 18 feet tall or",@/
+ "more than 18 feet wide, so I suspect something went wrong.");
+ error();
+ if (tracing_output <= 0)
+ {@+begin_diagnostic();
+ print_nl("The following box has been deleted:");
+@.The following...deleted@>
+ show_box(p);
+ end_diagnostic(true);
+ }
+ goto done;
+ }
+if (height(p)+depth(p)+v_offset > max_v) max_v=height(p)+depth(p)+v_offset;
+if (width(p)+h_offset > max_h) max_h=width(p)+h_offset
+
+@ At the end of the program, we must finish things off by writing the
+post\-amble. If |total_pages==0|, the \.{DVI} file was never opened.
+If |total_pages >= 65536|, the \.{DVI} file will lie. And if
+|max_push >= 65536|, the user deserves whatever chaos might ensue.
+
+An integer variable |k| will be declared for use by this routine.
+
+@<Finish the \.{DVI} file@>=
+while (cur_s > -1)
+ {@+if (cur_s > 0) dvi_out(pop)@;
+ else{@+dvi_out(eop);incr(total_pages);
+ }
+ decr(cur_s);
+ }
+if (total_pages==0) print_nl("No pages of output.");
+@.No pages of output@>
+else{@+dvi_out(post); /*beginning of the postamble*/
+ dvi_four(last_bop);last_bop=dvi_offset+dvi_ptr-5; /*|post| location*/
+ dvi_four(25400000);dvi_four(473628672); /*conversion ratio for sp*/
+ prepare_mag();dvi_four(mag); /*magnification factor*/
+ dvi_four(max_v);dvi_four(max_h);@/
+ dvi_out(max_push/256);dvi_out(max_push%256);@/
+ dvi_out((total_pages/256)%256);dvi_out(total_pages%256);@/
+ @<Output the font definitions for all fonts that were used@>;
+ dvi_out(post_post);dvi_four(last_bop);dvi_out(id_byte);@/
+ k=4+((dvi_buf_size-dvi_ptr)%4); /*the number of 223's*/
+ while (k > 0)
+ {@+dvi_out(223);decr(k);
+ }
+ @<Empty the last bytes out of |dvi_buf|@>;
+ print_nl("Output written on ");slow_print(output_file_name);
+@.Output written on x@>
+ print(" (");print_int(total_pages);print(" page");
+ if (total_pages!=1) print_char('s');
+ print(", ");print_int(dvi_offset+dvi_ptr);print(" bytes).");
+ b_close(&dvi_file);
+ }
+
+@ @<Output the font definitions...@>=
+while (font_ptr > font_base)
+ {@+if (font_used[font_ptr]) dvi_font_def(font_ptr);
+ decr(font_ptr);
+ }
+
+@* Packaging.
+We're essentially done with the parts of \TeX\ that are concerned with
+the input (|get_next|) and the output (|ship_out|). So it's time to
+get heavily into the remaining part, which does the real work of typesetting.
+
+After lists are constructed, \TeX\ wraps them up and puts them into boxes.
+Two major subroutines are given the responsibility for this task: |hpack|
+applies to horizontal lists (hlists) and |vpack| applies to vertical lists
+(vlists). The main duty of |hpack| and |vpack| is to compute the dimensions
+of the resulting boxes, and to adjust the glue if one of those dimensions
+is pre-specified. The computed sizes normally enclose all of the material
+inside the new box; but some items may stick out if negative glue is used,
+if the box is overfull, or if a \.{\\vbox} includes other boxes that have
+been shifted left.
+
+The subroutine call |hpack(p, w, m)| returns a pointer to an |hlist_node|
+for a box containing the hlist that starts at |p|. Parameter |w| specifies
+a width; and parameter |m| is either `|exactly|' or `|additional|'. Thus,
+|hpack(p, w, exactly)| produces a box whose width is exactly |w|, while
+|hpack(p, w, additional)| yields a box whose width is the natural width plus
+|w|. It is convenient to define a macro called `|natural|' to cover the
+most common case, so that we can say |hpack(p, natural)| to get a box that
+has the natural width of list |p|.
+
+Similarly, |vpack(p, w, m)| returns a pointer to a |vlist_node| for a
+box containing the vlist that starts at |p|. In this case |w| represents
+a height instead of a width; the parameter |m| is interpreted as in |hpack|.
+
+@d exactly 0 /*a box dimension is pre-specified*/
+@d additional 1 /*a box dimension is increased from the natural one*/
+@d natural 0, additional /*shorthand for parameters to |hpack| and |vpack|*/
+
+@ The parameters to |hpack| and |vpack| correspond to \TeX's primitives
+like `\.{\\hbox} \.{to} \.{300pt}', `\.{\\hbox} \.{spread} \.{10pt}'; note
+that `\.{\\hbox}' with no dimension following it is equivalent to
+`\.{\\hbox} \.{spread} \.{0pt}'. The |scan_spec| subroutine scans such
+constructions in the user's input, including the mandatory left brace that
+follows them, and it puts the specification onto |save_stack| so that the
+desired box can later be obtained by executing the following code:
+$$\vbox{\halign{#\hfil\cr
+|save_ptr=save_ptr-2;|\cr
+|hpack(p, saved(1), saved(0)).|\cr}}$$
+Special care is necessary to ensure that the special |save_stack| codes
+are placed just below the new group code, because scanning can change
+|save_stack| when \.{\\csname} appears.
+
+@p static void scan_spec(group_code @!c, bool @!three_codes)
+ /*scans a box specification and left brace*/
+{@+
+int @!s; /*temporarily saved value*/
+int @!spec_code;
+if (three_codes) s=saved(0);
+if (scan_keyword("to")) spec_code=exactly;
+@.to@>
+else if (scan_keyword("spread")) spec_code=additional;
+@.spread@>
+else{@+spec_code=additional;cur_val=0;
+ goto found;
+ }
+scan_normal_dimen;
+found: if (three_codes)
+ {@+saved(0)=s;incr(save_ptr);
+ }
+saved(0)=spec_code;saved(1)=cur_val;save_ptr=save_ptr+2;
+new_save_level(c);scan_left_brace();
+}
+
+@ To figure out the glue setting, |hpack| and |vpack| determine how much
+stretchability and shrinkability are present, considering all four orders
+of infinity. The highest order of infinity that has a nonzero coefficient
+is then used as if no other orders were present.
+
+For example, suppose that the given list contains six glue nodes with
+the respective stretchabilities 3pt, 8fill, 5fil, 6pt, $-3$fil, $-8$fill.
+Then the total is essentially 2fil; and if a total additional space of 6pt
+is to be achieved by stretching, the actual amounts of stretch will be
+0pt, 0pt, 15pt, 0pt, $-9$pt, and 0pt, since only `fil' glue will be
+considered. (The `fill' glue is therefore not really stretching infinitely
+with respect to `fil'; nobody would actually want that to happen.)
+
+The arrays |total_stretch| and |total_shrink| are used to determine how much
+glue of each kind is present. A global variable |last_badness| is used
+to implement \.{\\badness}.
+
+@<Glob...@>=
+static scaled @!total_stretch0[filll-normal+1],
+ *const @!total_stretch = @!total_stretch0-normal,
+ @!total_shrink0[filll-normal+1], *const @!total_shrink = @!total_shrink0-normal;
+ /*glue found by |hpack| or |vpack|*/
+static int @!last_badness; /*badness of the most recently packaged box*/
+
+@ If the global variable |adjust_tail| is non-null, the |hpack| routine
+also removes all occurrences of |ins_node|, |mark_node|, and |adjust_node|
+items and appends the resulting material onto the list that ends at
+location |adjust_tail|.
+
+@<Glob...@>=
+static pointer @!adjust_tail; /*tail of adjustment list*/
+
+@ @<Set init...@>=adjust_tail=null;last_badness=0;
+
+@ Here now is |hpack|, which contains few if any surprises.
+
+@p static pointer hpack(pointer @!p, scaled @!w, small_number @!m)
+{@+
+pointer r; /*the box node that will be returned*/
+pointer @!q; /*trails behind |p|*/
+scaled @!h, @!d, @!x; /*height, depth, and natural width*/
+scaled @!s; /*shift amount*/
+pointer @!g; /*points to a glue specification*/
+glue_ord @!o; /*order of infinity*/
+internal_font_number @!f; /*the font in a |char_node|*/
+four_quarters @!i; /*font information about a |char_node|*/
+eight_bits @!hd; /*height and depth indices for a character*/
+last_badness=0;r=get_node(box_node_size);type(r)=hlist_node;
+subtype(r)=min_quarterword;shift_amount(r)=0;
+q=r+list_offset;link(q)=p;@/
+h=0;@<Clear dimensions to zero@>;
+while (p!=null) @<Examine node |p| in the hlist, taking account of its effect
+on the dimensions of the new box, or moving it to the adjustment list; then
+advance |p| to the next node@>;
+if (adjust_tail!=null) link(adjust_tail)=null;
+height(r)=h;depth(r)=d;@/
+@<Determine the value of |width(r)| and the appropriate glue setting; then
+|return| or |goto common_ending|@>;
+common_ending: @<Finish issuing a diagnostic message for an overfull or underfull
+hbox@>;
+end: return r;
+}
+
+@ @<Clear dimensions to zero@>=
+d=0;x=0;
+total_stretch[normal]=0;total_shrink[normal]=0;
+total_stretch[fil]=0;total_shrink[fil]=0;
+total_stretch[fill]=0;total_shrink[fill]=0;
+total_stretch[filll]=0;total_shrink[filll]=0
+
+@ @<Examine node |p| in the hlist, taking account of its effect...@>=
+@^inner loop@>
+{@+reswitch: while (is_char_node(p))
+ @<Incorporate character dimensions into the dimensions of the hbox that
+will contain~it, then move to the next node@>;
+if (p!=null)
+ {@+switch (type(p)) {
+ case hlist_node: case vlist_node: case rule_node:
+ case unset_node:
+ @<Incorporate box dimensions into the dimensions of the hbox that will
+contain~it@>@;@+break;
+ case ins_node: case mark_node: case adjust_node: if (adjust_tail!=null)
+ @<Transfer node |p| to the adjustment list@>@;@+break;
+ case whatsit_node: @<Incorporate a whatsit node into an hbox@>;@+break;
+ case glue_node: @<Incorporate glue into the horizontal totals@>@;@+break;
+ case kern_node: case math_node: x=x+width(p);@+break;
+ case ligature_node: @<Make node |p| look like a |char_node| and |goto reswitch|@>@;
+ default:do_nothing;
+ } @/
+ p=link(p);
+ }
+}
+
+
+@ @<Make node |p| look like a |char_node| and |goto reswitch|@>=
+{@+mem[lig_trick]=mem[lig_char(p)];link(lig_trick)=link(p);
+p=lig_trick;goto reswitch;
+}
+
+@ The code here implicitly uses the fact that running dimensions are
+indicated by |null_flag|, which will be ignored in the calculations
+because it is a highly negative number.
+
+@<Incorporate box dimensions into the dimensions of the hbox...@>=
+{@+x=x+width(p);
+if (type(p) >= rule_node) s=0;@+else s=shift_amount(p);
+if (height(p)-s > h) h=height(p)-s;
+if (depth(p)+s > d) d=depth(p)+s;
+}
+
+@ The following code is part of \TeX's inner loop; i.e., adding another
+character of text to the user's input will cause each of these instructions
+to be exercised one more time.
+@^inner loop@>
+
+@<Incorporate character dimensions into the dimensions of the hbox...@>=
+{@+f=font(p);i=char_info(f, character(p));hd=height_depth(i);
+x=x+char_width(f, i);@/
+s=char_height(f, hd);@+if (s > h) h=s;
+s=char_depth(f, hd);@+if (s > d) d=s;
+p=link(p);
+}
+
+@ Although node |q| is not necessarily the immediate predecessor of node |p|,
+it always points to some node in the list preceding |p|. Thus, we can delete
+nodes by moving |q| when necessary. The algorithm takes linear time, and the
+extra computation does not intrude on the inner loop unless it is necessary
+to make a deletion.
+@^inner loop@>
+
+@<Transfer node |p| to the adjustment list@>=
+{@+while (link(q)!=p) q=link(q);
+if (type(p)==adjust_node)
+ {@+link(adjust_tail)=adjust_ptr(p);
+ while (link(adjust_tail)!=null) adjust_tail=link(adjust_tail);
+ p=link(p);free_node(link(q), small_node_size);
+ }
+else{@+link(adjust_tail)=p;adjust_tail=p;p=link(p);
+ }
+link(q)=p;p=q;
+}
+
+@ @<Incorporate glue into the horizontal totals@>=
+{@+g=glue_ptr(p);x=x+width(g);@/
+o=stretch_order(g);total_stretch[o]=total_stretch[o]+stretch(g);
+o=shrink_order(g);total_shrink[o]=total_shrink[o]+shrink(g);
+if (subtype(p) >= a_leaders)
+ {@+g=leader_ptr(p);
+ if (height(g) > h) h=height(g);
+ if (depth(g) > d) d=depth(g);
+ }
+}
+
+@ When we get to the present part of the program, |x| is the natural width
+of the box being packaged.
+
+@<Determine the value of |width(r)| and the appropriate glue setting...@>=
+if (m==additional) w=x+w;
+width(r)=w;x=w-x; /*now |x| is the excess to be made up*/
+if (x==0)
+ {@+glue_sign(r)=normal;glue_order(r)=normal;
+ set_glue_ratio_zero(glue_set(r));
+ goto end;
+ }
+else if (x > 0) @<Determine horizontal glue stretch setting, then |return|
+or \hbox{|goto common_ending|}@>@;
+else@<Determine horizontal glue shrink setting, then |return| or \hbox{|goto
+common_ending|}@>@;
+
+@ @<Determine horizontal glue stretch setting...@>=
+{@+@<Determine the stretch order@>;
+glue_order(r)=o;glue_sign(r)=stretching;
+if (total_stretch[o]!=0) glue_set(r)=fix(x/(double)total_stretch[o]);
+@^real division@>
+else{@+glue_sign(r)=normal;
+ set_glue_ratio_zero(glue_set(r)); /*there's nothing to stretch*/
+ }
+if (o==normal) if (list_ptr(r)!=null)
+ @<Report an underfull hbox and |goto common_ending|, if this box is sufficiently
+bad@>;
+goto end;
+}
+
+@ @<Determine the stretch order@>=
+if (total_stretch[filll]!=0) o=filll;
+else if (total_stretch[fill]!=0) o=fill;
+else if (total_stretch[fil]!=0) o=fil;
+else o=normal
+
+@ @<Report an underfull hbox and |goto common_ending|, if...@>=
+{@+last_badness=badness(x, total_stretch[normal]);
+if (last_badness > hbadness)
+ {@+print_ln();
+ if (last_badness > 100) print_nl("Underfull");@+else print_nl("Loose");
+ print(" \\hbox (badness ");print_int(last_badness);
+@.Underfull \\hbox...@>
+@.Loose \\hbox...@>
+ goto common_ending;
+ }
+}
+
+@ In order to provide a decent indication of where an overfull or underfull
+box originated, we use a global variable |pack_begin_line| that is
+set nonzero only when |hpack| is being called by the paragraph builder
+or the alignment finishing routine.
+
+@<Glob...@>=
+static int @!pack_begin_line; /*source file line where the current paragraph
+ or alignment began; a negative value denotes alignment*/
+
+@ @<Set init...@>=
+pack_begin_line=0;
+
+@ @<Finish issuing a diagnostic message for an overfull or underfull hbox@>=
+if (output_active) print(") has occurred while \\output is active");
+else{@+if (pack_begin_line!=0)
+ {@+if (pack_begin_line > 0) print(") in paragraph at lines ");
+ else print(") in alignment at lines ");
+ print_int(abs(pack_begin_line));
+ print("--");
+ }
+ else print(") detected at line ");
+ print_int(line);
+ }
+print_ln();@/
+font_in_short_display=null_font;short_display(list_ptr(r));print_ln();@/
+begin_diagnostic();show_box(r);end_diagnostic(true)
+
+@ @<Determine horizontal glue shrink setting...@>=
+{@+@<Determine the shrink order@>;
+glue_order(r)=o;glue_sign(r)=shrinking;
+if (total_shrink[o]!=0) glue_set(r)=fix((-x)/(double)total_shrink[o]);
+@^real division@>
+else{@+glue_sign(r)=normal;
+ set_glue_ratio_zero(glue_set(r)); /*there's nothing to shrink*/
+ }
+if ((total_shrink[o] < -x)&&(o==normal)&&(list_ptr(r)!=null))
+ {@+last_badness=1000000;
+ set_glue_ratio_one(glue_set(r)); /*use the maximum shrinkage*/
+ @<Report an overfull hbox and |goto common_ending|, if this box is sufficiently
+bad@>;
+ }
+else if (o==normal) if (list_ptr(r)!=null)
+ @<Report a tight hbox and |goto common_ending|, if this box is sufficiently
+bad@>;
+goto end;
+}
+
+@ @<Determine the shrink order@>=
+if (total_shrink[filll]!=0) o=filll;
+else if (total_shrink[fill]!=0) o=fill;
+else if (total_shrink[fil]!=0) o=fil;
+else o=normal
+
+@ @<Report an overfull hbox and |goto common_ending|, if...@>=
+if ((-x-total_shrink[normal] > hfuzz)||(hbadness < 100))
+ {@+if ((overfull_rule > 0)&&(-x-total_shrink[normal] > hfuzz))
+ {@+while (link(q)!=null) q=link(q);
+ link(q)=new_rule();
+ width(link(q))=overfull_rule;
+ }
+ print_ln();print_nl("Overfull \\hbox (");
+@.Overfull \\hbox...@>
+ print_scaled(-x-total_shrink[normal]);print("pt too wide");
+ goto common_ending;
+ }
+
+@ @<Report a tight hbox and |goto common_ending|, if...@>=
+{@+last_badness=badness(-x, total_shrink[normal]);
+if (last_badness > hbadness)
+ {@+print_ln();print_nl("Tight \\hbox (badness ");print_int(last_badness);
+@.Tight \\hbox...@>
+ goto common_ending;
+ }
+}
+
+@ The |vpack| subroutine is actually a special case of a slightly more
+general routine called |vpackage|, which has four parameters. The fourth
+parameter, which is |max_dimen| in the case of |vpack|, specifies the
+maximum depth of the page box that is constructed. The depth is first
+computed by the normal rules; if it exceeds this limit, the reference
+point is simply moved down until the limiting depth is attained.
+
+
+@p
+#define vpack(...) @[vpackage(__VA_ARGS__, max_dimen)@] /*special case of unconstrained depth*/
+static pointer vpackage(pointer @!p, scaled @!h, small_number @!m, scaled @!l)
+{@+
+pointer r; /*the box node that will be returned*/
+scaled @!w, @!d, @!x; /*width, depth, and natural height*/
+scaled @!s; /*shift amount*/
+pointer @!g; /*points to a glue specification*/
+glue_ord @!o; /*order of infinity*/
+last_badness=0;r=get_node(box_node_size);type(r)=vlist_node;
+subtype(r)=min_quarterword;shift_amount(r)=0;
+list_ptr(r)=p;@/
+w=0;@<Clear dimensions to zero@>;
+while (p!=null) @<Examine node |p| in the vlist, taking account of its effect
+on the dimensions of the new box; then advance |p| to the next node@>;
+width(r)=w;
+if (d > l)
+ {@+x=x+d-l;depth(r)=l;
+ }
+else depth(r)=d;
+@<Determine the value of |height(r)| and the appropriate glue setting; then
+|return| or |goto common_ending|@>;
+common_ending: @<Finish issuing a diagnostic message for an overfull or underfull
+vbox@>;
+end: return r;
+}
+
+@ @<Examine node |p| in the vlist, taking account of its effect...@>=
+{@+if (is_char_node(p)) confusion("vpack");
+@:this can't happen vpack}{\quad vpack@>
+else switch (type(p)) {
+ case hlist_node: case vlist_node: case rule_node:
+ case unset_node:
+ @<Incorporate box dimensions into the dimensions of the vbox that will
+contain~it@>@;@+break;
+ case whatsit_node: @<Incorporate a whatsit node into a vbox@>;@+break;
+ case glue_node: @<Incorporate glue into the vertical totals@>@;@+break;
+ case kern_node: {@+x=x+d+width(p);d=0;
+ } @+break;
+ default:do_nothing;
+ }
+p=link(p);
+}
+
+@ @<Incorporate box dimensions into the dimensions of the vbox...@>=
+{@+x=x+d+height(p);d=depth(p);
+if (type(p) >= rule_node) s=0;@+else s=shift_amount(p);
+if (width(p)+s > w) w=width(p)+s;
+}
+
+@ @<Incorporate glue into the vertical totals@>=
+{@+x=x+d;d=0;@/
+g=glue_ptr(p);x=x+width(g);@/
+o=stretch_order(g);total_stretch[o]=total_stretch[o]+stretch(g);
+o=shrink_order(g);total_shrink[o]=total_shrink[o]+shrink(g);
+if (subtype(p) >= a_leaders)
+ {@+g=leader_ptr(p);
+ if (width(g) > w) w=width(g);
+ }
+}
+
+@ When we get to the present part of the program, |x| is the natural height
+of the box being packaged.
+
+@<Determine the value of |height(r)| and the appropriate glue setting...@>=
+if (m==additional) h=x+h;
+height(r)=h;x=h-x; /*now |x| is the excess to be made up*/
+if (x==0)
+ {@+glue_sign(r)=normal;glue_order(r)=normal;
+ set_glue_ratio_zero(glue_set(r));
+ goto end;
+ }
+else if (x > 0) @<Determine vertical glue stretch setting, then |return| or
+\hbox{|goto common_ending|}@>@;
+else@<Determine vertical glue shrink setting, then |return| or \hbox{|goto
+common_ending|}@>@;
+
+@ @<Determine vertical glue stretch setting...@>=
+{@+@<Determine the stretch order@>;
+glue_order(r)=o;glue_sign(r)=stretching;
+if (total_stretch[o]!=0) glue_set(r)=fix(x/(double)total_stretch[o]);
+@^real division@>
+else{@+glue_sign(r)=normal;
+ set_glue_ratio_zero(glue_set(r)); /*there's nothing to stretch*/
+ }
+if (o==normal) if (list_ptr(r)!=null)
+ @<Report an underfull vbox and |goto common_ending|, if this box is sufficiently
+bad@>;
+goto end;
+}
+
+@ @<Report an underfull vbox and |goto common_ending|, if...@>=
+{@+last_badness=badness(x, total_stretch[normal]);
+if (last_badness > vbadness)
+ {@+print_ln();
+ if (last_badness > 100) print_nl("Underfull");@+else print_nl("Loose");
+ print(" \\vbox (badness ");print_int(last_badness);
+@.Underfull \\vbox...@>
+@.Loose \\vbox...@>
+ goto common_ending;
+ }
+}
+
+@ @<Finish issuing a diagnostic message for an overfull or underfull vbox@>=
+if (output_active) print(") has occurred while \\output is active");
+else{@+if (pack_begin_line!=0) /*it's actually negative*/
+ {@+print(") in alignment at lines ");
+ print_int(abs(pack_begin_line));
+ print("--");
+ }
+ else print(") detected at line ");
+ print_int(line);
+ print_ln();@/
+ }
+begin_diagnostic();show_box(r);end_diagnostic(true)
+
+@ @<Determine vertical glue shrink setting...@>=
+{@+@<Determine the shrink order@>;
+glue_order(r)=o;glue_sign(r)=shrinking;
+if (total_shrink[o]!=0) glue_set(r)=fix((-x)/(double)total_shrink[o]);
+@^real division@>
+else{@+glue_sign(r)=normal;
+ set_glue_ratio_zero(glue_set(r)); /*there's nothing to shrink*/
+ }
+if ((total_shrink[o] < -x)&&(o==normal)&&(list_ptr(r)!=null))
+ {@+last_badness=1000000;
+ set_glue_ratio_one(glue_set(r)); /*use the maximum shrinkage*/
+ @<Report an overfull vbox and |goto common_ending|, if this box is sufficiently
+bad@>;
+ }
+else if (o==normal) if (list_ptr(r)!=null)
+ @<Report a tight vbox and |goto common_ending|, if this box is sufficiently
+bad@>;
+goto end;
+}
+
+@ @<Report an overfull vbox and |goto common_ending|, if...@>=
+if ((-x-total_shrink[normal] > vfuzz)||(vbadness < 100))
+ {@+print_ln();print_nl("Overfull \\vbox (");
+@.Overfull \\vbox...@>
+ print_scaled(-x-total_shrink[normal]);print("pt too high");
+ goto common_ending;
+ }
+
+@ @<Report a tight vbox and |goto common_ending|, if...@>=
+{@+last_badness=badness(-x, total_shrink[normal]);
+if (last_badness > vbadness)
+ {@+print_ln();print_nl("Tight \\vbox (badness ");print_int(last_badness);
+@.Tight \\vbox...@>
+ goto common_ending;
+ }
+}
+
+@ When a box is being appended to the current vertical list, the
+baselineskip calculation is handled by the |append_to_vlist| routine.
+
+@p static void append_to_vlist(pointer @!b)
+{@+scaled d; /*deficiency of space between baselines*/
+pointer @!p; /*a new glue node*/
+if (prev_depth > ignore_depth)
+ {@+d=width(baseline_skip)-prev_depth-height(b);
+ if (d < line_skip_limit) p=new_param_glue(line_skip_code);
+ else{@+p=new_skip_param(baseline_skip_code);
+ width(temp_ptr)=d; /*|temp_ptr==glue_ptr(p)|*/
+ }
+ link(tail)=p;tail=p;
+ }
+link(tail)=b;tail=b;prev_depth=depth(b);
+}
+
+@* Data structures for math mode.
+When \TeX\ reads a formula that is enclosed between \.\$'s, it constructs an
+{\sl mlist}, which is essentially a tree structure representing that
+formula. An mlist is a linear sequence of items, but we can regard it as
+a tree structure because mlists can appear within mlists. For example, many
+of the entries can be subscripted or superscripted, and such ``scripts''
+are mlists in their own right.
+
+An entire formula is parsed into such a tree before any of the actual
+typesetting is done, because the current style of type is usually not
+known until the formula has been fully scanned. For example, when the
+formula `\.{\$a+b \\over c+d\$}' is being read, there is no way to tell
+that `\.{a+b}' will be in script size until `\.{\\over}' has appeared.
+
+During the scanning process, each element of the mlist being built is
+classified as a relation, a binary operator, an open parenthesis, etc.,
+or as a construct like `\.{\\sqrt}' that must be built up. This classification
+appears in the mlist data structure.
+
+After a formula has been fully scanned, the mlist is converted to an hlist
+so that it can be incorporated into the surrounding text. This conversion is
+controlled by a recursive procedure that decides all of the appropriate
+styles by a ``top-down'' process starting at the outermost level and working
+in towards the subformulas. The formula is ultimately pasted together using
+combinations of horizontal and vertical boxes, with glue and penalty nodes
+inserted as necessary.
+
+An mlist is represented internally as a linked list consisting chiefly
+of ``noads'' (pronounced ``no-adds''), to distinguish them from the somewhat
+similar ``nodes'' in hlists and vlists. Certain kinds of ordinary nodes are
+allowed to appear in mlists together with the noads; \TeX\ tells the difference
+by means of the |type| field, since a noad's |type| is always greater than
+that of a node. An mlist does not contain character nodes, hlist nodes, vlist
+nodes, math nodes, ligature nodes,
+or unset nodes; in particular, each mlist item appears in the
+variable-size part of |mem|, so the |type| field is always present.
+
+@ Each noad is four or more words long. The first word contains the |type|
+and |subtype| and |link| fields that are already so familiar to us; the
+second, third, and fourth words are called the noad's |nucleus|, |subscr|,
+and |supscr| fields.
+
+Consider, for example, the simple formula `\.{\$x\^2\$}', which would be
+parsed into an mlist containing a single element called an |ord_noad|.
+The |nucleus| of this noad is a representation of `\.x', the |subscr| is
+empty, and the |supscr| is a representation of `\.2'.
+
+The |nucleus|, |subscr|, and |supscr| fields are further broken into
+subfields. If |p| points to a noad, and if |q| is one of its principal
+fields (e.g., |q==subscr(p)|), there are several possibilities for the
+subfields, depending on the |math_type| of |q|.
+
+\yskip\hang|math_type(q)==math_char| means that |fam(q)| refers to one of
+the sixteen font families, and |character(q)| is the number of a character
+within a font of that family, as in a character node.
+
+\yskip\hang|math_type(q)==math_text_char| is similar, but the character is
+unsubscripted and unsuperscripted and it is followed immediately by another
+character from the same font. (This |math_type| setting appears only
+briefly during the processing; it is used to suppress unwanted italic
+corrections.)
+
+\yskip\hang|math_type(q)==empty| indicates a field with no value (the
+corresponding attribute of noad |p| is not present).
+
+\yskip\hang|math_type(q)==sub_box| means that |info(q)| points to a box
+node (either an |hlist_node| or a |vlist_node|) that should be used as the
+value of the field. The |shift_amount| in the subsidiary box node is the
+amount by which that box will be shifted downward.
+
+\yskip\hang|math_type(q)==sub_mlist| means that |info(q)| points to
+an mlist; the mlist must be converted to an hlist in order to obtain
+the value of this field.
+
+\yskip\noindent In the latter case, we might have |info(q)==null|. This
+is not the same as |math_type(q)==empty|; for example, `\.{\$P\_\{\}\$}'
+and `\.{\$P\$}' produce different results (the former will not have the
+``italic correction'' added to the width of |P|, but the ``script skip''
+will be added).
+
+The definitions of subfields given here are evidently wasteful of space,
+since a halfword is being used for the |math_type| although only three
+bits would be needed. However, there are hardly ever many noads present at
+once, since they are soon converted to nodes that take up even more space,
+so we can afford to represent them in whatever way simplifies the
+programming.
+
+@d noad_size 4 /*number of words in a normal noad*/
+@d nucleus(A) A+1 /*the |nucleus| field of a noad*/
+@d supscr(A) A+2 /*the |supscr| field of a noad*/
+@d subscr(A) A+3 /*the |subscr| field of a noad*/
+@d math_type(A) link(A) /*a |halfword| in |mem|*/
+@d fam font /*a |quarterword| in |mem|*/
+@d math_char 1 /*|math_type| when the attribute is simple*/
+@d sub_box 2 /*|math_type| when the attribute is a box*/
+@d sub_mlist 3 /*|math_type| when the attribute is a formula*/
+@d math_text_char 4 /*|math_type| when italic correction is dubious*/
+
+@ Each portion of a formula is classified as Ord, Op, Bin, Rel, Open,
+Close, Punct, or Inner, for purposes of spacing and line breaking. An
+|ord_noad|, |op_noad|, |bin_noad|, |rel_noad|, |open_noad|, |close_noad|,
+|punct_noad|, or |inner_noad| is used to represent portions of the various
+types. For example, an `\.=' sign in a formula leads to the creation of a
+|rel_noad| whose |nucleus| field is a representation of an equals sign
+(usually |fam==0|, |character==075|). A formula preceded by \.{\\mathrel}
+also results in a |rel_noad|. When a |rel_noad| is followed by an
+|op_noad|, say, and possibly separated by one or more ordinary nodes (not
+noads), \TeX\ will insert a penalty node (with the current |rel_penalty|)
+just after the formula that corresponds to the |rel_noad|, unless there
+already was a penalty immediately following; and a ``thick space'' will be
+inserted just before the formula that corresponds to the |op_noad|.
+
+A noad of type |ord_noad|, |op_noad|, \dots, |inner_noad| usually
+has a |subtype==normal|. The only exception is that an |op_noad| might
+have |subtype==limits| or |no_limits|, if the normal positioning of
+limits has been overridden for this operator.
+
+@d ord_noad (unset_node+3) /*|type| of a noad classified Ord*/
+@d op_noad (ord_noad+1) /*|type| of a noad classified Op*/
+@d bin_noad (ord_noad+2) /*|type| of a noad classified Bin*/
+@d rel_noad (ord_noad+3) /*|type| of a noad classified Rel*/
+@d open_noad (ord_noad+4) /*|type| of a noad classified Open*/
+@d close_noad (ord_noad+5) /*|type| of a noad classified Close*/
+@d punct_noad (ord_noad+6) /*|type| of a noad classified Punct*/
+@d inner_noad (ord_noad+7) /*|type| of a noad classified Inner*/
+@d limits 1 /*|subtype| of |op_noad| whose scripts are to be above, below*/
+@d no_limits 2 /*|subtype| of |op_noad| whose scripts are to be normal*/
+
+@ A |radical_noad| is five words long; the fifth word is the |left_delimiter|
+field, which usually represents a square root sign.
+
+A |fraction_noad| is six words long; it has a |right_delimiter| field
+as well as a |left_delimiter|.
+
+Delimiter fields are of type |four_quarters|, and they have four subfields
+called |small_fam|, |small_char|, |large_fam|, |large_char|. These subfields
+represent variable-size delimiters by giving the ``small'' and ``large''
+starting characters, as explained in Chapter~17 of {\sl The \TeX book}.
+@:TeXbook}{\sl The \TeX book@>
+
+A |fraction_noad| is actually quite different from all other noads. Not
+only does it have six words, it has |thickness|, |denominator|, and
+|numerator| fields instead of |nucleus|, |subscr|, and |supscr|. The
+|thickness| is a scaled value that tells how thick to make a fraction
+rule; however, the special value |default_code| is used to stand for the
+|default_rule_thickness| of the current size. The |numerator| and
+|denominator| point to mlists that define a fraction; we always have
+$$\hbox{|math_type(numerator)==math_type(denominator)==sub_mlist|}.$$ The
+|left_delimiter| and |right_delimiter| fields specify delimiters that will
+be placed at the left and right of the fraction. In this way, a
+|fraction_noad| is able to represent all of \TeX's operators \.{\\over},
+\.{\\atop}, \.{\\above}, \.{\\overwithdelims}, \.{\\atopwithdelims}, and
+ \.{\\abovewithdelims}.
+
+@d left_delimiter(A) A+4 /*first delimiter field of a noad*/
+@d right_delimiter(A) A+5 /*second delimiter field of a fraction noad*/
+@d radical_noad (inner_noad+1) /*|type| of a noad for square roots*/
+@d radical_noad_size 5 /*number of |mem| words in a radical noad*/
+@d fraction_noad (radical_noad+1) /*|type| of a noad for generalized fractions*/
+@d fraction_noad_size 6 /*number of |mem| words in a fraction noad*/
+@d small_fam(A) mem[A].qqqq.b0 /*|fam| for ``small'' delimiter*/
+@d small_char(A) mem[A].qqqq.b1 /*|character| for ``small'' delimiter*/
+@d large_fam(A) mem[A].qqqq.b2 /*|fam| for ``large'' delimiter*/
+@d large_char(A) mem[A].qqqq.b3 /*|character| for ``large'' delimiter*/
+@d thickness(A) width(A) /*|thickness| field in a fraction noad*/
+@d default_code 010000000000 /*denotes |default_rule_thickness|*/
+@d numerator(A) supscr(A) /*|numerator| field in a fraction noad*/
+@d denominator(A) subscr(A) /*|denominator| field in a fraction noad*/
+
+@ The global variable |empty_field| is set up for initialization of empty
+fields in new noads. Similarly, |null_delimiter| is for the initialization
+of delimiter fields.
+
+@<Glob...@>=
+static two_halves @!empty_field;
+static four_quarters @!null_delimiter;
+
+@ @<Set init...@>=
+empty_field.rh=empty;empty_field.lh=null;@/
+null_delimiter.b0=0;null_delimiter.b1=min_quarterword;@/
+null_delimiter.b2=0;null_delimiter.b3=min_quarterword;
+
+@ The |new_noad| function creates an |ord_noad| that is completely null.
+
+@p static pointer new_noad(void)
+{@+pointer p;
+p=get_node(noad_size);
+type(p)=ord_noad;subtype(p)=normal;
+mem[nucleus(p)].hh=empty_field;
+mem[subscr(p)].hh=empty_field;
+mem[supscr(p)].hh=empty_field;
+return p;
+}
+
+@ A few more kinds of noads will complete the set: An |under_noad| has its
+nucleus underlined; an |over_noad| has it overlined. An |accent_noad| places
+an accent over its nucleus; the accent character appears as
+|fam(accent_chr(p))| and |character(accent_chr(p))|. A |vcenter_noad|
+centers its nucleus vertically with respect to the axis of the formula;
+in such noads we always have |math_type(nucleus(p))==sub_box|.
+
+And finally, we have |left_noad| and |right_noad| types, to implement
+\TeX's \.{\\left} and \.{\\right} as well as \eTeX's \.{\\middle}.
+The |nucleus| of such noads is
+replaced by a |delimiter| field; thus, for example, `\.{\\left(}' produces
+a |left_noad| such that |delimiter(p)| holds the family and character
+codes for all left parentheses. A |left_noad| never appears in an mlist
+except as the first element, and a |right_noad| never appears in an mlist
+except as the last element; furthermore, we either have both a |left_noad|
+and a |right_noad|, or neither one is present. The |subscr| and |supscr|
+fields are always |empty| in a |left_noad| and a |right_noad|.
+
+@d under_noad (fraction_noad+1) /*|type| of a noad for underlining*/
+@d over_noad (under_noad+1) /*|type| of a noad for overlining*/
+@d accent_noad (over_noad+1) /*|type| of a noad for accented subformulas*/
+@d accent_noad_size 5 /*number of |mem| words in an accent noad*/
+@d accent_chr(A) A+4 /*the |accent_chr| field of an accent noad*/
+@d vcenter_noad (accent_noad+1) /*|type| of a noad for \.{\\vcenter}*/
+@d left_noad (vcenter_noad+1) /*|type| of a noad for \.{\\left}*/
+@d right_noad (left_noad+1) /*|type| of a noad for \.{\\right}*/
+@d delimiter(A) nucleus(A) /*|delimiter| field in left and right noads*/
+@d middle_noad 1 /*|subtype| of right noad representing \.{\\middle}*/
+@d scripts_allowed(A) (type(A) >= ord_noad)&&(type(A) < left_noad)
+
+@ Math formulas can also contain instructions like \.{\\textstyle} that
+override \TeX's normal style rules. A |style_node| is inserted into the
+data structure to record such instructions; it is three words long, so it
+is considered a node instead of a noad. The |subtype| is either |display_style|
+or |text_style| or |script_style| or |script_script_style|. The
+second and third words of a |style_node| are not used, but they are
+present because a |choice_node| is converted to a |style_node|.
+
+\TeX\ uses even numbers 0, 2, 4, 6 to encode the basic styles
+|display_style|, \dots, |script_script_style|, and adds~1 to get the
+``cramped'' versions of these styles. This gives a numerical order that
+is backwards from the convention of Appendix~G in {\sl The \TeX book\/};
+i.e., a smaller style has a larger numerical value.
+@:TeXbook}{\sl The \TeX book@>
+
+@d style_node (unset_node+1) /*|type| of a style node*/
+@d style_node_size 3 /*number of words in a style node*/
+@d display_style 0 /*|subtype| for \.{\\displaystyle}*/
+@d text_style 2 /*|subtype| for \.{\\textstyle}*/
+@d script_style 4 /*|subtype| for \.{\\scriptstyle}*/
+@d script_script_style 6 /*|subtype| for \.{\\scriptscriptstyle}*/
+@d cramped 1 /*add this to an uncramped style if you want to cramp it*/
+
+@p static pointer new_style(small_number @!s) /*create a style node*/
+{@+pointer p; /*the new node*/
+p=get_node(style_node_size);type(p)=style_node;
+subtype(p)=s;width(p)=0;depth(p)=0; /*the |width| and |depth| are not used*/
+return p;
+}
+
+@ Finally, the \.{\\mathchoice} primitive creates a |choice_node|, which
+has special subfields |display_mlist|, |text_mlist|, |script_mlist|,
+and |script_script_mlist| pointing to the mlists for each style.
+
+@d choice_node (unset_node+2) /*|type| of a choice node*/
+@d display_mlist(A) info(A+1) /*mlist to be used in display style*/
+@d text_mlist(A) link(A+1) /*mlist to be used in text style*/
+@d script_mlist(A) info(A+2) /*mlist to be used in script style*/
+@d script_script_mlist(A) link(A+2) /*mlist to be used in scriptscript style*/
+
+@p static pointer new_choice(void) /*create a choice node*/
+{@+pointer p; /*the new node*/
+p=get_node(style_node_size);type(p)=choice_node;
+subtype(p)=0; /*the |subtype| is not used*/
+display_mlist(p)=null;text_mlist(p)=null;script_mlist(p)=null;
+script_script_mlist(p)=null;
+return p;
+}
+
+@ Let's consider now the previously unwritten part of |show_node_list|
+that displays the things that can only be present in mlists; this
+program illustrates how to access the data structures just defined.
+
+In the context of the following program, |p| points to a node or noad that
+should be displayed, and the current string contains the ``recursion history''
+that leads to this point. The recursion history consists of a dot for each
+outer level in which |p| is subsidiary to some node, or in which |p| is
+subsidiary to the |nucleus| field of some noad; the dot is replaced by
+`\.\_' or `\.\^' or `\./' or `\.\\' if |p| is descended from the |subscr|
+or |supscr| or |denominator| or |numerator| fields of noads. For example,
+the current string would be `\.{.\^.\_/}' if |p| points to the |ord_noad| for
+|x| in the (ridiculous) formula
+`\.{\$\\sqrt\{a\^\{\\mathinner\{b\_\{c\\over x+y\}\}\}\}\$}'.
+
+@<Cases of |show_node_list| that arise...@>=
+case style_node: print_style(subtype(p));@+break;
+case choice_node: @<Display choice node |p|@>@;@+break;
+case ord_noad: case op_noad: case bin_noad:
+ case rel_noad: case open_noad: case close_noad:
+ case punct_noad: case inner_noad:
+ case radical_noad: case over_noad: case under_noad:
+ case vcenter_noad: case accent_noad:
+ case left_noad: case right_noad: @<Display normal noad |p|@>@;@+break;
+case fraction_noad: @<Display fraction noad |p|@>@;@+break;
+
+@ Here are some simple routines used in the display of noads.
+
+@<Declare procedures needed for displaying the elements of mlists@>=
+static void print_fam_and_char(pointer @!p) /*prints family and character*/
+{@+print_esc("fam");print_int(fam(p));print_char(' ');
+print_ASCII(qo(character(p)));
+}
+@#
+static void print_delimiter(pointer @!p) /*prints a delimiter as 24-bit hex value*/
+{@+int a; /*accumulator*/
+a=small_fam(p)*256+qo(small_char(p));
+a=a*0x1000+large_fam(p)*256+qo(large_char(p));
+if (a < 0) print_int(a); /*this should never happen*/
+else print_hex(a);
+}
+
+@ The next subroutine will descend to another level of recursion when a
+subsidiary mlist needs to be displayed. The parameter |c| indicates what
+character is to become part of the recursion history. An empty mlist is
+distinguished from a field with |math_type(p)==empty|, because these are
+not equivalent (as explained above).
+@^recursion@>
+
+@<Declare procedures needed for displaying...@>=
+static void show_info(void); /*|show_node_list(info(temp_ptr))|*/
+static void print_subsidiary_data(pointer @!p, ASCII_code @!c)
+ /*display a noad field*/
+{@+if (cur_length >= depth_threshold)
+ {@+if (math_type(p)!=empty) print(" []");
+ }
+else{@+append_char(c); /*include |c| in the recursion history*/
+ temp_ptr=p; /*prepare for |show_info| if recursion is needed*/
+ switch (math_type(p)) {
+ case math_char: {@+print_ln();print_current_string();print_fam_and_char(p);
+ } @+break;
+ case sub_box: show_info();@+break; /*recursive call*/
+ case sub_mlist: if (info(p)==null)
+ {@+print_ln();print_current_string();print("{}");
+ }
+ else show_info();@+break; /*recursive call*/
+ default:do_nothing; /*|empty|*/
+ } @/
+ flush_char; /*remove |c| from the recursion history*/
+ }
+}
+
+@ The inelegant introduction of |show_info| in the code above seems better
+than the alternative of using \PASCAL's strange |forward| declaration for a
+procedure with parameters. The \PASCAL\ convention about dropping parameters
+from a post-|forward| procedure is, frankly, so intolerable to the author
+of \TeX\ that he would rather stoop to communication via a global temporary
+variable. (A similar stoopidity occurred with respect to |hlist_out| and
+|vlist_out| above, and it will occur with respect to |mlist_to_hlist| below.)
+@^Knuth, Donald Ervin@>
+@:PASCAL}{\PASCAL@>
+
+@p static void show_info(void) /*the reader will kindly forgive this*/
+{@+show_node_list(info(temp_ptr));
+}
+
+@ @<Declare procedures needed for displaying...@>=
+static void print_style(int @!c)
+{@+switch (c/2) {
+case 0: print_esc("displaystyle");@+break; /*|display_style==0|*/
+case 1: print_esc("textstyle");@+break; /*|text_style==2|*/
+case 2: print_esc("scriptstyle");@+break; /*|script_style==4|*/
+case 3: print_esc("scriptscriptstyle");@+break; /*|script_script_style==6|*/
+default:print("Unknown style!");
+}
+}
+
+@ @<Display choice node |p|@>=
+{@+print_esc("mathchoice");
+append_char('D');show_node_list(display_mlist(p));flush_char;
+append_char('T');show_node_list(text_mlist(p));flush_char;
+append_char('S');show_node_list(script_mlist(p));flush_char;
+append_char('s');show_node_list(script_script_mlist(p));flush_char;
+}
+
+@ @<Display normal noad |p|@>=
+{@+switch (type(p)) {
+case ord_noad: print_esc("mathord");@+break;
+case op_noad: print_esc("mathop");@+break;
+case bin_noad: print_esc("mathbin");@+break;
+case rel_noad: print_esc("mathrel");@+break;
+case open_noad: print_esc("mathopen");@+break;
+case close_noad: print_esc("mathclose");@+break;
+case punct_noad: print_esc("mathpunct");@+break;
+case inner_noad: print_esc("mathinner");@+break;
+case over_noad: print_esc("overline");@+break;
+case under_noad: print_esc("underline");@+break;
+case vcenter_noad: print_esc("vcenter");@+break;
+case radical_noad: {@+print_esc("radical");print_delimiter(left_delimiter(p));
+ } @+break;
+case accent_noad: {@+print_esc("accent");print_fam_and_char(accent_chr(p));
+ } @+break;
+case left_noad: {@+print_esc("left");print_delimiter(delimiter(p));
+ } @+break;
+case right_noad: {@+if (subtype(p)==normal) print_esc("right");
+ else print_esc("middle");
+ print_delimiter(delimiter(p));
+ }
+}
+if (type(p) < left_noad)
+ {@+if (subtype(p)!=normal)
+ if (subtype(p)==limits) print_esc("limits");
+ else print_esc("nolimits");
+ print_subsidiary_data(nucleus(p),'.');
+ }
+print_subsidiary_data(supscr(p),'^');
+print_subsidiary_data(subscr(p),'_');
+}
+
+@ @<Display fraction noad |p|@>=
+{@+print_esc("fraction, thickness ");
+if (thickness(p)==default_code) print("= default");
+else print_scaled(thickness(p));
+if ((small_fam(left_delimiter(p))!=0)||@+
+ (small_char(left_delimiter(p))!=min_quarterword)||@|
+ (large_fam(left_delimiter(p))!=0)||@|
+ (large_char(left_delimiter(p))!=min_quarterword))
+ {@+print(", left-delimiter ");print_delimiter(left_delimiter(p));
+ }
+if ((small_fam(right_delimiter(p))!=0)||@|
+ (small_char(right_delimiter(p))!=min_quarterword)||@|
+ (large_fam(right_delimiter(p))!=0)||@|
+ (large_char(right_delimiter(p))!=min_quarterword))
+ {@+print(", right-delimiter ");print_delimiter(right_delimiter(p));
+ }
+print_subsidiary_data(numerator(p),'\\');
+print_subsidiary_data(denominator(p),'/');
+}
+
+@ That which can be displayed can also be destroyed.
+
+@<Cases of |flush_node_list| that arise...@>=
+case style_node: {@+free_node(p, style_node_size);goto done;
+ }
+case choice_node: {@+flush_node_list(display_mlist(p));
+ flush_node_list(text_mlist(p));
+ flush_node_list(script_mlist(p));
+ flush_node_list(script_script_mlist(p));
+ free_node(p, style_node_size);goto done;
+ }
+case ord_noad: case op_noad: case bin_noad:
+ case rel_noad: case open_noad: case close_noad:
+ case punct_noad: case inner_noad:
+ case radical_noad: case over_noad: case under_noad:
+ case vcenter_noad: case accent_noad: @t@>@;@/
+ {@+if (math_type(nucleus(p)) >= sub_box)
+ flush_node_list(info(nucleus(p)));
+ if (math_type(supscr(p)) >= sub_box)
+ flush_node_list(info(supscr(p)));
+ if (math_type(subscr(p)) >= sub_box)
+ flush_node_list(info(subscr(p)));
+ if (type(p)==radical_noad) free_node(p, radical_noad_size);
+ else if (type(p)==accent_noad) free_node(p, accent_noad_size);
+ else free_node(p, noad_size);
+ goto done;
+ }
+case left_noad: case right_noad: {@+free_node(p, noad_size);goto done;
+ }
+case fraction_noad: {@+flush_node_list(info(numerator(p)));
+ flush_node_list(info(denominator(p)));
+ free_node(p, fraction_noad_size);goto done;
+ }
+
+@* Subroutines for math mode.
+In order to convert mlists to hlists, i.e., noads to nodes, we need several
+subroutines that are conveniently dealt with now.
+
+Let us first introduce the macros that make it easy to get at the parameters and
+other font information. A size code, which is a multiple of 16, is added to a
+family number to get an index into the table of internal font numbers
+for each combination of family and size. (Be alert: Size codes get
+larger as the type gets smaller.)
+
+@d text_size 0 /*size code for the largest size in a family*/
+@d script_size 16 /*size code for the medium size in a family*/
+@d script_script_size 32 /*size code for the smallest size in a family*/
+
+@<Basic printing procedures@>=
+static void print_size(int @!s)
+{@+if (s==text_size) print_esc("textfont");
+else if (s==script_size) print_esc("scriptfont");
+else print_esc("scriptscriptfont");
+}
+
+@ Before an mlist is converted to an hlist, \TeX\ makes sure that
+the fonts in family~2 have enough parameters to be math-symbol
+fonts, and that the fonts in family~3 have enough parameters to be
+math-extension fonts. The math-symbol parameters are referred to by using the
+following macros, which take a size code as their parameter; for example,
+|num1(cur_size)| gives the value of the |num1| parameter for the current size.
+@^parameters for symbols@>
+@^font parameters@>
+
+@d mathsy_end(A) fam_fnt(2+A)]].sc
+@d mathsy(A) font_info[A+param_base[mathsy_end
+@d math_x_height mathsy(5) /*height of `\.x'*/
+@d math_quad mathsy(6) /*\.{18mu}*/
+@d num1 mathsy(8) /*numerator shift-up in display styles*/
+@d num2 mathsy(9) /*numerator shift-up in non-display, non-\.{\\atop}*/
+@d num3 mathsy(10) /*numerator shift-up in non-display \.{\\atop}*/
+@d denom1 mathsy(11) /*denominator shift-down in display styles*/
+@d denom2 mathsy(12) /*denominator shift-down in non-display styles*/
+@d sup1 mathsy(13) /*superscript shift-up in uncramped display style*/
+@d sup2 mathsy(14) /*superscript shift-up in uncramped non-display*/
+@d sup3 mathsy(15) /*superscript shift-up in cramped styles*/
+@d sub1 mathsy(16) /*subscript shift-down if superscript is absent*/
+@d sub2 mathsy(17) /*subscript shift-down if superscript is present*/
+@d sup_drop mathsy(18) /*superscript baseline below top of large box*/
+@d sub_drop mathsy(19) /*subscript baseline below bottom of large box*/
+@d delim1 mathsy(20) /*size of \.{\\atopwithdelims} delimiters
+ in display styles*/
+@d delim2 mathsy(21) /*size of \.{\\atopwithdelims} delimiters in non-displays*/
+@d axis_height mathsy(22) /*height of fraction lines above the baseline*/
+@d total_mathsy_params 22
+
+@ The math-extension parameters have similar macros, but the size code is
+omitted (since it is always |cur_size| when we refer to such parameters).
+@^parameters for symbols@>
+@^font parameters@>
+
+@d mathex(A) font_info[A+param_base[fam_fnt(3+cur_size)]].sc
+@d default_rule_thickness mathex(8) /*thickness of \.{\\over} bars*/
+@d big_op_spacing1 mathex(9) /*minimum clearance above a displayed op*/
+@d big_op_spacing2 mathex(10) /*minimum clearance below a displayed op*/
+@d big_op_spacing3 mathex(11) /*minimum baselineskip above displayed op*/
+@d big_op_spacing4 mathex(12) /*minimum baselineskip below displayed op*/
+@d big_op_spacing5 mathex(13) /*padding above and below displayed limits*/
+@d total_mathex_params 13
+
+@ We also need to compute the change in style between mlists and their
+subsidiaries. The following macros define the subsidiary style for
+an overlined nucleus (|cramped_style|), for a subscript or a superscript
+(|sub_style| or |sup_style|), or for a numerator or denominator (|num_style|
+or |denom_style|).
+
+@d cramped_style(A) 2*(A/2)+cramped /*cramp the style*/
+@d sub_style(A) 2*(A/4)+script_style+cramped /*smaller and cramped*/
+@d sup_style(A) 2*(A/4)+script_style+(A%2) /*smaller*/
+@d num_style(A) A+2-2*(A/6) /*smaller unless already script-script*/
+@d denom_style(A) 2*(A/2)+cramped+2-2*(A/6) /*smaller, cramped*/
+
+@ When the style changes, the following piece of program computes associated
+information:
+
+@<Set up the values of |cur_size| and |cur_mu|, based on |cur_style|@>=
+{@+if (cur_style < script_style) cur_size=text_size;
+else cur_size=16*((cur_style-text_style)/2);
+cur_mu=x_over_n(math_quad(cur_size), 18);
+}
+
+@ Here is a function that returns a pointer to a rule node having a given
+thickness |t|. The rule will extend horizontally to the boundary of the vlist
+that eventually contains it.
+
+@p static pointer fraction_rule(scaled @!t)
+ /*construct the bar for a fraction*/
+{@+pointer p; /*the new node*/
+p=new_rule();height(p)=t;depth(p)=0;return p;
+}
+
+@ The |overbar| function returns a pointer to a vlist box that consists of
+a given box |b|, above which has been placed a kern of height |k| under a
+fraction rule of thickness |t| under additional space of height |t|.
+
+@p static pointer overbar(pointer @!b, scaled @!k, scaled @!t)
+{@+pointer p, @!q; /*nodes being constructed*/
+p=new_kern(k);link(p)=b;q=fraction_rule(t);link(q)=p;
+p=new_kern(t);link(p)=q;return vpack(p, natural);
+}
+
+@ The |var_delimiter| function, which finds or constructs a sufficiently
+large delimiter, is the most interesting of the auxiliary functions that
+currently concern us. Given a pointer |d| to a delimiter field in some noad,
+together with a size code |s| and a vertical distance |v|, this function
+returns a pointer to a box that contains the smallest variant of |d| whose
+height plus depth is |v| or more. (And if no variant is large enough, it
+returns the largest available variant.) In particular, this routine will
+construct arbitrarily large delimiters from extensible components, if
+|d| leads to such characters.
+
+The value returned is a box whose |shift_amount| has been set so that
+the box is vertically centered with respect to the axis in the given size.
+If a built-up symbol is returned, the height of the box before shifting
+will be the height of its topmost component.
+
+@p @t\4@>@<Declare subprocedures for |var_delimiter|@>@;
+static pointer var_delimiter(pointer @!d, small_number @!s, scaled @!v)
+{@+
+pointer b; /*the box that will be constructed*/
+internal_font_number @!f, @!g; /*best-so-far and tentative font codes*/
+quarterword @!c, @!x, @!y; /*best-so-far and tentative character codes*/
+int @!m, @!n; /*the number of extensible pieces*/
+scaled @!u; /*height-plus-depth of a tentative character*/
+scaled @!w; /*largest height-plus-depth so far*/
+four_quarters @!q; /*character info*/
+eight_bits @!hd; /*height-depth byte*/
+four_quarters @!r; /*extensible pieces*/
+small_number @!z; /*runs through font family members*/
+bool @!large_attempt; /*are we trying the ``large'' variant?*/
+f=null_font;w=0;large_attempt=false;
+z=small_fam(d);x=small_char(d);
+loop@+{@+@<Look at the variants of |(z,x)|; set |f| and |c| whenever a better
+character is found; |goto found| as soon as a large enough variant is encountered@>;
+ if (large_attempt) goto found; /*there were none large enough*/
+ large_attempt=true;z=large_fam(d);x=large_char(d);
+ }
+found: if (f!=null_font)
+ @<Make variable |b| point to a box for |(f,c)|@>;
+else{@+b=new_null_box();
+ width(b)=null_delimiter_space; /*use this width if no delimiter was found*/
+ }
+shift_amount(b)=half(height(b)-depth(b))-axis_height(s);
+return b;
+}
+
+@ The search process is complicated slightly by the facts that some of the
+characters might not be present in some of the fonts, and they might not
+be probed in increasing order of height.
+
+@<Look at the variants of |(z,x)|; set |f| and |c|...@>=
+if ((z!=0)||(x!=min_quarterword))
+ {@+z=z+s+16;
+ @/do@+{z=z-16;g=fam_fnt(z);
+ if (g!=null_font)
+ @<Look at the list of characters starting with |x| in font |g|; set |f|
+and |c| whenever a better character is found; |goto found| as soon as a large
+enough variant is encountered@>;
+ }@+ while (!(z < 16));
+ }
+
+@ @<Look at the list of characters starting with |x|...@>=
+{@+y=x;
+if ((qo(y) >= font_bc[g])&&(qo(y) <= font_ec[g]))
+ {@+resume: q=char_info(g, y);
+ if (char_exists(q))
+ {@+if (char_tag(q)==ext_tag)
+ {@+f=g;c=y;goto found;
+ }
+ hd=height_depth(q);
+ u=char_height(g, hd)+char_depth(g, hd);
+ if (u > w)
+ {@+f=g;c=y;w=u;
+ if (u >= v) goto found;
+ }
+ if (char_tag(q)==list_tag)
+ {@+y=rem_byte(q);goto resume;
+ }
+ }
+ }
+}
+
+@ Here is a subroutine that creates a new box, whose list contains a
+single character, and whose width includes the italic correction for
+that character. The height or depth of the box will be negative, if
+the height or depth of the character is negative; thus, this routine
+may deliver a slightly different result than |hpack| would produce.
+
+@<Declare subprocedures for |var_delimiter|@>=
+static pointer char_box(internal_font_number @!f, quarterword @!c)
+{@+four_quarters q;
+eight_bits @!hd; /*|height_depth| byte*/
+pointer @!b, @!p; /*the new box and its character node*/
+q=char_info(f, c);hd=height_depth(q);
+b=new_null_box();width(b)=char_width(f, q)+char_italic(f, q);
+height(b)=char_height(f, hd);depth(b)=char_depth(f, hd);
+p=get_avail();character(p)=c;font(p)=f;list_ptr(b)=p;return b;
+}
+
+@ When the following code is executed, |char_tag(q)| will be equal to
+|ext_tag| if and only if a built-up symbol is supposed to be returned.
+
+@<Make variable |b| point to a box for |(f,c)|@>=
+if (char_tag(q)==ext_tag)
+ @<Construct an extensible character in a new box |b|, using recipe |rem_byte(q)|
+and font |f|@>@;
+else b=char_box(f, c)
+
+@ When we build an extensible character, it's handy to have the
+following subroutine, which puts a given character on top
+of the characters already in box |b|:
+
+@<Declare subprocedures for |var_delimiter|@>=
+static void stack_into_box(pointer @!b, internal_font_number @!f,
+ quarterword @!c)
+{@+pointer p; /*new node placed into |b|*/
+p=char_box(f, c);link(p)=list_ptr(b);list_ptr(b)=p;
+height(b)=height(p);
+}
+
+@ Another handy subroutine computes the height plus depth of
+a given character:
+
+@<Declare subprocedures for |var_delimiter|@>=
+static scaled height_plus_depth(internal_font_number @!f, quarterword @!c)
+{@+four_quarters q;
+eight_bits @!hd; /*|height_depth| byte*/
+q=char_info(f, c);hd=height_depth(q);
+return char_height(f, hd)+char_depth(f, hd);
+}
+
+@ @<Construct an extensible...@>=
+{@+b=new_null_box();
+type(b)=vlist_node;
+r=font_info[exten_base[f]+rem_byte(q)].qqqq;@/
+@<Compute the minimum suitable height, |w|, and the corresponding number of
+extension steps, |n|; also set |width(b)|@>;
+c=ext_bot(r);
+if (c!=min_quarterword) stack_into_box(b, f, c);
+c=ext_rep(r);
+for (m=1; m<=n; m++) stack_into_box(b, f, c);
+c=ext_mid(r);
+if (c!=min_quarterword)
+ {@+stack_into_box(b, f, c);c=ext_rep(r);
+ for (m=1; m<=n; m++) stack_into_box(b, f, c);
+ }
+c=ext_top(r);
+if (c!=min_quarterword) stack_into_box(b, f, c);
+depth(b)=w-height(b);
+}
+
+@ The width of an extensible character is the width of the repeatable
+module. If this module does not have positive height plus depth,
+we don't use any copies of it, otherwise we use as few as possible
+(in groups of two if there is a middle part).
+
+@<Compute the minimum suitable height, |w|, and...@>=
+c=ext_rep(r);u=height_plus_depth(f, c);
+w=0;q=char_info(f, c);width(b)=char_width(f, q)+char_italic(f, q);@/
+c=ext_bot(r);@+if (c!=min_quarterword) w=w+height_plus_depth(f, c);
+c=ext_mid(r);@+if (c!=min_quarterword) w=w+height_plus_depth(f, c);
+c=ext_top(r);@+if (c!=min_quarterword) w=w+height_plus_depth(f, c);
+n=0;
+if (u > 0) while (w < v)
+ {@+w=w+u;incr(n);
+ if (ext_mid(r)!=min_quarterword) w=w+u;
+ }
+
+@ The next subroutine is much simpler; it is used for numerators and
+denominators of fractions as well as for displayed operators and
+their limits above and below. It takes a given box~|b| and
+changes it so that the new box is centered in a box of width~|w|.
+The centering is done by putting \.{\\hss} glue at the left and right
+of the list inside |b|, then packaging the new box; thus, the
+actual box might not really be centered, if it already contains
+infinite glue.
+
+The given box might contain a single character whose italic correction
+has been added to the width of the box; in this case a compensating
+kern is inserted.
+
+@p static pointer rebox(pointer @!b, scaled @!w)
+{@+pointer p; /*temporary register for list manipulation*/
+internal_font_number @!f; /*font in a one-character box*/
+scaled @!v; /*width of a character without italic correction*/
+if ((width(b)!=w)&&(list_ptr(b)!=null))
+ {@+if (type(b)==vlist_node) b=hpack(b, natural);
+ p=list_ptr(b);
+ if ((is_char_node(p))&&(link(p)==null))
+ {@+f=font(p);v=char_width(f, char_info(f, character(p)));
+ if (v!=width(b)) link(p)=new_kern(width(b)-v);
+ }
+ free_node(b, box_node_size);
+ b=new_glue(ss_glue);link(b)=p;
+ while (link(p)!=null) p=link(p);
+ link(p)=new_glue(ss_glue);
+ return hpack(b, w, exactly);
+ }
+else{@+width(b)=w;return b;
+ }
+}
+
+@ Here is a subroutine that creates a new glue specification from another
+one that is expressed in `\.{mu}', given the value of the math unit.
+
+@d mu_mult(A) nx_plus_y(n, A, xn_over_d(A, f, 0200000))
+
+@p static pointer math_glue(pointer @!g, scaled @!m)
+{@+pointer p; /*the new glue specification*/
+int @!n; /*integer part of |m|*/
+scaled @!f; /*fraction part of |m|*/
+n=x_over_n(m, 0200000);f=rem;@/
+if (f < 0)
+ {@+decr(n);f=f+0200000;
+ }
+p=get_node(glue_spec_size);
+width(p)=mu_mult(width(g)); /*convert \.{mu} to \.{pt}*/
+stretch_order(p)=stretch_order(g);
+if (stretch_order(p)==normal) stretch(p)=mu_mult(stretch(g));
+else stretch(p)=stretch(g);
+shrink_order(p)=shrink_order(g);
+if (shrink_order(p)==normal) shrink(p)=mu_mult(shrink(g));
+else shrink(p)=shrink(g);
+return p;
+}
+
+@ The |math_kern| subroutine removes |mu_glue| from a kern node, given
+the value of the math unit.
+
+@p static void math_kern(pointer @!p, scaled @!m)
+{@+int @!n; /*integer part of |m|*/
+scaled @!f; /*fraction part of |m|*/
+if (subtype(p)==mu_glue)
+ {@+n=x_over_n(m, 0200000);f=rem;@/
+ if (f < 0)
+ {@+decr(n);f=f+0200000;
+ }
+ width(p)=mu_mult(width(p));subtype(p)=explicit;
+ }
+}
+
+@ Sometimes it is necessary to destroy an mlist. The following
+subroutine empties the current list, assuming that |abs(mode)==mmode|.
+
+@p static void flush_math(void)
+{@+flush_node_list(link(head));flush_node_list(incompleat_noad);
+link(head)=null;tail=head;incompleat_noad=null;
+}
+
+@* Typesetting math formulas.
+\TeX's most important routine for dealing with formulas is called
+|mlist_to_hlist|. After a formula has been scanned and represented as an
+mlist, this routine converts it to an hlist that can be placed into a box
+or incorporated into the text of a paragraph. There are three implicit
+parameters, passed in global variables: |cur_mlist| points to the first
+node or noad in the given mlist (and it might be |null|); |cur_style| is a
+style code; and |mlist_penalties| is |true| if penalty nodes for potential
+line breaks are to be inserted into the resulting hlist. After
+|mlist_to_hlist| has acted, |link(temp_head)| points to the translated hlist.
+
+Since mlists can be inside mlists, the procedure is recursive. And since this
+is not part of \TeX's inner loop, the program has been written in a manner
+that stresses compactness over efficiency.
+@^recursion@>
+
+@<Glob...@>=
+static pointer @!cur_mlist; /*beginning of mlist to be translated*/
+static small_number @!cur_style; /*style code at current place in the list*/
+static small_number @!cur_size; /*size code corresponding to |cur_style|*/
+static scaled @!cur_mu; /*the math unit width corresponding to |cur_size|*/
+static bool @!mlist_penalties; /*should |mlist_to_hlist| insert penalties?*/
+
+@ The recursion in |mlist_to_hlist| is due primarily to a subroutine
+called |clean_box| that puts a given noad field into a box using a given
+math style; |mlist_to_hlist| can call |clean_box|, which can call
+|mlist_to_hlist|.
+@^recursion@>
+
+The box returned by |clean_box| is ``clean'' in the
+sense that its |shift_amount| is zero.
+
+@p static void mlist_to_hlist(void);@/
+static pointer clean_box(pointer @!p, small_number @!s)
+{@+
+pointer q; /*beginning of a list to be boxed*/
+small_number @!save_style; /*|cur_style| to be restored*/
+pointer @!x; /*box to be returned*/
+pointer @!r; /*temporary pointer*/
+switch (math_type(p)) {
+case math_char: {@+cur_mlist=new_noad();mem[nucleus(cur_mlist)]=mem[p];
+ } @+break;
+case sub_box: {@+q=info(p);goto found;
+ }
+case sub_mlist: cur_mlist=info(p);@+break;
+default:{@+q=new_null_box();goto found;
+ }
+} @/
+save_style=cur_style;cur_style=s;mlist_penalties=false;@/
+mlist_to_hlist();q=link(temp_head); /*recursive call*/
+cur_style=save_style; /*restore the style*/
+@<Set up the values of |cur_size| and |cur_mu|, based on |cur_style|@>;
+found: if (is_char_node(q)||(q==null)) x=hpack(q, natural);
+ else if ((link(q)==null)&&(type(q) <= vlist_node)&&(shift_amount(q)==0))
+ x=q; /*it's already clean*/
+ else x=hpack(q, natural);
+@<Simplify a trivial box@>;
+return x;
+}
+
+@ Here we save memory space in a common case.
+
+@<Simplify a trivial box@>=
+q=list_ptr(x);
+if (is_char_node(q))
+ {@+r=link(q);
+ if (r!=null) if (link(r)==null) if (!is_char_node(r))
+ if (type(r)==kern_node) /*unneeded italic correction*/
+ {@+free_node(r, small_node_size);link(q)=null;
+ }
+ }
+
+@ It is convenient to have a procedure that converts a |math_char|
+field to an ``unpacked'' form. The |fetch| routine sets |cur_f|, |cur_c|,
+and |cur_i| to the font code, character code, and character information bytes of
+a given noad field. It also takes care of issuing error messages for
+nonexistent characters; in such cases, |char_exists(cur_i)| will be |false|
+after |fetch| has acted, and the field will also have been reset to |empty|.
+
+@p static void fetch(pointer @!a) /*unpack the |math_char| field |a|*/
+{@+cur_c=character(a);cur_f=fam_fnt(fam(a)+cur_size);
+if (cur_f==null_font)
+ @<Complain about an undefined family and set |cur_i| null@>@;
+else{@+if ((qo(cur_c) >= font_bc[cur_f])&&(qo(cur_c) <= font_ec[cur_f]))
+ cur_i=char_info(cur_f, cur_c);
+ else cur_i=null_character;
+ if (!(char_exists(cur_i)))
+ {@+char_warning(cur_f, qo(cur_c));
+ math_type(a)=empty;cur_i=null_character;
+ }
+ }
+}
+
+@ @<Complain about an undefined family...@>=
+{@+print_err("");print_size(cur_size);print_char(' ');
+print_int(fam(a));print(" is undefined (character ");
+print_ASCII(qo(cur_c));print_char(')');
+help4("Somewhere in the math formula just ended, you used the",@/
+"stated character from an undefined font family. For example,",@/
+"plain TeX doesn't allow \\it or \\sl in subscripts. Proceed,",@/
+"and I'll try to forget that I needed that character.");
+error();cur_i=null_character;math_type(a)=empty;
+}
+
+@ The outputs of |fetch| are placed in global variables.
+
+@<Glob...@>=
+static internal_font_number @!cur_f; /*the |font| field of a |math_char|*/
+static quarterword @!cur_c; /*the |character| field of a |math_char|*/
+static four_quarters @!cur_i; /*the |char_info| of a |math_char|,
+ or a lig/kern instruction*/
+
+@ We need to do a lot of different things, so |mlist_to_hlist| makes two
+passes over the given mlist.
+
+The first pass does most of the processing: It removes ``mu'' spacing from
+glue, it recursively evaluates all subsidiary mlists so that only the
+top-level mlist remains to be handled, it puts fractions and square roots
+and such things into boxes, it attaches subscripts and superscripts, and
+it computes the overall height and depth of the top-level mlist so that
+the size of delimiters for a |left_noad| and a |right_noad| will be known.
+The hlist resulting from each noad is recorded in that noad's |new_hlist|
+field, an integer field that replaces the |nucleus| or |thickness|.
+@^recursion@>
+
+The second pass eliminates all noads and inserts the correct glue and
+penalties between nodes.
+
+@d new_hlist(A) mem[nucleus(A)].i /*the translation of an mlist*/
+
+@ Here is the overall plan of |mlist_to_hlist|, and the list of its
+local variables.
+
+@p @t\4@>@<Declare math construction procedures@>@;
+static void mlist_to_hlist(void)
+{@+
+
+pointer mlist; /*beginning of the given list*/
+bool @!penalties; /*should penalty nodes be inserted?*/
+small_number @!style; /*the given style*/
+small_number @!save_style; /*holds |cur_style| during recursion*/
+pointer @!q; /*runs through the mlist*/
+pointer @!r; /*the most recent noad preceding |q|*/
+small_number @!r_type; /*the |type| of noad |r|, or |op_noad| if |r==null|*/
+small_number @!t; /*the effective |type| of noad |q| during the second pass*/
+pointer @!p, @!x, @!y, @!z; /*temporary registers for list construction*/
+int @!pen; /*a penalty to be inserted*/
+small_number @!s; /*the size of a noad to be deleted*/
+scaled @!max_h, @!max_d; /*maximum height and depth of the list translated so far*/
+scaled @!delta; /*offset between subscript and superscript*/
+mlist=cur_mlist;penalties=mlist_penalties;
+style=cur_style; /*tuck global parameters away as local variables*/
+q=mlist;r=null;r_type=op_noad;max_h=0;max_d=0;
+@<Set up the values of |cur_size| and |cur_mu|, based on |cur_style|@>;
+while (q!=null) @<Process node-or-noad |q| as much as possible in preparation
+for the second pass of |mlist_to_hlist|, then move to the next item in the
+mlist@>;
+@<Convert \(a)a final |bin_noad| to an |ord_noad|@>;
+@<Make a second pass over the mlist, removing all noads and inserting the
+proper spacing and penalties@>;
+}
+
+@ We use the fact that no character nodes appear in an mlist, hence
+the field |type(q)| is always present.
+
+@<Process node-or-noad...@>=
+{@+@<Do first-pass processing based on |type(q)|; |goto done_with_noad| if
+a noad has been fully processed, |goto check_dimensions| if it has been translated
+into |new_hlist(q)|, or |goto done_with_node| if a node has been fully processed@>;
+check_dimensions: z=hpack(new_hlist(q), natural);
+if (height(z) > max_h) max_h=height(z);
+if (depth(z) > max_d) max_d=depth(z);
+free_node(z, box_node_size);
+done_with_noad: r=q;r_type=type(r);
+if (r_type==right_noad)
+ {@+r_type=left_noad;cur_style=style;@<Set up the values...@>;
+ }
+done_with_node: q=link(q);
+}
+
+@ One of the things we must do on the first pass is change a |bin_noad| to
+an |ord_noad| if the |bin_noad| is not in the context of a binary operator.
+The values of |r| and |r_type| make this fairly easy.
+
+@<Do first-pass processing...@>=
+reswitch: delta=0;
+switch (type(q)) {
+case bin_noad: switch (r_type) {
+ case bin_noad: case op_noad: case rel_noad:
+ case open_noad: case punct_noad: case left_noad:
+ {@+type(q)=ord_noad;goto reswitch;
+ }
+ default:do_nothing;
+ } @+break;
+case rel_noad: case close_noad: case punct_noad:
+ case right_noad: {@+@t@>@;@/
+ @<Convert \(a)a final |bin_noad| to an |ord_noad|@>;
+ if (type(q)==right_noad) goto done_with_noad;
+ } @+break;
+@t\4@>@<Cases for noads that can follow a |bin_noad|@>@;
+@t\4@>@<Cases for nodes that can appear in an mlist, after which we |goto
+done_with_node|@>@;
+default:confusion("mlist1");
+@:this can't happen mlist1}{\quad mlist1@>
+} @/
+@<Convert \(n)|nucleus(q)| to an hlist and attach the sub/superscripts@>@;
+
+@ @<Convert \(a)a final |bin_noad| to an |ord_noad|@>=
+if (r_type==bin_noad) type(r)=ord_noad
+
+@ @<Cases for nodes that can appear in an mlist...@>=
+case style_node: {@+cur_style=subtype(q);
+ @<Set up the values of |cur_size| and |cur_mu|, based on |cur_style|@>;
+ goto done_with_node;
+ }
+case choice_node: @<Change this node to a style node followed by the correct
+choice, then |goto done_with_node|@>@;
+case ins_node: case mark_node: case adjust_node:
+ case whatsit_node: case penalty_node:
+ case disc_node: goto done_with_node;
+case rule_node: {@+if (height(q) > max_h) max_h=height(q);
+ if (depth(q) > max_d) max_d=depth(q);goto done_with_node;
+ }
+case glue_node: {@+@<Convert \(m)math glue to ordinary glue@>;
+ goto done_with_node;
+ }
+case kern_node: {@+math_kern(q, cur_mu);goto done_with_node;
+ }
+
+@ @d choose_mlist(A) {@+p=A(q);A(q)=null;@+}
+
+@<Change this node to a style node...@>=
+{@+switch (cur_style/2) {
+case 0: choose_mlist(display_mlist)@;@+break; /*|display_style==0|*/
+case 1: choose_mlist(text_mlist)@;@+break; /*|text_style==2|*/
+case 2: choose_mlist(script_mlist)@;@+break; /*|script_style==4|*/
+case 3: choose_mlist(script_script_mlist); /*|script_script_style==6|*/
+} /*there are no other cases*/
+flush_node_list(display_mlist(q));
+flush_node_list(text_mlist(q));
+flush_node_list(script_mlist(q));
+flush_node_list(script_script_mlist(q));@/
+type(q)=style_node;subtype(q)=cur_style;width(q)=0;depth(q)=0;
+if (p!=null)
+ {@+z=link(q);link(q)=p;
+ while (link(p)!=null) p=link(p);
+ link(p)=z;
+ }
+goto done_with_node;
+}
+
+@ Conditional math glue (`\.{\\nonscript}') results in a |glue_node|
+pointing to |zero_glue|, with |subtype(q)==cond_math_glue|; in such a case
+the node following will be eliminated if it is a glue or kern node and if the
+current size is different from |text_size|. Unconditional math glue
+(`\.{\\muskip}') is converted to normal glue by multiplying the dimensions
+by |cur_mu|.
+@!@:non\_script\_}{\.{\\nonscript} primitive@>
+
+@<Convert \(m)math glue to ordinary glue@>=
+if (subtype(q)==mu_glue)
+ {@+x=glue_ptr(q);
+ y=math_glue(x, cur_mu);delete_glue_ref(x);glue_ptr(q)=y;
+ subtype(q)=normal;
+ }
+else if ((cur_size!=text_size)&&(subtype(q)==cond_math_glue))
+ {@+p=link(q);
+ if (p!=null) if ((type(p)==glue_node)||(type(p)==kern_node))
+ {@+link(q)=link(p);link(p)=null;flush_node_list(p);
+ }
+ }
+
+@ @<Cases for noads that can follow a |bin_noad|@>=
+case left_noad: goto done_with_noad;
+case fraction_noad: {@+make_fraction(q);goto check_dimensions;
+ }
+case op_noad: {@+delta=make_op(q);
+ if (subtype(q)==limits) goto check_dimensions;
+ } @+break;
+case ord_noad: make_ord(q);@+break;
+case open_noad: case inner_noad: do_nothing;@+break;
+case radical_noad: make_radical(q);@+break;
+case over_noad: make_over(q);@+break;
+case under_noad: make_under(q);@+break;
+case accent_noad: make_math_accent(q);@+break;
+case vcenter_noad: make_vcenter(q);@+break;
+
+@ Most of the actual construction work of |mlist_to_hlist| is done
+by procedures with names
+like |make_fraction|, |make_radical|, etc. To illustrate
+the general setup of such procedures, let's begin with a couple of
+simple ones.
+
+@<Declare math...@>=
+static void make_over(pointer @!q)
+{@+info(nucleus(q))=@|
+ overbar(clean_box(nucleus(q), cramped_style(cur_style)),@|
+ 3*default_rule_thickness, default_rule_thickness);
+math_type(nucleus(q))=sub_box;
+}
+
+@ @<Declare math...@>=
+static void make_under(pointer @!q)
+{@+pointer p, @!x, @!y; /*temporary registers for box construction*/
+scaled @!delta; /*overall height plus depth*/
+x=clean_box(nucleus(q), cur_style);
+p=new_kern(3*default_rule_thickness);link(x)=p;
+link(p)=fraction_rule(default_rule_thickness);
+y=vpack(x, natural);
+delta=height(y)+depth(y)+default_rule_thickness;
+height(y)=height(x);depth(y)=delta-height(y);
+info(nucleus(q))=y;math_type(nucleus(q))=sub_box;
+}
+
+@ @<Declare math...@>=
+static void make_vcenter(pointer @!q)
+{@+pointer v; /*the box that should be centered vertically*/
+scaled @!delta; /*its height plus depth*/
+v=info(nucleus(q));
+if (type(v)!=vlist_node) confusion("vcenter");
+@:this can't happen vcenter}{\quad vcenter@>
+delta=height(v)+depth(v);
+height(v)=axis_height(cur_size)+half(delta);
+depth(v)=delta-height(v);
+}
+
+@ According to the rules in the \.{DVI} file specifications, we ensure alignment
+@^square roots@>
+between a square root sign and the rule above its nucleus by assuming that the
+baseline of the square-root symbol is the same as the bottom of the rule. The
+height of the square-root symbol will be the thickness of the rule, and the
+depth of the square-root symbol should exceed or equal the height-plus-depth
+of the nucleus plus a certain minimum clearance~|clr|. The symbol will be
+placed so that the actual clearance is |clr| plus half the excess.
+
+@<Declare math...@>=
+static void make_radical(pointer @!q)
+{@+pointer x, @!y; /*temporary registers for box construction*/
+scaled @!delta, @!clr; /*dimensions involved in the calculation*/
+x=clean_box(nucleus(q), cramped_style(cur_style));
+if (cur_style < text_style) /*display style*/
+ clr=default_rule_thickness+(abs(math_x_height(cur_size))/4);
+else{@+clr=default_rule_thickness;clr=clr+(abs(clr)/4);
+ }
+y=var_delimiter(left_delimiter(q), cur_size, height(x)+depth(x)+clr+
+ default_rule_thickness);
+delta=depth(y)-(height(x)+depth(x)+clr);
+if (delta > 0) clr=clr+half(delta); /*increase the actual clearance*/
+shift_amount(y)=-(height(x)+clr);
+link(y)=overbar(x, clr, height(y));
+info(nucleus(q))=hpack(y, natural);math_type(nucleus(q))=sub_box;
+}
+
+@ Slants are not considered when placing accents in math mode. The accenter is
+centered over the accentee, and the accent width is treated as zero with
+respect to the size of the final box.
+
+@<Declare math...@>=
+static void make_math_accent(pointer @!q)
+{@+
+pointer p, @!x, @!y; /*temporary registers for box construction*/
+int @!a; /*address of lig/kern instruction*/
+quarterword @!c; /*accent character*/
+internal_font_number @!f; /*its font*/
+four_quarters @!i; /*its |char_info|*/
+scaled @!s; /*amount to skew the accent to the right*/
+scaled @!h; /*height of character being accented*/
+scaled @!delta; /*space to remove between accent and accentee*/
+scaled @!w; /*width of the accentee, not including sub/superscripts*/
+fetch(accent_chr(q));
+if (char_exists(cur_i))
+ {@+i=cur_i;c=cur_c;f=cur_f;@/
+ @<Compute the amount of skew@>;
+ x=clean_box(nucleus(q), cramped_style(cur_style));w=width(x);h=height(x);
+ @<Switch to a larger accent if available and appropriate@>;
+ if (h < x_height(f)) delta=h;@+else delta=x_height(f);
+ if ((math_type(supscr(q))!=empty)||(math_type(subscr(q))!=empty))
+ if (math_type(nucleus(q))==math_char)
+ @<Swap the subscript and superscript into box |x|@>;
+ y=char_box(f, c);
+ shift_amount(y)=s+half(w-width(y));
+ width(y)=0;p=new_kern(-delta);link(p)=x;link(y)=p;
+ y=vpack(y, natural);width(y)=width(x);
+ if (height(y) < h) @<Make the height of box |y| equal to |h|@>;
+ info(nucleus(q))=y;
+ math_type(nucleus(q))=sub_box;
+ }
+}
+
+@ @<Make the height of box |y|...@>=
+{@+p=new_kern(h-height(y));link(p)=list_ptr(y);list_ptr(y)=p;
+height(y)=h;
+}
+
+@ @<Switch to a larger accent if available and appropriate@>=
+loop@+{@+if (char_tag(i)!=list_tag) goto done;
+ y=rem_byte(i);
+ i=char_info(f, y);
+ if (!char_exists(i)) goto done;
+ if (char_width(f, i) > w) goto done;
+ c=y;
+ }
+done:
+
+@ @<Compute the amount of skew@>=
+s=0;
+if (math_type(nucleus(q))==math_char)
+ {@+fetch(nucleus(q));
+ if (char_tag(cur_i)==lig_tag)
+ {@+a=lig_kern_start(cur_f, cur_i);
+ cur_i=font_info[a].qqqq;
+ if (skip_byte(cur_i) > stop_flag)
+ {@+a=lig_kern_restart(cur_f, cur_i);
+ cur_i=font_info[a].qqqq;
+ }
+ loop@+{@+if (qo(next_char(cur_i))==skew_char[cur_f])
+ {@+if (op_byte(cur_i) >= kern_flag)
+ if (skip_byte(cur_i) <= stop_flag) s=char_kern(cur_f, cur_i);
+ goto done1;
+ }
+ if (skip_byte(cur_i) >= stop_flag) goto done1;
+ a=a+qo(skip_byte(cur_i))+1;
+ cur_i=font_info[a].qqqq;
+ }
+ }
+ }
+done1:
+
+@ @<Swap the subscript and superscript into box |x|@>=
+{@+flush_node_list(x);x=new_noad();
+mem[nucleus(x)]=mem[nucleus(q)];
+mem[supscr(x)]=mem[supscr(q)];
+mem[subscr(x)]=mem[subscr(q)];@/
+mem[supscr(q)].hh=empty_field;
+mem[subscr(q)].hh=empty_field;@/
+math_type(nucleus(q))=sub_mlist;info(nucleus(q))=x;
+x=clean_box(nucleus(q), cur_style);delta=delta+height(x)-h;h=height(x);
+}
+
+@ The |make_fraction| procedure is a bit different because it sets
+|new_hlist(q)| directly rather than making a sub-box.
+
+@<Declare math...@>=
+static void make_fraction(pointer @!q)
+{@+pointer p, @!v, @!x, @!y, @!z; /*temporary registers for box construction*/
+scaled @!delta, @!delta1, @!delta2, @!shift_up, @!shift_down, @!clr;
+ /*dimensions for box calculations*/
+if (thickness(q)==default_code) thickness(q)=default_rule_thickness;
+@<Create equal-width boxes |x| and |z| for the numerator and denominator,
+and compute the default amounts |shift_up| and |shift_down| by which they
+are displaced from the baseline@>;
+if (thickness(q)==0) @<Adjust \(s)|shift_up| and |shift_down| for the case
+of no fraction line@>@;
+else@<Adjust \(s)|shift_up| and |shift_down| for the case of a fraction line@>;
+@<Construct a vlist box for the fraction, according to |shift_up| and |shift_down|@>;
+@<Put the \(f)fraction into a box with its delimiters, and make |new_hlist(q)|
+point to it@>;
+}
+
+@ @<Create equal-width boxes |x| and |z| for the numerator and denom...@>=
+x=clean_box(numerator(q), num_style(cur_style));
+z=clean_box(denominator(q), denom_style(cur_style));
+if (width(x) < width(z)) x=rebox(x, width(z));
+else z=rebox(z, width(x));
+if (cur_style < text_style) /*display style*/
+ {@+shift_up=num1(cur_size);shift_down=denom1(cur_size);
+ }
+else{@+shift_down=denom2(cur_size);
+ if (thickness(q)!=0) shift_up=num2(cur_size);
+ else shift_up=num3(cur_size);
+ }
+
+@ The numerator and denominator must be separated by a certain minimum
+clearance, called |clr| in the following program. The difference between
+|clr| and the actual clearance is twice |delta|.
+
+@<Adjust \(s)|shift_up| and |shift_down| for the case of no fraction line@>=
+{@+if (cur_style < text_style) clr=7*default_rule_thickness;
+else clr=3*default_rule_thickness;
+delta=half(clr-((shift_up-depth(x))-(height(z)-shift_down)));
+if (delta > 0)
+ {@+shift_up=shift_up+delta;
+ shift_down=shift_down+delta;
+ }
+}
+
+@ In the case of a fraction line, the minimum clearance depends on the actual
+thickness of the line.
+
+@<Adjust \(s)|shift_up| and |shift_down| for the case of a fraction line@>=
+{@+if (cur_style < text_style) clr=3*thickness(q);
+else clr=thickness(q);
+delta=half(thickness(q));
+delta1=clr-((shift_up-depth(x))-(axis_height(cur_size)+delta));
+delta2=clr-((axis_height(cur_size)-delta)-(height(z)-shift_down));
+if (delta1 > 0) shift_up=shift_up+delta1;
+if (delta2 > 0) shift_down=shift_down+delta2;
+}
+
+@ @<Construct a vlist box for the fraction...@>=
+v=new_null_box();type(v)=vlist_node;
+height(v)=shift_up+height(x);depth(v)=depth(z)+shift_down;
+width(v)=width(x); /*this also equals |width(z)|*/
+if (thickness(q)==0)
+ {@+p=new_kern((shift_up-depth(x))-(height(z)-shift_down));
+ link(p)=z;
+ }
+else{@+y=fraction_rule(thickness(q));@/
+ p=new_kern((axis_height(cur_size)-delta)-@|(height(z)-shift_down));@/
+ link(y)=p;link(p)=z;@/
+ p=new_kern((shift_up-depth(x))-(axis_height(cur_size)+delta));
+ link(p)=y;
+ }
+link(x)=p;list_ptr(v)=x
+
+@ @<Put the \(f)fraction into a box with its delimiters...@>=
+if (cur_style < text_style) delta=delim1(cur_size);
+else delta=delim2(cur_size);
+x=var_delimiter(left_delimiter(q), cur_size, delta);link(x)=v;@/
+z=var_delimiter(right_delimiter(q), cur_size, delta);link(v)=z;@/
+new_hlist(q)=hpack(x, natural)
+
+@ If the nucleus of an |op_noad| is a single character, it is to be
+centered vertically with respect to the axis, after first being enlarged
+(via a character list in the font) if we are in display style. The normal
+convention for placing displayed limits is to put them above and below the
+operator in display style.
+
+The italic correction is removed from the character if there is a subscript
+and the limits are not being displayed. The |make_op|
+routine returns the value that should be used as an offset between
+subscript and superscript.
+
+After |make_op| has acted, |subtype(q)| will be |limits| if and only if
+the limits have been set above and below the operator. In that case,
+|new_hlist(q)| will already contain the desired final box.
+
+@<Declare math...@>=
+static scaled make_op(pointer @!q)
+{@+scaled delta; /*offset between subscript and superscript*/
+pointer @!p, @!v, @!x, @!y, @!z; /*temporary registers for box construction*/
+quarterword @!c;@+four_quarters @!i; /*registers for character examination*/
+scaled @!shift_up, @!shift_down; /*dimensions for box calculation*/
+if ((subtype(q)==normal)&&(cur_style < text_style))
+ subtype(q)=limits;
+if (math_type(nucleus(q))==math_char)
+ {@+fetch(nucleus(q));
+ if ((cur_style < text_style)&&(char_tag(cur_i)==list_tag)) /*make it larger*/
+ {@+c=rem_byte(cur_i);i=char_info(cur_f, c);
+ if (char_exists(i))
+ {@+cur_c=c;cur_i=i;character(nucleus(q))=c;
+ }
+ }
+ delta=char_italic(cur_f, cur_i);x=clean_box(nucleus(q), cur_style);
+ if ((math_type(subscr(q))!=empty)&&(subtype(q)!=limits))
+ width(x)=width(x)-delta; /*remove italic correction*/
+ shift_amount(x)=half(height(x)-depth(x))-axis_height(cur_size);
+ /*center vertically*/
+ math_type(nucleus(q))=sub_box;info(nucleus(q))=x;
+ }
+else delta=0;
+if (subtype(q)==limits)
+ @<Construct a box with limits above and below it, skewed by |delta|@>;
+return delta;
+}
+
+@ The following program builds a vlist box |v| for displayed limits. The
+width of the box is not affected by the fact that the limits may be skewed.
+
+@<Construct a box with limits above and below it...@>=
+{@+x=clean_box(supscr(q), sup_style(cur_style));
+y=clean_box(nucleus(q), cur_style);
+z=clean_box(subscr(q), sub_style(cur_style));
+v=new_null_box();type(v)=vlist_node;width(v)=width(y);
+if (width(x) > width(v)) width(v)=width(x);
+if (width(z) > width(v)) width(v)=width(z);
+x=rebox(x, width(v));y=rebox(y, width(v));z=rebox(z, width(v));@/
+shift_amount(x)=half(delta);shift_amount(z)=-shift_amount(x);
+height(v)=height(y);depth(v)=depth(y);
+@<Attach the limits to |y| and adjust |height(v)|, |depth(v)| to account for
+their presence@>;
+new_hlist(q)=v;
+}
+
+@ We use |shift_up| and |shift_down| in the following program for the
+amount of glue between the displayed operator |y| and its limits |x| and
+|z|. The vlist inside box |v| will consist of |x| followed by |y| followed
+by |z|, with kern nodes for the spaces between and around them.
+
+@<Attach the limits to |y| and adjust |height(v)|, |depth(v)|...@>=
+if (math_type(supscr(q))==empty)
+ {@+free_node(x, box_node_size);list_ptr(v)=y;
+ }
+else{@+shift_up=big_op_spacing3-depth(x);
+ if (shift_up < big_op_spacing1) shift_up=big_op_spacing1;
+ p=new_kern(shift_up);link(p)=y;link(x)=p;@/
+ p=new_kern(big_op_spacing5);link(p)=x;list_ptr(v)=p;
+ height(v)=height(v)+big_op_spacing5+height(x)+depth(x)+shift_up;
+ }
+if (math_type(subscr(q))==empty) free_node(z, box_node_size);
+else{@+shift_down=big_op_spacing4-height(z);
+ if (shift_down < big_op_spacing2) shift_down=big_op_spacing2;
+ p=new_kern(shift_down);link(y)=p;link(p)=z;@/
+ p=new_kern(big_op_spacing5);link(z)=p;
+ depth(v)=depth(v)+big_op_spacing5+height(z)+depth(z)+shift_down;
+ }
+
+@ A ligature found in a math formula does not create a |ligature_node|, because
+there is no question of hyphenation afterwards; the ligature will simply be
+stored in an ordinary |char_node|, after residing in an |ord_noad|.
+
+The |math_type| is converted to |math_text_char| here if we would not want to
+apply an italic correction to the current character unless it belongs
+to a math font (i.e., a font with |space==0|).
+
+No boundary characters enter into these ligatures.
+
+@<Declare math...@>=
+static void make_ord(pointer @!q)
+{@+
+int a; /*address of lig/kern instruction*/
+pointer @!p, @!r; /*temporary registers for list manipulation*/
+restart: @t@>@;@/
+if (math_type(subscr(q))==empty) if (math_type(supscr(q))==empty)
+ if (math_type(nucleus(q))==math_char)
+ {@+p=link(q);
+ if (p!=null) if ((type(p) >= ord_noad)&&(type(p) <= punct_noad))
+ if (math_type(nucleus(p))==math_char)
+ if (fam(nucleus(p))==fam(nucleus(q)))
+ {@+math_type(nucleus(q))=math_text_char;
+ fetch(nucleus(q));
+ if (char_tag(cur_i)==lig_tag)
+ {@+a=lig_kern_start(cur_f, cur_i);
+ cur_c=character(nucleus(p));
+ cur_i=font_info[a].qqqq;
+ if (skip_byte(cur_i) > stop_flag)
+ {@+a=lig_kern_restart(cur_f, cur_i);
+ cur_i=font_info[a].qqqq;
+ }
+ loop@+{@+@<If instruction |cur_i| is a kern with |cur_c|, attach the
+kern after~|q|; or if it is a ligature with |cur_c|, combine noads |q| and~|p|
+appropriately; then |return| if the cursor has moved past a noad, or |goto
+restart|@>;
+ if (skip_byte(cur_i) >= stop_flag) return;
+ a=a+qo(skip_byte(cur_i))+1;
+ cur_i=font_info[a].qqqq;
+ }
+ }
+ }
+ }
+}
+
+@ Note that a ligature between an |ord_noad| and another kind of noad
+is replaced by an |ord_noad|, when the two noads collapse into one.
+But we could make a parenthesis (say) change shape when it follows
+certain letters. Presumably a font designer will define such
+ligatures only when this convention makes sense.
+
+\chardef\@@='174 % vertical line to indicate character retention
+
+@<If instruction |cur_i| is a kern with |cur_c|,...@>=
+if (next_char(cur_i)==cur_c) if (skip_byte(cur_i) <= stop_flag)
+ if (op_byte(cur_i) >= kern_flag)
+ {@+p=new_kern(char_kern(cur_f, cur_i));
+ link(p)=link(q);link(q)=p;return;
+ }
+ else{@+check_interrupt; /*allow a way out of infinite ligature loop*/
+ switch (op_byte(cur_i)) {
+ case qi(1): case qi(5): character(nucleus(q))=rem_byte(cur_i);@+break; /*\.{=:\@@}, \.{=:\@@>}*/
+ case qi(2): case qi(6): character(nucleus(p))=rem_byte(cur_i);@+break; /*\.{\@@=:}, \.{\@@=:>}*/
+ case qi(3): case qi(7): case qi(11): {@+r=new_noad(); /*\.{\@@=:\@@}, \.{\@@=:\@@>}, \.{\@@=:\@@>>}*/
+ character(nucleus(r))=rem_byte(cur_i);
+ fam(nucleus(r))=fam(nucleus(q));@/
+ link(q)=r;link(r)=p;
+ if (op_byte(cur_i) < qi(11)) math_type(nucleus(r))=math_char;
+ else math_type(nucleus(r))=math_text_char; /*prevent combination*/
+ } @+break;
+ default:{@+link(q)=link(p);
+ character(nucleus(q))=rem_byte(cur_i); /*\.{=:}*/
+ mem[subscr(q)]=mem[subscr(p)];mem[supscr(q)]=mem[supscr(p)];@/
+ free_node(p, noad_size);
+ }
+ }
+ if (op_byte(cur_i) > qi(3)) return;
+ math_type(nucleus(q))=math_char;goto restart;
+ }
+
+@ When we get to the following part of the program, we have ``fallen through''
+from cases that did not lead to |check_dimensions| or |done_with_noad| or
+|done_with_node|. Thus, |q|~points to a noad whose nucleus may need to be
+converted to an hlist, and whose subscripts and superscripts need to be
+appended if they are present.
+
+If |nucleus(q)| is not a |math_char|, the variable |delta| is the amount
+by which a superscript should be moved right with respect to a subscript
+when both are present.
+@^subscripts@>
+@^superscripts@>
+
+@<Convert \(n)|nucleus(q)| to an hlist and attach the sub/superscripts@>=
+switch (math_type(nucleus(q))) {
+case math_char: case math_text_char:
+ @<Create a character node |p| for |nucleus(q)|, possibly followed by a kern
+node for the italic correction, and set |delta| to the italic correction if
+a subscript is present@>@;@+break;
+case empty: p=null;@+break;
+case sub_box: p=info(nucleus(q));@+break;
+case sub_mlist: {@+cur_mlist=info(nucleus(q));save_style=cur_style;
+ mlist_penalties=false;mlist_to_hlist(); /*recursive call*/
+@^recursion@>
+ cur_style=save_style;@<Set up the values...@>;
+ p=hpack(link(temp_head), natural);
+ } @+break;
+default:confusion("mlist2");
+@:this can't happen mlist2}{\quad mlist2@>
+} @/
+new_hlist(q)=p;
+if ((math_type(subscr(q))==empty)&&(math_type(supscr(q))==empty))
+ goto check_dimensions;
+make_scripts(q, delta)
+
+@ @<Create a character node |p| for |nucleus(q)|...@>=
+{@+fetch(nucleus(q));
+if (char_exists(cur_i))
+ {@+delta=char_italic(cur_f, cur_i);p=new_character(cur_f, qo(cur_c));
+ if ((math_type(nucleus(q))==math_text_char)&&(space(cur_f)!=0))
+ delta=0; /*no italic correction in mid-word of text font*/
+ if ((math_type(subscr(q))==empty)&&(delta!=0))
+ {@+link(p)=new_kern(delta);delta=0;
+ }
+ }
+else p=null;
+}
+
+@ The purpose of |make_scripts(q, delta)| is to attach the subscript and/or
+superscript of noad |q| to the list that starts at |new_hlist(q)|,
+given that the subscript and superscript aren't both empty. The superscript
+will appear to the right of the subscript by a given distance |delta|.
+
+We set |shift_down| and |shift_up| to the minimum amounts to shift the
+baseline of subscripts and superscripts based on the given nucleus.
+
+@<Declare math...@>=
+static void make_scripts(pointer @!q, scaled @!delta)
+{@+pointer p, @!x, @!y, @!z; /*temporary registers for box construction*/
+scaled @!shift_up, @!shift_down, @!clr; /*dimensions in the calculation*/
+small_number @!t; /*subsidiary size code*/
+p=new_hlist(q);
+if (is_char_node(p))
+ {@+shift_up=0;shift_down=0;
+ }
+else{@+z=hpack(p, natural);
+ if (cur_style < script_style) t=script_size;@+else t=script_script_size;
+ shift_up=height(z)-sup_drop(t);
+ shift_down=depth(z)+sub_drop(t);
+ free_node(z, box_node_size);
+ }
+if (math_type(supscr(q))==empty)
+ @<Construct a subscript box |x| when there is no superscript@>@;
+else{@+@<Construct a superscript box |x|@>;
+ if (math_type(subscr(q))==empty) shift_amount(x)=-shift_up;
+ else@<Construct a sub/superscript combination box |x|, with the superscript
+offset by |delta|@>;
+ }
+if (new_hlist(q)==null) new_hlist(q)=x;
+else{@+p=new_hlist(q);
+ while (link(p)!=null) p=link(p);
+ link(p)=x;
+ }
+}
+
+@ When there is a subscript without a superscript, the top of the subscript
+should not exceed the baseline plus four-fifths of the x-height.
+
+@<Construct a subscript box |x| when there is no superscript@>=
+{@+x=clean_box(subscr(q), sub_style(cur_style));
+width(x)=width(x)+script_space;
+if (shift_down < sub1(cur_size)) shift_down=sub1(cur_size);
+clr=height(x)-(abs(math_x_height(cur_size)*4)/5);
+if (shift_down < clr) shift_down=clr;
+shift_amount(x)=shift_down;
+}
+
+@ The bottom of a superscript should never descend below the baseline plus
+one-fourth of the x-height.
+
+@<Construct a superscript box |x|@>=
+{@+x=clean_box(supscr(q), sup_style(cur_style));
+width(x)=width(x)+script_space;
+if (odd(cur_style)) clr=sup3(cur_size);
+else if (cur_style < text_style) clr=sup1(cur_size);
+else clr=sup2(cur_size);
+if (shift_up < clr) shift_up=clr;
+clr=depth(x)+(abs(math_x_height(cur_size))/4);
+if (shift_up < clr) shift_up=clr;
+}
+
+@ When both subscript and superscript are present, the subscript must be
+separated from the superscript by at least four times |default_rule_thickness|.
+If this condition would be violated, the subscript moves down, after which
+both subscript and superscript move up so that the bottom of the superscript
+is at least as high as the baseline plus four-fifths of the x-height.
+
+@<Construct a sub/superscript combination box |x|...@>=
+{@+y=clean_box(subscr(q), sub_style(cur_style));
+width(y)=width(y)+script_space;
+if (shift_down < sub2(cur_size)) shift_down=sub2(cur_size);
+clr=4*default_rule_thickness-
+ ((shift_up-depth(x))-(height(y)-shift_down));
+if (clr > 0)
+ {@+shift_down=shift_down+clr;
+ clr=(abs(math_x_height(cur_size)*4)/5)-(shift_up-depth(x));
+ if (clr > 0)
+ {@+shift_up=shift_up+clr;
+ shift_down=shift_down-clr;
+ }
+ }
+shift_amount(x)=delta; /*superscript is |delta| to the right of the subscript*/
+p=new_kern((shift_up-depth(x))-(height(y)-shift_down));link(x)=p;link(p)=y;
+x=vpack(x, natural);shift_amount(x)=shift_down;
+}
+
+@ We have now tied up all the loose ends of the first pass of |mlist_to_hlist|.
+The second pass simply goes through and hooks everything together with the
+proper glue and penalties. It also handles the |left_noad| and |right_noad| that
+might be present, since |max_h| and |max_d| are now known. Variable |p| points
+to a node at the current end of the final hlist.
+
+@<Make a second pass over the mlist,...@>=
+p=temp_head;link(p)=null;q=mlist;r_type=0;cur_style=style;
+@<Set up the values of |cur_size| and |cur_mu|, based on |cur_style|@>;
+while (q!=null)
+ {@+@<If node |q| is a style node, change the style and |goto delete_q|;
+otherwise if it is not a noad, put it into the hlist, advance |q|, and |goto
+done|; otherwise set |s| to the size of noad |q|, set |t| to the associated
+type (|ord_noad.. inner_noad|), and set |pen| to the associated penalty@>;
+ @<Append inter-element spacing based on |r_type| and |t|@>;
+ @<Append any |new_hlist| entries for |q|, and any appropriate penalties@>;
+ if (type(q)==right_noad) t=open_noad;
+ r_type=t;
+ delete_q: r=q;q=link(q);free_node(r, s);
+ done: ;}
+
+@ Just before doing the big |case| switch in the second pass, the program
+sets up default values so that most of the branches are short.
+
+@<If node |q| is a style node, change the style...@>=
+t=ord_noad;s=noad_size;pen=inf_penalty;
+switch (type(q)) {
+case op_noad: case open_noad: case close_noad:
+ case punct_noad: case inner_noad: t=type(q);@+break;
+case bin_noad: {@+t=bin_noad;pen=bin_op_penalty;
+ } @+break;
+case rel_noad: {@+t=rel_noad;pen=rel_penalty;
+ } @+break;
+case ord_noad: case vcenter_noad: case over_noad:
+ case under_noad: do_nothing;@+break;
+case radical_noad: s=radical_noad_size;@+break;
+case accent_noad: s=accent_noad_size;@+break;
+case fraction_noad: s=fraction_noad_size;@+break;
+case left_noad: case right_noad: t=make_left_right(q, style, max_d, max_h);@+break;
+case style_node: @<Change the current style and |goto delete_q|@>@;
+case whatsit_node: case penalty_node:
+ case rule_node: case disc_node: case adjust_node:
+ case ins_node: case mark_node:
+ case glue_node: case kern_node: @t@>@;@/
+ {@+link(p)=q;p=q;q=link(q);link(p)=null;goto done;
+ }
+default:confusion("mlist3");
+@:this can't happen mlist3}{\quad mlist3@>
+}
+
+@ The |make_left_right| function constructs a left or right delimiter of
+the required size and returns the value |open_noad| or |close_noad|. The
+|right_noad| and |left_noad| will both be based on the original |style|,
+so they will have consistent sizes.
+
+We use the fact that |right_noad-left_noad==close_noad-open_noad|.
+
+@<Declare math...@>=
+static small_number make_left_right(pointer @!q, small_number @!style,
+ scaled @!max_d, scaled @!max_h)
+{@+scaled delta, @!delta1, @!delta2; /*dimensions used in the calculation*/
+cur_style=style;@<Set up the values...@>;
+delta2=max_d+axis_height(cur_size);
+delta1=max_h+max_d-delta2;
+if (delta2 > delta1) delta1=delta2; /*|delta1| is max distance from axis*/
+delta=(delta1/500)*delimiter_factor;
+delta2=delta1+delta1-delimiter_shortfall;
+if (delta < delta2) delta=delta2;
+new_hlist(q)=var_delimiter(delimiter(q), cur_size, delta);
+return type(q)-(left_noad-open_noad); /*|open_noad| or |close_noad|*/
+}
+
+@ @<Change the current style and |goto delete_q|@>=
+{@+cur_style=subtype(q);s=style_node_size;
+@<Set up the values of |cur_size| and |cur_mu|, based on |cur_style|@>;
+goto delete_q;
+}
+
+@ The inter-element spacing in math formulas depends on an $8\times8$ table that
+\TeX\ preloads as a 64-digit string. The elements of this string have the
+following significance:
+$$\vbox{\halign{#\hfil\cr
+\.0 means no space;\cr
+\.1 means a conditional thin space (\.{\\nonscript\\mskip\\thinmuskip});\cr
+\.2 means a thin space (\.{\\mskip\\thinmuskip});\cr
+\.3 means a conditional medium space
+ (\.{\\nonscript\\mskip\\medmuskip});\cr
+\.4 means a conditional thick space
+ (\.{\\nonscript\\mskip\\thickmuskip});\cr
+\.* means an impossible case.\cr}}$$
+This is all pretty cryptic, but {\sl The \TeX book\/} explains what is
+supposed to happen, and the string makes it happen.
+@:TeXbook}{\sl The \TeX book@>
+
+A global variable |magic_offset| is computed so that if |a| and |b| are
+in the range |ord_noad dotdot inner_noad|, then |str_pool[a*8+b+magic_offset]|
+is the digit for spacing between noad types |a| and |b|.
+
+If \PASCAL\ had provided a good way to preload constant arrays, this part of
+the program would not have been so strange.
+@:PASCAL}{\PASCAL@>
+
+@d math_spacing @;@/
+@t\hskip-35pt@>
+"0234000122*4000133**3**344*0400400*000000234000111*1111112341011"
+@t$ \hskip-35pt$@>
+
+@ @<Glob...@>=
+static const int @!magic_offset=-9*ord_noad; /*used to find inter-element spacing*/
+
+@ @<Append inter-element spacing based on |r_type| and |t|@>=
+if (r_type > 0) /*not the first noad*/
+ {@+switch (so(math_spacing[r_type*8+t+magic_offset])) {
+ case '0': x=0;@+break;
+ case '1': if (cur_style < script_style) x=thin_mu_skip_code;@+else x=0;@+break;
+ case '2': x=thin_mu_skip_code;@+break;
+ case '3': if (cur_style < script_style) x=med_mu_skip_code;@+else x=0;@+break;
+ case '4': if (cur_style < script_style) x=thick_mu_skip_code;@+else x=0;@+break;
+ default:confusion("mlist4");
+@:this can't happen mlist4}{\quad mlist4@>
+ }
+ if (x!=0)
+ {@+y=math_glue(glue_par(x), cur_mu);
+ z=new_glue(y);glue_ref_count(y)=null;link(p)=z;p=z;@/
+ subtype(z)=x+1; /*store a symbolic subtype*/
+ }
+ }
+
+@ We insert a penalty node after the hlist entries of noad |q| if |pen|
+is not an ``infinite'' penalty, and if the node immediately following |q|
+is not a penalty node or a |rel_noad| or absent entirely.
+
+@<Append any |new_hlist| entries for |q|, and any appropriate penalties@>=
+if (new_hlist(q)!=null)
+ {@+link(p)=new_hlist(q);
+ @/do@+{p=link(p);
+ }@+ while (!(link(p)==null));
+ }
+if (penalties) if (link(q)!=null) if (pen < inf_penalty)
+ {@+r_type=type(link(q));
+ if (r_type!=penalty_node) if (r_type!=rel_noad)
+ {@+z=new_penalty(pen);link(p)=z;p=z;
+ }
+ }
+
+@* Alignment.
+It's sort of a miracle whenever \.{\\halign} and \.{\\valign} work, because
+they cut across so many of the control structures of \TeX.
+
+Therefore the
+present page is probably not the best place for a beginner to start reading
+this program; it is better to master everything else first.
+
+Let us focus our thoughts on an example of what the input might be, in order
+to get some idea about how the alignment miracle happens. The example doesn't
+do anything useful, but it is sufficiently general to indicate all of the
+special cases that must be dealt with; please do not be disturbed by its
+apparent complexity and meaninglessness.
+$$\vbox{\halign{\.{#}\hfil\cr
+{}\\tabskip 2pt plus 3pt\cr
+{}\\halign to 300pt\{u1\#v1\&\cr
+\hskip 50pt\\tabskip 1pt plus 1fil u2\#v2\&\cr
+\hskip 50pt u3\#v3\\cr\cr
+\hskip 25pt a1\&\\omit a2\&\\vrule\\cr\cr
+\hskip 25pt \\noalign\{\\vskip 3pt\}\cr
+\hskip 25pt b1\\span b2\\cr\cr
+\hskip 25pt \\omit\&c2\\span\\omit\\cr\}\cr}}$$
+Here's what happens:
+
+\yskip
+(0) When `\.{\\halign to 300pt\{}' is scanned, the |scan_spec| routine
+places the 300pt dimension onto the |save_stack|, and an |align_group|
+code is placed above it. This will make it possible to complete the alignment
+when the matching `\.\}' is found.
+
+(1) The preamble is scanned next. Macros in the preamble are not expanded,
+@^preamble@>
+except as part of a tabskip specification. For example, if \.{u2} had been
+a macro in the preamble above, it would have been expanded, since \TeX\
+must look for `\.{minus...}' as part of the tabskip glue. A ``preamble list''
+is constructed based on the user's preamble; in our case it contains the
+following seven items:
+$$\vbox{\halign{\.{#}\hfil\qquad&(#)\hfil\cr
+{}\\glue 2pt plus 3pt&the tabskip preceding column 1\cr
+{}\\alignrecord, width $-\infty$&preamble info for column 1\cr
+{}\\glue 2pt plus 3pt&the tabskip between columns 1 and 2\cr
+{}\\alignrecord, width $-\infty$&preamble info for column 2\cr
+{}\\glue 1pt plus 1fil&the tabskip between columns 2 and 3\cr
+{}\\alignrecord, width $-\infty$&preamble info for column 3\cr
+{}\\glue 1pt plus 1fil&the tabskip following column 3\cr}}$$
+These ``alignrecord'' entries have the same size as an |unset_node|,
+since they will later be converted into such nodes. However, at the
+moment they have no |type| or |subtype| fields; they have |info| fields
+instead, and these |info| fields are initially set to the value |end_span|,
+for reasons explained below. Furthermore, the alignrecord nodes have no
+|height| or |depth| fields; these are renamed |u_part| and |v_part|,
+and they point to token lists for the templates of the alignment.
+For example, the |u_part| field in the first alignrecord points to the
+token list `\.{u1}', i.e., the template preceding the `\.\#' for column~1.
+
+(2) \TeX\ now looks at what follows the \.{\\cr} that ended the preamble.
+It is not `\.{\\noalign}' or `\.{\\omit}', so this input is put back to
+be read again, and the template `\.{u1}' is fed to the scanner. Just
+before reading `\.{u1}', \TeX\ goes into restricted horizontal mode.
+Just after reading `\.{u1}', \TeX\ will see `\.{a1}', and then (when the
+{\.\&} is sensed) \TeX\ will see `\.{v1}'. Then \TeX\ scans an |endv|
+token, indicating the end of a column. At this point an |unset_node| is
+created, containing the contents of the current hlist (i.e., `\.{u1a1v1}').
+The natural width of this unset node replaces the |width| field of the
+alignrecord for column~1; in general, the alignrecords will record the
+maximum natural width that has occurred so far in a given column.
+
+(3) Since `\.{\\omit}' follows the `\.\&', the templates for column~2
+are now bypassed. Again \TeX\ goes into restricted horizontal mode and
+makes an |unset_node| from the resulting hlist; but this time the
+hlist contains simply `\.{a2}'. The natural width of the new unset box
+is remembered in the |width| field of the alignrecord for column~2.
+
+(4) A third |unset_node| is created for column 3, using essentially the
+mechanism that worked for column~1; this unset box contains `\.{u3\\vrule
+v3}'. The vertical rule in this case has running dimensions that will later
+extend to the height and depth of the whole first row, since each |unset_node|
+in a row will eventually inherit the height and depth of its enclosing box.
+
+(5) The first row has now ended; it is made into a single unset box
+comprising the following seven items:
+$$\vbox{\halign{\hbox to 325pt{\qquad\.{#}\hfil}\cr
+{}\\glue 2pt plus 3pt\cr
+{}\\unsetbox for 1 column: u1a1v1\cr
+{}\\glue 2pt plus 3pt\cr
+{}\\unsetbox for 1 column: a2\cr
+{}\\glue 1pt plus 1fil\cr
+{}\\unsetbox for 1 column: u3\\vrule v3\cr
+{}\\glue 1pt plus 1fil\cr}}$$
+The width of this unset row is unimportant, but it has the correct height
+and depth, so the correct baselineskip glue will be computed as the row
+is inserted into a vertical list.
+
+(6) Since `\.{\\noalign}' follows the current \.{\\cr}, \TeX\ appends
+additional material (in this case \.{\\vskip 3pt}) to the vertical list.
+While processing this material, \TeX\ will be in internal vertical
+mode, and |no_align_group| will be on |save_stack|.
+
+(7) The next row produces an unset box that looks like this:
+$$\vbox{\halign{\hbox to 325pt{\qquad\.{#}\hfil}\cr
+{}\\glue 2pt plus 3pt\cr
+{}\\unsetbox for 2 columns: u1b1v1u2b2v2\cr
+{}\\glue 1pt plus 1fil\cr
+{}\\unsetbox for 1 column: {\rm(empty)}\cr
+{}\\glue 1pt plus 1fil\cr}}$$
+The natural width of the unset box that spans columns 1~and~2 is stored
+in a ``span node,'' which we will explain later; the |info| field of the
+alignrecord for column~1 now points to the new span node, and the |info|
+of the span node points to |end_span|.
+
+(8) The final row produces the unset box
+$$\vbox{\halign{\hbox to 325pt{\qquad\.{#}\hfil}\cr
+{}\\glue 2pt plus 3pt\cr
+{}\\unsetbox for 1 column: {\rm(empty)}\cr
+{}\\glue 2pt plus 3pt\cr
+{}\\unsetbox for 2 columns: u2c2v2\cr
+{}\\glue 1pt plus 1fil\cr}}$$
+A new span node is attached to the alignrecord for column 2.
+
+(9) The last step is to compute the true column widths and to change all the
+unset boxes to hboxes, appending the whole works to the vertical list that
+encloses the \.{\\halign}. The rules for deciding on the final widths of
+each unset column box will be explained below.
+
+\yskip\noindent
+Note that as \.{\\halign} is being processed, we fearlessly give up control
+to the rest of \TeX. At critical junctures, an alignment routine is
+called upon to step in and do some little action, but most of the time
+these routines just lurk in the background. It's something like
+post-hypnotic suggestion.
+
+@ We have mentioned that alignrecords contain no |height| or |depth| fields.
+Their |glue_sign| and |glue_order| are pre-empted as well, since it
+is necessary to store information about what to do when a template ends.
+This information is called the |extra_info| field.
+
+@d u_part(A) mem[A+height_offset].i /*pointer to \<u_j> token list*/
+@d v_part(A) mem[A+depth_offset].i /*pointer to \<v_j> token list*/
+@d extra_info(A) info(A+list_offset) /*info to remember during template*/
+
+@ Alignments can occur within alignments, so a small stack is used to access
+the alignrecord information. At each level we have a |preamble| pointer,
+indicating the beginning of the preamble list; a |cur_align| pointer,
+indicating the current position in the preamble list; a |cur_span| pointer,
+indicating the value of |cur_align| at the beginning of a sequence of
+spanned columns; a |cur_loop| pointer, indicating the tabskip glue before
+an alignrecord that should be copied next if the current list is extended;
+and the |align_state| variable, which indicates the nesting of braces so
+that \.{\\cr} and \.{\\span} and tab marks are properly intercepted.
+There also are pointers |cur_head| and |cur_tail| to the head and tail
+of a list of adjustments being moved out from horizontal mode to
+vertical~mode.
+
+The current values of these seven quantities appear in global variables;
+when they have to be pushed down, they are stored in 5-word nodes, and
+|align_ptr| points to the topmost such node.
+
+@d preamble link(align_head) /*the current preamble list*/
+@d align_stack_node_size 5 /*number of |mem| words to save alignment states*/
+
+@<Glob...@>=
+static pointer @!cur_align; /*current position in preamble list*/
+static pointer @!cur_span; /*start of currently spanned columns in preamble list*/
+static pointer @!cur_loop; /*place to copy when extending a periodic preamble*/
+static pointer @!align_ptr; /*most recently pushed-down alignment stack node*/
+static pointer @!cur_head, @!cur_tail; /*adjustment list pointers*/
+
+@ The |align_state| and |preamble| variables are initialized elsewhere.
+
+@<Set init...@>=
+align_ptr=null;cur_align=null;cur_span=null;cur_loop=null;
+cur_head=null;cur_tail=null;
+
+@ Alignment stack maintenance is handled by a pair of trivial routines
+called |push_alignment| and |pop_alignment|.
+
+@p static void push_alignment(void)
+{@+pointer p; /*the new alignment stack node*/
+p=get_node(align_stack_node_size);
+link(p)=align_ptr;info(p)=cur_align;
+llink(p)=preamble;rlink(p)=cur_span;
+mem[p+2].i=cur_loop;mem[p+3].i=align_state;
+info(p+4)=cur_head;link(p+4)=cur_tail;
+align_ptr=p;
+cur_head=get_avail();
+}
+@#
+static void pop_alignment(void)
+{@+pointer p; /*the top alignment stack node*/
+free_avail(cur_head);
+p=align_ptr;
+cur_tail=link(p+4);cur_head=info(p+4);
+align_state=mem[p+3].i;cur_loop=mem[p+2].i;
+cur_span=rlink(p);preamble=llink(p);
+cur_align=info(p);align_ptr=link(p);
+free_node(p, align_stack_node_size);
+}
+
+@ \TeX\ has eight procedures that govern alignments: |init_align| and
+|fin_align| are used at the very beginning and the very end; |init_row| and
+|fin_row| are used at the beginning and end of individual rows; |init_span|
+is used at the beginning of a sequence of spanned columns (possibly involving
+only one column); |init_col| and |fin_col| are used at the beginning and
+end of individual columns; and |align_peek| is used after \.{\\cr} to see
+whether the next item is \.{\\noalign}.
+
+We shall consider these routines in the order they are first used during
+the course of a complete \.{\\halign}, namely |init_align|, |align_peek|,
+|init_row|, |init_span|, |init_col|, |fin_col|, |fin_row|, |fin_align|.
+
+@ When \.{\\halign} or \.{\\valign} has been scanned in an appropriate
+mode, \TeX\ calls |init_align|, whose task is to get everything off to a
+good start. This mostly involves scanning the preamble and putting its
+information into the preamble list.
+@^preamble@>
+
+@p @t\4@>@<Declare the procedure called |get_preamble_token|@>@t@>@/
+static void align_peek(void);@/
+static void normal_paragraph(void);@/
+static void init_align(void)
+{@+
+pointer save_cs_ptr; /*|warning_index| value for error messages*/
+pointer @!p; /*for short-term temporary use*/
+save_cs_ptr=cur_cs; /*\.{\\halign} or \.{\\valign}, usually*/
+push_alignment();align_state=-1000000; /*enter a new alignment level*/
+@<Check for improper alignment in displayed math@>;
+push_nest(); /*enter a new semantic level*/
+@<Change current mode to |-vmode| for \.{\\halign}, |-hmode| for \.{\\valign}@>;
+scan_spec(align_group, false);@/
+@<Scan the preamble and record it in the |preamble| list@>;
+new_save_level(align_group);
+if (every_cr!=null) begin_token_list(every_cr, every_cr_text);
+align_peek(); /*look for \.{\\noalign} or \.{\\omit}*/
+}
+
+@ In vertical modes, |prev_depth| already has the correct value. But
+if we are in |mmode| (displayed formula mode), we reach out to the
+enclosing vertical mode for the |prev_depth| value that produces the
+correct baseline calculations.
+
+@<Change current mode...@>=
+if (mode==mmode)
+ {@+mode=-vmode;prev_depth=nest[nest_ptr-2].aux_field.sc;
+ }
+else if (mode > 0) negate(mode)
+
+@ When \.{\\halign} is used as a displayed formula, there should be
+no other pieces of mlists present.
+
+@<Check for improper alignment in displayed math@>=
+if ((mode==mmode)&&((tail!=head)||(incompleat_noad!=null)))
+ {@+print_err("Improper ");print_esc("halign");print(" inside $$'s");
+@.Improper \\halign...@>
+ help3("Displays can use special alignments (like \\eqalignno)",@/
+ "only if nothing but the alignment itself is between $$'s.",@/
+ "So I've deleted the formulas that preceded this alignment.");
+ error();flush_math();
+ }
+
+@ @<Scan the preamble and record it in the |preamble| list@>=
+preamble=null;cur_align=align_head;cur_loop=null;scanner_status=aligning;
+warning_index=save_cs_ptr;align_state=-1000000;
+ /*at this point, |cur_cmd==left_brace|*/
+loop@+{@+@<Append the current tabskip glue to the preamble list@>;
+ if (cur_cmd==car_ret) goto done; /*\.{\\cr} ends the preamble*/
+ @<Scan preamble text until |cur_cmd| is |tab_mark| or |car_ret|, looking
+for changes in the tabskip glue; append an alignrecord to the preamble list@>;
+ }
+done: scanner_status=normal
+
+@ @<Append the current tabskip glue to the preamble list@>=
+link(cur_align)=new_param_glue(tab_skip_code);
+cur_align=link(cur_align)
+
+@ @<Scan preamble text until |cur_cmd| is |tab_mark| or |car_ret|...@>=
+@<Scan the template \<u_j>, putting the resulting token list in |hold_head|@>;
+link(cur_align)=new_null_box();cur_align=link(cur_align); /*a new alignrecord*/
+info(cur_align)=end_span;width(cur_align)=null_flag;
+u_part(cur_align)=link(hold_head);
+@<Scan the template \<v_j>, putting the resulting token list in |hold_head|@>;
+v_part(cur_align)=link(hold_head)
+
+@ We enter `\.{\\span}' into |eqtb| with |tab_mark| as its command code,
+and with |span_code| as the command modifier. This makes \TeX\ interpret it
+essentially the same as an alignment delimiter like `\.\&', yet it is
+recognizably different when we need to distinguish it from a normal delimiter.
+It also turns out to be useful to give a special |cr_code| to `\.{\\cr}',
+and an even larger |cr_cr_code| to `\.{\\crcr}'.
+
+The end of a template is represented by two ``frozen'' control sequences
+called \.{\\endtemplate}. The first has the command code |end_template|, which
+is | > outer_call|, so it will not easily disappear in the presence of errors.
+The |get_x_token| routine converts the first into the second, which has |endv|
+as its command code.
+
+@d span_code 256 /*distinct from any character*/
+@d cr_code 257 /*distinct from |span_code| and from any character*/
+@d cr_cr_code (cr_code+1) /*this distinguishes \.{\\crcr} from \.{\\cr}*/
+@d end_template_token cs_token_flag+frozen_end_template
+
+@<Put each of \TeX's primitives into the hash table@>=
+primitive("span", tab_mark, span_code);@/
+@!@:span\_}{\.{\\span} primitive@>
+primitive("cr", car_ret, cr_code);
+@!@:cr\_}{\.{\\cr} primitive@>
+text(frozen_cr)=text(cur_val);eqtb[frozen_cr]=eqtb[cur_val];@/
+primitive("crcr", car_ret, cr_cr_code);
+@!@:cr\_cr\_}{\.{\\crcr} primitive@>
+text(frozen_end_template)=text(frozen_endv)=s_no("endtemplate");
+@.endtemplate@>
+eq_type(frozen_endv)=endv;equiv(frozen_endv)=null_list;
+eq_level(frozen_endv)=level_one;@/
+eqtb[frozen_end_template]=eqtb[frozen_endv];
+eq_type(frozen_end_template)=end_template;
+
+@ @<Cases of |print_cmd_chr|...@>=
+case tab_mark: if (chr_code==span_code) print_esc("span");
+ else chr_cmd("alignment tab character ")@;@+break;
+case car_ret: if (chr_code==cr_code) print_esc("cr");
+ else print_esc("crcr");@+break;
+
+@ The preamble is copied directly, except that \.{\\tabskip} causes a change
+to the tabskip glue, thereby possibly expanding macros that immediately
+follow it. An appearance of \.{\\span} also causes such an expansion.
+
+Note that if the preamble contains `\.{\\global\\tabskip}', the `\.{\\global}'
+token survives in the preamble and the `\.{\\tabskip}' defines new
+tabskip glue (locally).
+
+@<Declare the procedure called |get_preamble_token|@>=
+static void get_preamble_token(void)
+{@+
+restart: get_token();
+while ((cur_chr==span_code)&&(cur_cmd==tab_mark))
+ {@+get_token(); /*this token will be expanded once*/
+ if (cur_cmd > max_command)
+ {@+expand();get_token();
+ }
+ }
+if (cur_cmd==endv)
+ fatal_error("(interwoven alignment preambles are not allowed)");
+@.interwoven alignment preambles...@>
+if ((cur_cmd==assign_glue)&&(cur_chr==glue_base+tab_skip_code))
+ {@+scan_optional_equals();scan_glue(glue_val);
+ if (global_defs > 0) geq_define(glue_base+tab_skip_code, glue_ref, cur_val);
+ else eq_define(glue_base+tab_skip_code, glue_ref, cur_val);
+ goto restart;
+ }
+}
+
+@ Spaces are eliminated from the beginning of a template.
+
+@<Scan the template \<u_j>...@>=
+p=hold_head;link(p)=null;
+loop@+{@+get_preamble_token();
+ if (cur_cmd==mac_param) goto done1;
+ if ((cur_cmd <= car_ret)&&(cur_cmd >= tab_mark)&&(align_state==-1000000))
+ if ((p==hold_head)&&(cur_loop==null)&&(cur_cmd==tab_mark)
+ ) cur_loop=cur_align;
+ else{@+print_err("Missing # inserted in alignment preamble");
+@.Missing \# inserted...@>
+ help3("There should be exactly one # between &'s, when an",@/
+ "\\halign or \\valign is being set up. In this case you had",@/
+ "none, so I've put one in; maybe that will work.");
+ back_error();goto done1;
+ }
+ else if ((cur_cmd!=spacer)||(p!=hold_head))
+ {@+link(p)=get_avail();p=link(p);info(p)=cur_tok;
+ }
+ }
+done1:
+
+@ @<Scan the template \<v_j>...@>=
+p=hold_head;link(p)=null;
+loop@+{@+resume: get_preamble_token();
+ if ((cur_cmd <= car_ret)&&(cur_cmd >= tab_mark)&&(align_state==-1000000))
+ goto done2;
+ if (cur_cmd==mac_param)
+ {@+print_err("Only one # is allowed per tab");
+@.Only one \# is allowed...@>
+ help3("There should be exactly one # between &'s, when an",@/
+ "\\halign or \\valign is being set up. In this case you had",@/
+ "more than one, so I'm ignoring all but the first.");
+ error();goto resume;
+ }
+ link(p)=get_avail();p=link(p);info(p)=cur_tok;
+ }
+done2: link(p)=get_avail();p=link(p);
+info(p)=end_template_token /*put \.{\\endtemplate} at the end*/
+
+@ The tricky part about alignments is getting the templates into the
+scanner at the right time, and recovering control when a row or column
+is finished.
+
+We usually begin a row after each \.{\\cr} has been sensed, unless that
+\.{\\cr} is followed by \.{\\noalign} or by the right brace that terminates
+the alignment. The |align_peek| routine is used to look ahead and do
+the right thing; it either gets a new row started, or gets a \.{\\noalign}
+started, or finishes off the alignment.
+
+@<Declare the procedure called |align_peek|@>=
+static void align_peek(void)
+{@+
+restart: align_state=1000000;
+@/do@+{get_x_or_protected();
+}@+ while (!(cur_cmd!=spacer));
+if (cur_cmd==no_align)
+ {@+scan_left_brace();new_save_level(no_align_group);
+ if (mode==-vmode) normal_paragraph();
+ }
+else if (cur_cmd==right_brace) fin_align();
+else if ((cur_cmd==car_ret)&&(cur_chr==cr_cr_code))
+ goto restart; /*ignore \.{\\crcr}*/
+else{@+init_row(); /*start a new row*/
+ init_col(); /*start a new column and replace what we peeked at*/
+ }
+}
+
+@ To start a row (i.e., a `row' that rhymes with `dough' but not with `bough'),
+we enter a new semantic level, copy the first tabskip glue, and change
+from internal vertical mode to restricted horizontal mode or vice versa.
+The |space_factor| and |prev_depth| are not used on this semantic level,
+but we clear them to zero just to be tidy.
+
+@p @t\4@>@<Declare the procedure called |init_span|@>@t@>@/
+static void init_row(void)
+{@+push_nest();mode=(-hmode-vmode)-mode;
+if (mode==-hmode) space_factor=0;@+else prev_depth=0;
+tail_append(new_glue(glue_ptr(preamble)));
+subtype(tail)=tab_skip_code+1;@/
+cur_align=link(preamble);cur_tail=cur_head;init_span(cur_align);
+}
+
+@ The parameter to |init_span| is a pointer to the alignrecord where the
+next column or group of columns will begin. A new semantic level is
+entered, so that the columns will generate a list for subsequent packaging.
+
+@<Declare the procedure called |init_span|@>=
+static void init_span(pointer @!p)
+{@+push_nest();
+if (mode==-hmode) space_factor=1000;
+else{@+prev_depth=ignore_depth;normal_paragraph();
+ }
+cur_span=p;
+}
+
+@ When a column begins, we assume that |cur_cmd| is either |omit| or else
+the current token should be put back into the input until the \<u_j>
+template has been scanned. (Note that |cur_cmd| might be |tab_mark| or
+|car_ret|.) We also assume that |align_state| is approximately 1000000 at
+this time. We remain in the same mode, and start the template if it is
+called for.
+
+@p static void init_col(void)
+{@+extra_info(cur_align)=cur_cmd;
+if (cur_cmd==omit) align_state=0;
+else{@+back_input();begin_token_list(u_part(cur_align), u_template);
+ } /*now |align_state==1000000|*/
+}
+
+@ The scanner sets |align_state| to zero when the \<u_j> template ends. When
+a subsequent \.{\\cr} or \.{\\span} or tab mark occurs with |align_state==0|,
+the scanner activates the following code, which fires up the \<v_j> template.
+We need to remember the |cur_chr|, which is either |cr_cr_code|, |cr_code|,
+|span_code|, or a character code, depending on how the column text has ended.
+
+This part of the program had better not be activated when the preamble
+to another alignment is being scanned, or when no alignment preamble is active.
+
+@<Insert the \(v)\<v_j>...@>=
+{@+if ((scanner_status==aligning)||(cur_align==null))
+ fatal_error("(interwoven alignment preambles are not allowed)");
+@.interwoven alignment preambles...@>
+cur_cmd=extra_info(cur_align);extra_info(cur_align)=cur_chr;
+if (cur_cmd==omit) begin_token_list(omit_template, v_template);
+else begin_token_list(v_part(cur_align), v_template);
+align_state=1000000;goto restart;
+}
+
+@ The token list |omit_template| just referred to is a constant token
+list that contains the special control sequence \.{\\endtemplate} only.
+
+@<Initialize the special...@>=
+info(omit_template)=end_template_token; /*|link(omit_template)==null|*/
+
+@ When the |endv| command at the end of a \<v_j> template comes through the
+scanner, things really start to happen; and it is the |fin_col| routine
+that makes them happen. This routine returns |true| if a row as well as a
+column has been finished.
+
+@p static bool fin_col(void)
+{@+
+pointer p; /*the alignrecord after the current one*/
+pointer @!q, @!r; /*temporary pointers for list manipulation*/
+pointer @!s; /*a new span node*/
+pointer @!u; /*a new unset box*/
+scaled @!w; /*natural width*/
+glue_ord @!o; /*order of infinity*/
+halfword @!n; /*span counter*/
+if (cur_align==null) confusion("endv");
+q=link(cur_align);@+if (q==null) confusion("endv");
+@:this can't happen endv}{\quad endv@>
+if (align_state < 500000)
+ fatal_error("(interwoven alignment preambles are not allowed)");
+@.interwoven alignment preambles...@>
+p=link(q);
+@<If the preamble list has been traversed, check that the row has ended@>;
+if (extra_info(cur_align)!=span_code)
+ {@+unsave();new_save_level(align_group);@/
+ @<Package an unset box for the current column and record its width@>;
+ @<Copy the tabskip glue between columns@>;
+ if (extra_info(cur_align) >= cr_code)
+ {@+return true;
+ }
+ init_span(p);
+ }
+align_state=1000000;
+@/do@+{get_x_or_protected();
+}@+ while (!(cur_cmd!=spacer));
+cur_align=p;
+init_col();return false;
+}
+
+@ @<If the preamble list has been traversed, check that the row has ended@>=
+if ((p==null)&&(extra_info(cur_align) < cr_code))
+ if (cur_loop!=null) @<Lengthen the preamble periodically@>@;
+ else{@+print_err("Extra alignment tab has been changed to ");
+@.Extra alignment tab...@>
+ print_esc("cr");
+ help3("You have given more \\span or & marks than there were",@/
+ "in the preamble to the \\halign or \\valign now in progress.",@/
+ "So I'll assume that you meant to type \\cr instead.");
+ extra_info(cur_align)=cr_code;error();
+ }
+
+@ @<Lengthen the preamble...@>=
+{@+link(q)=new_null_box();p=link(q); /*a new alignrecord*/
+info(p)=end_span;width(p)=null_flag;cur_loop=link(cur_loop);
+@<Copy the templates from node |cur_loop| into node |p|@>;
+cur_loop=link(cur_loop);
+link(p)=new_glue(glue_ptr(cur_loop));
+subtype(link(p))=tab_skip_code+1;
+}
+
+@ @<Copy the templates from node |cur_loop| into node |p|@>=
+q=hold_head;r=u_part(cur_loop);
+while (r!=null)
+ {@+link(q)=get_avail();q=link(q);info(q)=info(r);r=link(r);
+ }
+link(q)=null;u_part(p)=link(hold_head);
+q=hold_head;r=v_part(cur_loop);
+while (r!=null)
+ {@+link(q)=get_avail();q=link(q);info(q)=info(r);r=link(r);
+ }
+link(q)=null;v_part(p)=link(hold_head)
+
+@ @<Copy the tabskip glue...@>=
+tail_append(new_glue(glue_ptr(link(cur_align))));
+subtype(tail)=tab_skip_code+1
+
+@ @<Package an unset...@>=
+{@+if (mode==-hmode)
+ {@+adjust_tail=cur_tail;u=hpack(link(head), natural);w=width(u);
+ cur_tail=adjust_tail;adjust_tail=null;
+ }
+else{@+u=vpackage(link(head), natural, 0);w=height(u);
+ }
+n=min_quarterword; /*this represents a span count of 1*/
+if (cur_span!=cur_align) @<Update width entry for spanned columns@>@;
+else if (w > width(cur_align)) width(cur_align)=w;
+type(u)=unset_node;span_count(u)=n;@/
+@<Determine the stretch order@>;
+glue_order(u)=o;glue_stretch(u)=total_stretch[o];@/
+@<Determine the shrink order@>;
+glue_sign(u)=o;glue_shrink(u)=total_shrink[o];@/
+pop_nest();link(tail)=u;tail=u;
+}
+
+@ A span node is a 2-word record containing |width|, |info|, and |link|
+fields. The |link| field is not really a link, it indicates the number of
+spanned columns; the |info| field points to a span node for the same
+starting column, having a greater extent of spanning, or to |end_span|,
+which has the largest possible |link| field; the |width| field holds the
+largest natural width corresponding to a particular set of spanned columns.
+
+A list of the maximum widths so far, for spanned columns starting at a
+given column, begins with the |info| field of the alignrecord for that
+column.
+
+@d span_node_size 2 /*number of |mem| words for a span node*/
+
+@<Initialize the special list heads...@>=
+link(end_span)=max_quarterword+1;info(end_span)=null;
+
+@ @<Update width entry for spanned columns@>=
+{@+q=cur_span;
+@/do@+{incr(n);q=link(link(q));
+}@+ while (!(q==cur_align));
+if (n > max_quarterword) confusion("256 spans"); /*this can happen, but won't*/
+@^system dependencies@>
+@:this can't happen 256 spans}{\quad 256 spans@>
+q=cur_span;while (link(info(q)) < n) q=info(q);
+if (link(info(q)) > n)
+ {@+s=get_node(span_node_size);info(s)=info(q);link(s)=n;
+ info(q)=s;width(s)=w;
+ }
+else if (width(info(q)) < w) width(info(q))=w;
+}
+
+@ At the end of a row, we append an unset box to the current vlist (for
+\.{\\halign}) or the current hlist (for \.{\\valign}). This unset box
+contains the unset boxes for the columns, separated by the tabskip glue.
+Everything will be set later.
+
+@p static void fin_row(void)
+{@+pointer p; /*the new unset box*/
+if (mode==-hmode)
+ {@+p=hpack(link(head), natural);
+ pop_nest();append_to_vlist(p);
+ if (cur_head!=cur_tail)
+ {@+link(tail)=link(cur_head);tail=cur_tail;
+ }
+ }
+else{@+p=vpack(link(head), natural);pop_nest();
+ link(tail)=p;tail=p;space_factor=1000;
+ }
+type(p)=unset_node;glue_stretch(p)=0;
+if (every_cr!=null) begin_token_list(every_cr, every_cr_text);
+align_peek();
+} /*note that |glue_shrink(p)==0| since |glue_shrink====shift_amount|*/
+
+@ Finally, we will reach the end of the alignment, and we can breathe a
+sigh of relief that memory hasn't overflowed. All the unset boxes will now be
+set so that the columns line up, taking due account of spanned columns.
+
+@p static void do_assignments(void);@/
+static void resume_after_display(void);@/
+static void build_page(void);@/
+static void fin_align(void)
+{@+pointer @!p, @!q, @!r, @!s, @!u, @!v; /*registers for the list operations*/
+scaled @!t, @!w; /*width of column*/
+scaled @!o; /*shift offset for unset boxes*/
+halfword @!n; /*matching span amount*/
+scaled @!rule_save; /*temporary storage for |overfull_rule|*/
+memory_word @!aux_save; /*temporary storage for |aux|*/
+if (cur_group!=align_group) confusion("align1");
+@:this can't happen align}{\quad align@>
+unsave(); /*that |align_group| was for individual entries*/
+if (cur_group!=align_group) confusion("align0");
+unsave(); /*that |align_group| was for the whole alignment*/
+if (nest[nest_ptr-1].mode_field==mmode) o=display_indent;
+ else o=0;
+@<Go through the preamble list, determining the column widths and changing
+the alignrecords to dummy unset boxes@>;
+@<Package the preamble list, to determine the actual tabskip glue amounts,
+and let |p| point to this prototype box@>;
+@<Set the glue in all the unset boxes of the current list@>;
+flush_node_list(p);pop_alignment();
+@<Insert the \(c)current list into its environment@>;
+} @/
+@t\4@>@<Declare the procedure called |align_peek|@>@;
+
+@ It's time now to dismantle the preamble list and to compute the column
+widths. Let $w_{ij}$ be the maximum of the natural widths of all entries
+that span columns $i$ through $j$, inclusive. The alignrecord for column~$i$
+contains $w_{ii}$ in its |width| field, and there is also a linked list of
+the nonzero $w_{ij}$ for increasing $j$, accessible via the |info| field;
+these span nodes contain the value $j-i+|min_quarterword|$ in their
+|link| fields. The values of $w_{ii}$ were initialized to |null_flag|, which
+we regard as $-\infty$.
+
+The final column widths are defined by the formula
+$$w_j=\max_{1\le i\le j}\biggl( w_{ij}-\sum_{i\le k<j}(t_k+w_k)\biggr),$$
+where $t_k$ is the natural width of the tabskip glue between columns
+$k$ and~$k+1$. However, if $w_{ij}=-\infty$ for all |i| in the range
+|1 <= i <= j| (i.e., if every entry that involved column~|j| also involved
+column~|j+1|), we let $w_j=0$, and we zero out the tabskip glue after
+column~|j|.
+
+\TeX\ computes these values by using the following scheme: First $w_1=w_{11}$.
+Then replace $w_{2j}$ by $\max(w_{2j},w_{1j}-t_1-w_1)$, for all $j>1$.
+Then $w_2=w_{22}$. Then replace $w_{3j}$ by $\max(w_{3j},w_{2j}-t_2-w_2)$
+for all $j>2$; and so on. If any $w_j$ turns out to be $-\infty$, its
+value is changed to zero and so is the next tabskip.
+
+@<Go through the preamble list,...@>=
+q=link(preamble);
+@/do@+{flush_list(u_part(q));flush_list(v_part(q));
+p=link(link(q));
+if (width(q)==null_flag)
+ @<Nullify |width(q)| and the tabskip glue following this column@>;
+if (info(q)!=end_span)
+ @<Merge the widths in the span nodes of |q| with those of |p|, destroying
+the span nodes of |q|@>;
+type(q)=unset_node;span_count(q)=min_quarterword;height(q)=0;
+depth(q)=0;glue_order(q)=normal;glue_sign(q)=normal;
+glue_stretch(q)=0;glue_shrink(q)=0;q=p;
+}@+ while (!(q==null))
+
+@ @<Nullify |width(q)| and the tabskip glue following this column@>=
+{@+width(q)=0;r=link(q);s=glue_ptr(r);
+if (s!=zero_glue)
+ {@+add_glue_ref(zero_glue);delete_glue_ref(s);
+ glue_ptr(r)=zero_glue;
+ }
+}
+
+@ Merging of two span-node lists is a typical exercise in the manipulation of
+linearly linked data structures. The essential invariant in the following
+|@/do@+{| loop is that we want to dispense with node |r|, in |q|'s list,
+and |u| is its successor; all nodes of |p|'s list up to and including |s|
+have been processed, and the successor of |s| matches |r| or precedes |r|
+or follows |r|, according as |link(r)==n| or |link(r) > n| or |link(r) < n|.
+
+@<Merge the widths...@>=
+{@+t=width(q)+width(glue_ptr(link(q)));
+r=info(q);s=end_span;info(s)=p;n=min_quarterword+1;
+@/do@+{width(r)=width(r)-t;u=info(r);
+while (link(r) > n)
+ {@+s=info(s);n=link(info(s))+1;
+ }
+if (link(r) < n)
+ {@+info(r)=info(s);info(s)=r;decr(link(r));s=r;
+ }
+else{@+if (width(r) > width(info(s))) width(info(s))=width(r);
+ free_node(r, span_node_size);
+ }
+r=u;
+}@+ while (!(r==end_span));
+}
+
+@ Now the preamble list has been converted to a list of alternating unset
+boxes and tabskip glue, where the box widths are equal to the final
+column sizes. In case of \.{\\valign}, we change the widths to heights,
+so that a correct error message will be produced if the alignment is
+overfull or underfull.
+
+@<Package the preamble list...@>=
+save_ptr=save_ptr-2;pack_begin_line=-mode_line;
+if (mode==-vmode)
+ {@+rule_save=overfull_rule;
+ overfull_rule=0; /*prevent rule from being packaged*/
+ p=hpack(preamble, saved(1), saved(0));overfull_rule=rule_save;
+ }
+else{@+q=link(preamble);
+ @/do@+{height(q)=width(q);width(q)=0;q=link(link(q));
+ }@+ while (!(q==null));
+ p=vpack(preamble, saved(1), saved(0));
+ q=link(preamble);
+ @/do@+{width(q)=height(q);height(q)=0;q=link(link(q));
+ }@+ while (!(q==null));
+ }
+pack_begin_line=0
+
+@ @<Set the glue in all the unset...@>=
+q=link(head);s=head;
+while (q!=null)
+ {@+if (!is_char_node(q))
+ if (type(q)==unset_node)
+ @<Set the unset box |q| and the unset boxes in it@>@;
+ else if (type(q)==rule_node)
+ @<Make the running dimensions in rule |q| extend to the boundaries of
+the alignment@>;
+ s=q;q=link(q);
+ }
+
+@ @<Make the running dimensions in rule |q| extend...@>=
+{@+if (is_running(width(q))) width(q)=width(p);
+if (is_running(height(q))) height(q)=height(p);
+if (is_running(depth(q))) depth(q)=depth(p);
+if (o!=0)
+ {@+r=link(q);link(q)=null;q=hpack(q, natural);
+ shift_amount(q)=o;link(q)=r;link(s)=q;
+ }
+}
+
+@ The unset box |q| represents a row that contains one or more unset boxes,
+depending on how soon \.{\\cr} occurred in that row.
+
+@<Set the unset box |q| and the unset boxes in it@>=
+{@+if (mode==-vmode)
+ {@+type(q)=hlist_node;width(q)=width(p);
+ }
+else{@+type(q)=vlist_node;height(q)=height(p);
+ }
+glue_order(q)=glue_order(p);glue_sign(q)=glue_sign(p);
+glue_set(q)=glue_set(p);shift_amount(q)=o;
+r=link(list_ptr(q));s=link(list_ptr(p));
+@/do@+{@<Set the glue in node |r| and change it from an unset node@>;
+r=link(link(r));s=link(link(s));
+}@+ while (!(r==null));
+}
+
+@ A box made from spanned columns will be followed by tabskip glue nodes and
+by empty boxes as if there were no spanning. This permits perfect alignment
+of subsequent entries, and it prevents values that depend on floating point
+arithmetic from entering into the dimensions of any boxes.
+
+@<Set the glue in node |r|...@>=
+n=span_count(r);t=width(s);w=t;u=hold_head;
+while (n > min_quarterword)
+ {@+decr(n);
+ @<Append tabskip glue and an empty box to list |u|, and update |s| and |t|
+as the prototype nodes are passed@>;
+ }
+if (mode==-vmode)
+ @<Make the unset node |r| into an |hlist_node| of width |w|, setting the
+glue as if the width were |t|@>@;
+else@<Make the unset node |r| into a |vlist_node| of height |w|, setting the
+glue as if the height were |t|@>;
+shift_amount(r)=0;
+if (u!=hold_head) /*append blank boxes to account for spanned nodes*/
+ {@+link(u)=link(r);link(r)=link(hold_head);r=u;
+ }
+
+@ @<Append tabskip glue and an empty box to list |u|...@>=
+s=link(s);v=glue_ptr(s);link(u)=new_glue(v);u=link(u);
+subtype(u)=tab_skip_code+1;t=t+width(v);
+if (glue_sign(p)==stretching)
+ {@+if (stretch_order(v)==glue_order(p))
+ t=t+round(unfix(glue_set(p))*stretch(v));
+@^real multiplication@>
+ }
+else if (glue_sign(p)==shrinking)
+ {@+if (shrink_order(v)==glue_order(p))
+ t=t-round(unfix(glue_set(p))*shrink(v));
+ }
+s=link(s);link(u)=new_null_box();u=link(u);t=t+width(s);
+if (mode==-vmode) width(u)=width(s);@+else
+ {@+type(u)=vlist_node;height(u)=width(s);
+ }
+
+@ @<Make the unset node |r| into an |hlist_node| of width |w|...@>=
+{@+height(r)=height(q);depth(r)=depth(q);
+if (t==width(r))
+ {@+glue_sign(r)=normal;glue_order(r)=normal;
+ set_glue_ratio_zero(glue_set(r));
+ }
+else if (t > width(r))
+ {@+glue_sign(r)=stretching;
+ if (glue_stretch(r)==0) set_glue_ratio_zero(glue_set(r));
+ else glue_set(r)=fix((t-width(r))/(double)glue_stretch(r));
+@^real division@>
+ }
+else{@+glue_order(r)=glue_sign(r);glue_sign(r)=shrinking;
+ if (glue_shrink(r)==0) set_glue_ratio_zero(glue_set(r));
+ else if ((glue_order(r)==normal)&&(width(r)-t > glue_shrink(r)))
+ set_glue_ratio_one(glue_set(r));
+ else glue_set(r)=fix((width(r)-t)/(double)glue_shrink(r));
+ }
+width(r)=w;type(r)=hlist_node;
+}
+
+@ @<Make the unset node |r| into a |vlist_node| of height |w|...@>=
+{@+width(r)=width(q);
+if (t==height(r))
+ {@+glue_sign(r)=normal;glue_order(r)=normal;
+ set_glue_ratio_zero(glue_set(r));
+ }
+else if (t > height(r))
+ {@+glue_sign(r)=stretching;
+ if (glue_stretch(r)==0) set_glue_ratio_zero(glue_set(r));
+ else glue_set(r)=fix((t-height(r))/(double)glue_stretch(r));
+@^real division@>
+ }
+else{@+glue_order(r)=glue_sign(r);glue_sign(r)=shrinking;
+ if (glue_shrink(r)==0) set_glue_ratio_zero(glue_set(r));
+ else if ((glue_order(r)==normal)&&(height(r)-t > glue_shrink(r)))
+ set_glue_ratio_one(glue_set(r));
+ else glue_set(r)=fix((height(r)-t)/(double)glue_shrink(r));
+ }
+height(r)=w;type(r)=vlist_node;
+}
+
+@ We now have a completed alignment, in the list that starts at |head|
+and ends at |tail|. This list will be merged with the one that encloses
+it. (In case the enclosing mode is |mmode|, for displayed formulas,
+we will need to insert glue before and after the display; that part of the
+program will be deferred until we're more familiar with such operations.)
+
+In restricted horizontal mode, the |clang| part of |aux| is undefined;
+an over-cautious \PASCAL\ runtime system may complain about this.
+@^dirty \PASCAL@>
+
+@<Insert the \(c)current list into its environment@>=
+aux_save=aux;p=link(head);q=tail;pop_nest();
+if (mode==mmode) @<Finish an alignment in a display@>@;
+else{@+aux=aux_save;link(tail)=p;
+ if (p!=null) tail=q;
+ if (mode==vmode) build_page();
+ }
+
+@* Breaking paragraphs into lines.
+We come now to what is probably the most interesting algorithm of \TeX:
+the mechanism for choosing the ``best possible'' breakpoints that yield
+the individual lines of a paragraph. \TeX's line-breaking algorithm takes
+a given horizontal list and converts it to a sequence of boxes that are
+appended to the current vertical list. In the course of doing this, it
+creates a special data structure containing three kinds of records that are
+not used elsewhere in \TeX. Such nodes are created while a paragraph is
+being processed, and they are destroyed afterwards; thus, the other parts
+of \TeX\ do not need to know anything about how line-breaking is done.
+
+The method used here is based on an approach devised by Michael F. Plass and
+@^Plass, Michael Frederick@>
+@^Knuth, Donald Ervin@>
+the author in 1977, subsequently generalized and improved by the same two
+people in 1980. A detailed discussion appears in {\sl Software---Practice
+and Experience \bf11} (1981), 1119--1184, where it is shown that the
+line-breaking problem can be regarded as a special case of the problem of
+computing the shortest path in an acyclic network. The cited paper includes
+numerous examples and describes the history of line breaking as it has been
+practiced by printers through the ages. The present implementation adds two
+new ideas to the algorithm of 1980: Memory space requirements are considerably
+reduced by using smaller records for inactive nodes than for active ones,
+and arithmetic overflow is avoided by using ``delta distances'' instead of
+keeping track of the total distance from the beginning of the paragraph to the
+current point.
+
+@ The |line_break| procedure should be invoked only in horizontal mode; it
+leaves that mode and places its output into the current vlist of the
+enclosing vertical mode (or internal vertical mode).
+There is one explicit parameter: |final_widow_penalty| is the amount of
+additional penalty to be inserted before the final line of the paragraph.
+
+There are also a number of implicit parameters: The hlist to be broken
+starts at |link(head)|, and it is nonempty. The value of |prev_graf| in the
+enclosing semantic level tells where the paragraph should begin in the
+sequence of line numbers, in case hanging indentation or \.{\\parshape}
+is in use; |prev_graf| is zero unless this paragraph is being continued
+after a displayed formula. Other implicit parameters, such as the
+|par_shape_ptr| and various penalties to use for hyphenation, etc., appear
+in |eqtb|.
+
+After |line_break| has acted, it will have updated the current vlist and the
+value of |prev_graf|. Furthermore, the global variable |just_box| will
+point to the final box created by |line_break|, so that the width of this
+line can be ascertained when it is necessary to decide whether to use
+|above_display_skip| or |above_display_short_skip| before a displayed formula.
+
+@<Glob...@>=
+static pointer @!just_box; /*the |hlist_node| for the last line of the new paragraph*/
+
+@ Since |line_break| is a rather lengthy procedure---sort of a small world unto
+itself---we must build it up little by little, somewhat more cautiously
+than we have done with the simpler procedures of \TeX. Here is the
+general outline.
+
+@p @t\4@>@<Declare subprocedures for |line_break|@>@;
+static void line_break(int @!final_widow_penalty)
+{@+
+@<Local variables for line breaking@>@;
+@<Local variables to save the profiling context@>@;
+@<Charge the time used here on |line_break|@>@;
+pack_begin_line=mode_line; /*this is for over/underfull box messages*/
+@<Get ready to start line breaking@>;
+@<Find optimal breakpoints@>;
+@<Break the paragraph at the chosen breakpoints, justify the resulting lines
+to the correct widths, and append them to the current vertical list@>;
+@<Clean up the memory by removing the break nodes@>;
+pack_begin_line=0;
+@<restore the previous current file, line, and command@>@;
+}
+@#
+@t\4@>@<Declare \eTeX\ procedures for use by |main_control|@>@;
+
+@ The first task is to move the list from |head| to |temp_head| and go
+into the enclosing semantic level. We also append the \.{\\parfillskip}
+glue to the end of the paragraph, removing a space (or other glue node) if
+it was there, since spaces usually precede blank lines and instances of
+`\.{\$\$}'. The |par_fill_skip| is preceded by an infinite penalty, so
+it will never be considered as a potential breakpoint.
+
+This code assumes that a |glue_node| and a |penalty_node| occupy the
+same number of |mem|~words.
+@^data structure assumptions@>
+
+@<Get ready to start...@>=
+link(temp_head)=link(head);
+if (is_char_node(tail)) tail_append(new_penalty(inf_penalty))@;
+else if (type(tail)!=glue_node) tail_append(new_penalty(inf_penalty))@;
+else{@+type(tail)=penalty_node;delete_glue_ref(glue_ptr(tail));
+ flush_node_list(leader_ptr(tail));penalty(tail)=inf_penalty;
+ }
+link(tail)=new_param_glue(par_fill_skip_code);
+init_cur_lang=prev_graf%0200000;
+init_l_hyf=prev_graf/020000000;
+init_r_hyf=(prev_graf/0200000)%0100;
+pop_nest();
+
+@ When looking for optimal line breaks, \TeX\ creates a ``break node'' for
+each break that is {\sl feasible}, in the sense that there is a way to end
+a line at the given place without requiring any line to stretch more than
+a given tolerance. A break node is characterized by three things: the position
+of the break (which is a pointer to a |glue_node|, |math_node|, |penalty_node|,
+or |disc_node|); the ordinal number of the line that will follow this
+breakpoint; and the fitness classification of the line that has just
+ended, i.e., |tight_fit|, |decent_fit|, |loose_fit|, or |very_loose_fit|.
+
+@d tight_fit 3 /*fitness classification for lines shrinking 0.5 to 1.0 of their
+ shrinkability*/
+@d loose_fit 1 /*fitness classification for lines stretching 0.5 to 1.0 of their
+ stretchability*/
+@d very_loose_fit 0 /*fitness classification for lines stretching more than
+ their stretchability*/
+@d decent_fit 2 /*fitness classification for all other lines*/
+
+@ The algorithm essentially determines the best possible way to achieve
+each feasible combination of position, line, and fitness. Thus, it answers
+questions like, ``What is the best way to break the opening part of the
+paragraph so that the fourth line is a tight line ending at such-and-such
+a place?'' However, the fact that all lines are to be the same length
+after a certain point makes it possible to regard all sufficiently large
+line numbers as equivalent, when the looseness parameter is zero, and this
+makes it possible for the algorithm to save space and time.
+
+An ``active node'' and a ``passive node'' are created in |mem| for each
+feasible breakpoint that needs to be considered. Active nodes are three
+words long and passive nodes are two words long. We need active nodes only
+for breakpoints near the place in the paragraph that is currently being
+examined, so they are recycled within a comparatively short time after
+they are created.
+
+@ An active node for a given breakpoint contains six fields:
+
+\yskip\hang|link| points to the next node in the list of active nodes; the
+last active node has |link==last_active|.
+
+\yskip\hang|break_node| points to the passive node associated with this
+breakpoint.
+
+\yskip\hang|line_number| is the number of the line that follows this
+breakpoint.
+
+\yskip\hang|fitness| is the fitness classification of the line ending at this
+breakpoint.
+
+\yskip\hang|type| is either |hyphenated| or |unhyphenated|, depending on
+whether this breakpoint is a |disc_node|.
+
+\yskip\hang|total_demerits| is the minimum possible sum of demerits over all
+lines leading from the beginning of the paragraph to this breakpoint.
+
+\yskip\noindent
+The value of |link(active)| points to the first active node on a linked list
+of all currently active nodes. This list is in order by |line_number|,
+except that nodes with |line_number > easy_line| may be in any order relative
+to each other.
+
+@d active_node_size 3 /*number of words in active nodes*/
+@d fitness(A) subtype(A) /*|very_loose_fit dotdot tight_fit| on final line for this break*/
+@d break_node(A) rlink(A) /*pointer to the corresponding passive node*/
+@d line_number(A) llink(A) /*line that begins at this breakpoint*/
+@d total_demerits(A) mem[A+2].i /*the quantity that \TeX\ minimizes*/
+@d unhyphenated 0 /*the |type| of a normal active break node*/
+@d hyphenated 1 /*the |type| of an active node that breaks at a |disc_node|*/
+@d last_active active /*the active list ends where it begins*/
+
+@ @<Initialize the special list heads...@>=
+type(last_active)=hyphenated;line_number(last_active)=max_halfword;
+subtype(last_active)=0; /*the |subtype| is never examined by the algorithm*/
+
+@ The passive node for a given breakpoint contains only four fields:
+
+\yskip\hang|link| points to the passive node created just before this one,
+if any, otherwise it is |null|.
+
+\yskip\hang|cur_break| points to the position of this breakpoint in the
+horizontal list for the paragraph being broken.
+
+\yskip\hang|prev_break| points to the passive node that should precede this
+one in an optimal path to this breakpoint.
+
+\yskip\hang|serial| is equal to |n| if this passive node is the |n|th
+one created during the current pass. (This field is used only when
+printing out detailed statistics about the line-breaking calculations.)
+
+\yskip\noindent
+There is a global variable called |passive| that points to the most
+recently created passive node. Another global variable, |printed_node|,
+is used to help print out the paragraph when detailed information about
+the line-breaking computation is being displayed.
+
+@d passive_node_size 2 /*number of words in passive nodes*/
+@d cur_break(A) rlink(A) /*in passive node, points to position of this breakpoint*/
+@d prev_break(A) llink(A) /*points to passive node that should precede this one*/
+@d serial(A) info(A) /*serial number for symbolic identification*/
+
+@<Glob...@>=
+static pointer @!passive; /*most recent node on passive list*/
+static pointer @!printed_node; /*most recent node that has been printed*/
+static halfword @!pass_number; /*the number of passive nodes allocated on this pass*/
+
+@ The active list also contains ``delta'' nodes that help the algorithm
+compute the badness of individual lines. Such nodes appear only between two
+active nodes, and they have |type==delta_node|. If |p| and |r| are active nodes
+and if |q| is a delta node between them, so that |link(p)==q| and |link(q)==r|,
+then |q| tells the space difference between lines in the horizontal list that
+start after breakpoint |p| and lines that start after breakpoint |r|. In
+other words, if we know the length of the line that starts after |p| and
+ends at our current position, then the corresponding length of the line that
+starts after |r| is obtained by adding the amounts in node~|q|. A delta node
+contains six scaled numbers, since it must record the net change in glue
+stretchability with respect to all orders of infinity. The natural width
+difference appears in |mem[q+1].sc|; the stretch differences in units of
+pt, fil, fill, and filll appear in |mem[q+2 dotdot q+5].sc|; and the shrink difference
+appears in |mem[q+6].sc|. The |subtype| field of a delta node is not used.
+
+@d delta_node_size 7 /*number of words in a delta node*/
+@d delta_node 2 /*|type| field in a delta node*/
+
+@ As the algorithm runs, it maintains a set of six delta-like registers
+for the length of the line following the first active breakpoint to the
+current position in the given hlist. When it makes a pass through the
+active list, it also maintains a similar set of six registers for the
+length following the active breakpoint of current interest. A third set
+holds the length of an empty line (namely, the sum of \.{\\leftskip} and
+\.{\\rightskip}); and a fourth set is used to create new delta nodes.
+
+When we pass a delta node we want to do operations like
+$$\hbox{\ignorespaces|for
+k=1 to 6 do cur_active_width[k]=cur_active_width[k]+mem[q+k].sc|};$$ and we
+want to do this without the overhead of |for| loops. The |do_all_six|
+macro makes such six-tuples convenient.
+
+@d do_all_six(A) A(1);A(2);A(3);A(4);A(5);A(6)
+
+@<Glob...@>=
+static scaled @!active_width0[6], *const @!active_width = @!active_width0-1;
+ /*distance from first active node to~|cur_p|*/
+static scaled @!cur_active_width0[6],
+ *const @!cur_active_width = @!cur_active_width0-1; /*distance from current active node*/
+static scaled @!background0[6], *const @!background = @!background0-1; /*length of an ``empty'' line*/
+static scaled @!break_width0[6], *const @!break_width = @!break_width0-1; /*length being computed after current break*/
+
+@ Let's state the principles of the delta nodes more precisely and concisely,
+so that the following programs will be less obscure. For each legal
+breakpoint~|p| in the paragraph, we define two quantities $\alpha(p)$ and
+$\beta(p)$ such that the length of material in a line from breakpoint~|p|
+to breakpoint~|q| is $\gamma+\beta(q)-\alpha(p)$, for some fixed $\gamma$.
+Intuitively, $\alpha(p)$ and $\beta(q)$ are the total length of material from
+the beginning of the paragraph to a point ``after'' a break at |p| and to a
+point ``before'' a break at |q|; and $\gamma$ is the width of an empty line,
+namely the length contributed by \.{\\leftskip} and \.{\\rightskip}.
+
+Suppose, for example, that the paragraph consists entirely of alternating
+boxes and glue skips; let the boxes have widths $x_1\ldots x_n$ and
+let the skips have widths $y_1\ldots y_n$, so that the paragraph can be
+represented by $x_1y_1\ldots x_ny_n$. Let $p_i$ be the legal breakpoint
+at $y_i$; then $\alpha(p_i)=x_1+y_1+\cdots+x_i+y_i$, and $\beta(p_i)=
+x_1+y_1+\cdots+x_i$. To check this, note that the length of material from
+$p_2$ to $p_5$, say, is $\gamma+x_3+y_3+x_4+y_4+x_5=\gamma+\beta(p_5)
+-\alpha(p_2)$.
+
+The quantities $\alpha$, $\beta$, $\gamma$ involve glue stretchability and
+shrinkability as well as a natural width. If we were to compute $\alpha(p)$
+and $\beta(p)$ for each |p|, we would need multiple precision arithmetic, and
+the multiprecise numbers would have to be kept in the active nodes.
+\TeX\ avoids this problem by working entirely with relative differences
+or ``deltas.'' Suppose, for example, that the active list contains
+$a_1\,\delta_1\,a_2\,\delta_2\,a_3$, where the |a|'s are active breakpoints
+and the $\delta$'s are delta nodes. Then $\delta_1=\alpha(a_1)-\alpha(a_2)$
+and $\delta_2=\alpha(a_2)-\alpha(a_3)$. If the line breaking algorithm is
+currently positioned at some other breakpoint |p|, the |active_width| array
+contains the value $\gamma+\beta(p)-\alpha(a_1)$. If we are scanning through
+the list of active nodes and considering a tentative line that runs from
+$a_2$ to~|p|, say, the |cur_active_width| array will contain the value
+$\gamma+\beta(p)-\alpha(a_2)$. Thus, when we move from $a_2$ to $a_3$,
+we want to add $\alpha(a_2)-\alpha(a_3)$ to |cur_active_width|; and this
+is just $\delta_2$, which appears in the active list between $a_2$ and
+$a_3$. The |background| array contains $\gamma$. The |break_width| array
+will be used to calculate values of new delta nodes when the active
+list is being updated.
+
+@ Glue nodes in a horizontal list that is being paragraphed are not supposed to
+include ``infinite'' shrinkability; that is why the algorithm maintains
+four registers for stretching but only one for shrinking. If the user tries to
+introduce infinite shrinkability, the shrinkability will be reset to finite
+and an error message will be issued. A boolean variable |no_shrink_error_yet|
+prevents this error message from appearing more than once per paragraph.
+
+@d check_shrinkage(A) if ((shrink_order(A)!=normal)&&(shrink(A)!=0))
+ {@+A=finite_shrink(A);
+ }
+
+@<Glob...@>=
+static bool @!no_shrink_error_yet; /*have we complained about infinite shrinkage?*/
+
+@ @<Declare subprocedures for |line_break|@>=
+static pointer finite_shrink(pointer @!p) /*recovers from infinite shrinkage*/
+{@+pointer q; /*new glue specification*/
+if (no_shrink_error_yet)
+ {@+no_shrink_error_yet=false;
+#ifdef @!STAT
+ if (tracing_paragraphs > 0) end_diagnostic(true);
+#endif
+@;
+ print_err("Infinite glue shrinkage found in a paragraph");
+@.Infinite glue shrinkage...@>
+ help5("The paragraph just ended includes some glue that has",@/
+ "infinite shrinkability, e.g., `\\hskip 0pt minus 1fil'.",@/
+ "Such glue doesn't belong there---it allows a paragraph",@/
+ "of any length to fit on one line. But it's safe to proceed,",@/
+ "since the offensive shrinkability has been made finite.");
+ error();
+#ifdef @!STAT
+ if (tracing_paragraphs > 0) begin_diagnostic();
+#endif
+@;
+ }
+q=new_spec(p);shrink_order(q)=normal;
+delete_glue_ref(p);return q;
+}
+
+@ @<Get ready to start...@>=
+no_shrink_error_yet=true;@/
+check_shrinkage(left_skip);check_shrinkage(right_skip);@/
+q=left_skip;r=right_skip;background[1]=width(q)+width(r);@/
+background[2]=0;background[3]=0;background[4]=0;background[5]=0;@/
+background[2+stretch_order(q)]=stretch(q);@/
+background[2+stretch_order(r)]=@|background[2+stretch_order(r)]+stretch(r);@/
+background[6]=shrink(q)+shrink(r);
+
+@ A pointer variable |cur_p| runs through the given horizontal list as we look
+for breakpoints. This variable is global, since it is used both by |line_break|
+and by its subprocedure |try_break|.
+
+Another global variable called |threshold| is used to determine the feasibility
+of individual lines: Breakpoints are feasible if there is a way to reach
+them without creating lines whose badness exceeds |threshold|. (The
+badness is compared to |threshold| before penalties are added, so that
+penalty values do not affect the feasibility of breakpoints, except that
+no break is allowed when the penalty is 10000 or more.) If |threshold|
+is 10000 or more, all legal breaks are considered feasible, since the
+|badness| function specified above never returns a value greater than~10000.
+
+Up to three passes might be made through the paragraph in an attempt to find at
+least one set of feasible breakpoints. On the first pass, we have
+|threshold==pretolerance| and |second_pass==final_pass==false|.
+If this pass fails to find a
+feasible solution, |threshold| is set to |tolerance|, |second_pass| is set
+|true|, and an attempt is made to hyphenate as many words as possible.
+If that fails too, we add |emergency_stretch| to the background
+stretchability and set |final_pass==true|.
+
+@<Glob...@>=
+static pointer @!cur_p; /*the current breakpoint under consideration*/
+static bool @!second_pass; /*is this our second attempt to break this paragraph?*/
+static bool @!final_pass; /*is this our final attempt to break this paragraph?*/
+static int @!threshold; /*maximum badness on feasible lines*/
+
+@ The heart of the line-breaking procedure is `|try_break|', a subroutine
+that tests if the current breakpoint |cur_p| is feasible, by running
+through the active list to see what lines of text can be made from active
+nodes to~|cur_p|. If feasible breaks are possible, new break nodes are
+created. If |cur_p| is too far from an active node, that node is
+deactivated.
+
+The parameter |pi| to |try_break| is the penalty associated
+with a break at |cur_p|; we have |pi==eject_penalty| if the break is forced,
+and |pi==inf_penalty| if the break is illegal.
+
+The other parameter, |break_type|, is set to |hyphenated| or |unhyphenated|,
+depending on whether or not the current break is at a |disc_node|. The
+end of a paragraph is also regarded as `|hyphenated|'; this case is
+distinguishable by the condition |cur_p==null|.
+
+@d copy_to_cur_active(A) cur_active_width[A]=active_width[A]
+@<Declare subprocedures for |line_break|@>=
+static void try_break(int @!pi, small_number @!break_type)
+{@+
+pointer r; /*runs through the active list*/
+pointer @!prev_r; /*stays a step behind |r|*/
+halfword @!old_l; /*maximum line number in current equivalence class of lines*/
+bool @!no_break_yet; /*have we found a feasible break at |cur_p|?*/
+@<Other local variables for |try_break|@>@;
+@<Make sure that |pi| is in the proper range@>;
+no_break_yet=true;prev_r=active;old_l=0;
+do_all_six(copy_to_cur_active);
+loop@+{@+resume: r=link(prev_r);
+ @<If node |r| is of type |delta_node|, update |cur_active_width|, set |prev_r|
+and |prev_prev_r|, then |goto resume|@>;
+ @<If a line number class has ended, create new active nodes for the best
+feasible breaks in that class; then |return| if |r=last_active|, otherwise
+compute the new |line_width|@>;
+ @<Consider the demerits for a line from |r| to |cur_p|; deactivate node
+|r| if it should no longer be active; then |goto resume| if a line from
+|r| to |cur_p| is infeasible, otherwise record a new feasible break@>;
+ }
+end: ;
+#ifdef @!STAT
+@<Update the value of |printed_node| for symbolic displays@>;
+#endif
+@;
+}
+
+@ @<Other local variables for |try_break|@>=
+pointer @!prev_prev_r; /*a step behind |prev_r|, if |type(prev_r)==delta_node|*/
+pointer @!s; /*runs through nodes ahead of |cur_p|*/
+pointer @!q; /*points to a new node being created*/
+pointer @!v; /*points to a glue specification or a node ahead of |cur_p|*/
+int @!t; /*node count, if |cur_p| is a discretionary node*/
+internal_font_number @!f; /*used in character width calculation*/
+halfword @!l; /*line number of current active node*/
+bool @!node_r_stays_active; /*should node |r| remain in the active list?*/
+scaled @!line_width; /*the current line will be justified to this width*/
+int @!fit_class; /*possible fitness class of test line*/
+halfword @!b; /*badness of test line*/
+int @!d; /*demerits of test line*/
+bool @!artificial_demerits; /*has |d| been forced to zero?*/
+#ifdef @!STAT
+pointer @!save_link; /*temporarily holds value of |link(cur_p)|*/
+#endif
+scaled @!shortfall; /*used in badness calculations*/
+
+@ @<Make sure that |pi| is in the proper range@>=
+if (abs(pi) >= inf_penalty)
+ if (pi > 0) goto end; /*this breakpoint is inhibited by infinite penalty*/
+ else pi=eject_penalty /*this breakpoint will be forced*/
+
+@ The following code uses the fact that |type(last_active)!=delta_node|.
+
+@d update_width(A) @|
+ cur_active_width[A]=cur_active_width[A]+mem[r+A].sc
+
+@<If node |r|...@>=
+@^inner loop@>
+if (type(r)==delta_node)
+ {@+do_all_six(update_width);
+ prev_prev_r=prev_r;prev_r=r;goto resume;
+ }
+
+@ As we consider various ways to end a line at |cur_p|, in a given line number
+class, we keep track of the best total demerits known, in an array with
+one entry for each of the fitness classifications. For example,
+|minimal_demerits[tight_fit]| contains the fewest total demerits of feasible
+line breaks ending at |cur_p| with a |tight_fit| line; |best_place[tight_fit]|
+points to the passive node for the break before~|cur_p| that achieves such
+an optimum; and |best_pl_line[tight_fit]| is the |line_number| field in the
+active node corresponding to |best_place[tight_fit]|. When no feasible break
+sequence is known, the |minimal_demerits| entries will be equal to
+|awful_bad|, which is $2^{30}-1$. Another variable, |minimum_demerits|,
+keeps track of the smallest value in the |minimal_demerits| array.
+
+@d awful_bad 07777777777 /*more than a billion demerits*/
+
+@<Global...@>=
+static int @!minimal_demerits0[tight_fit-very_loose_fit+1],
+ *const @!minimal_demerits = @!minimal_demerits0-very_loose_fit; /*best total
+ demerits known for current line class and position, given the fitness*/
+static int @!minimum_demerits; /*best total demerits known for current line class
+ and position*/
+static pointer @!best_place0[tight_fit-very_loose_fit+1],
+ *const @!best_place = @!best_place0-very_loose_fit; /*how to achieve
+ |minimal_demerits|*/
+static halfword @!best_pl_line0[tight_fit-very_loose_fit+1],
+ *const @!best_pl_line = @!best_pl_line0-very_loose_fit; /*corresponding
+ line number*/
+
+@ @<Get ready to start...@>=
+minimum_demerits=awful_bad;
+minimal_demerits[tight_fit]=awful_bad;
+minimal_demerits[decent_fit]=awful_bad;
+minimal_demerits[loose_fit]=awful_bad;
+minimal_demerits[very_loose_fit]=awful_bad;
+
+@ The first part of the following code is part of \TeX's inner loop, so
+we don't want to waste any time. The current active node, namely node |r|,
+contains the line number that will be considered next. At the end of the
+list we have arranged the data structure so that |r==last_active| and
+|line_number(last_active) > old_l|.
+@^inner loop@>
+
+@<If a line number class...@>=
+{@+l=line_number(r);
+if (l > old_l)
+ {@+ /*now we are no longer in the inner loop*/
+ if ((minimum_demerits < awful_bad)&&@|
+ ((old_l!=easy_line)||(r==last_active)))
+ @<Create new active nodes for the best feasible breaks just found@>;
+ if (r==last_active) goto end;
+ @<Compute the new line width@>;
+ }
+}
+
+@ It is not necessary to create new active nodes having |minimal_demerits|
+greater than
+|minimum_demerits+abs(adj_demerits)|, since such active nodes will never
+be chosen in the final paragraph breaks. This observation allows us to
+omit a substantial number of feasible breakpoints from further consideration.
+
+@<Create new active nodes...@>=
+{@+if (no_break_yet) @<Compute the values of |break_width|@>;
+@<Insert a delta node to prepare for breaks at |cur_p|@>;
+if (abs(adj_demerits) >= awful_bad-minimum_demerits)
+ minimum_demerits=awful_bad-1;
+else minimum_demerits=minimum_demerits+abs(adj_demerits);
+for (fit_class=very_loose_fit; fit_class<=tight_fit; fit_class++)
+ {@+if (minimal_demerits[fit_class] <= minimum_demerits)
+ @<Insert a new active node from |best_place[fit_class]| to |cur_p|@>;
+ minimal_demerits[fit_class]=awful_bad;
+ }
+minimum_demerits=awful_bad;
+@<Insert a delta node to prepare for the next active node@>;
+}
+
+@ When we insert a new active node for a break at |cur_p|, suppose this
+new node is to be placed just before active node |a|; then we essentially
+want to insert `$\delta\,|cur_p|\,\delta^\prime$' before |a|, where
+$\delta=\alpha(a)-\alpha(|cur_p|)$ and $\delta^\prime=\alpha(|cur_p|)-\alpha(a)$
+in the notation explained above. The |cur_active_width| array now holds
+$\gamma+\beta(|cur_p|)-\alpha(a)$; so $\delta$ can be obtained by
+subtracting |cur_active_width| from the quantity $\gamma+\beta(|cur_p|)-
+\alpha(|cur_p|)$. The latter quantity can be regarded as the length of a
+line ``from |cur_p| to |cur_p|''; we call it the |break_width| at |cur_p|.
+
+The |break_width| is usually negative, since it consists of the background
+(which is normally zero) minus the width of nodes following~|cur_p| that are
+eliminated after a break. If, for example, node |cur_p| is a glue node, the
+width of this glue is subtracted from the background; and we also look
+ahead to eliminate all subsequent glue and penalty and kern and math
+nodes, subtracting their widths as well.
+
+Kern nodes do not disappear at a line break unless they are |explicit|.
+
+@d set_break_width_to_background(A) break_width[A]=background[A]
+
+@<Compute the values of |break...@>=
+{@+no_break_yet=false;do_all_six(set_break_width_to_background);
+s=cur_p;
+if (break_type > unhyphenated) if (cur_p!=null)
+ @<Compute the discretionary |break_width| values@>;
+while (s!=null)
+ {@+if (is_char_node(s)) goto done;
+ switch (type(s)) {
+ case glue_node: @<Subtract glue from |break_width|@>@;@+break;
+ case penalty_node: do_nothing;@+break;
+ case math_node: break_width[1]=break_width[1]-width(s);@+break;
+ case kern_node: if (subtype(s)!=explicit) goto done;
+ else break_width[1]=break_width[1]-width(s);@+break;
+ default:goto done;
+ } @/
+ s=link(s);
+ }
+done: ;}
+
+@ @<Subtract glue from |break...@>=
+{@+v=glue_ptr(s);break_width[1]=break_width[1]-width(v);
+break_width[2+stretch_order(v)]=break_width[2+stretch_order(v)]-stretch(v);
+break_width[6]=break_width[6]-shrink(v);
+}
+
+@ When |cur_p| is a discretionary break, the length of a line ``from |cur_p| to
+|cur_p|'' has to be defined properly so that the other calculations work out.
+Suppose that the pre-break text at |cur_p| has length $l_0$, the post-break
+text has length $l_1$, and the replacement text has length |l|. Suppose
+also that |q| is the node following the replacement text. Then length of a
+line from |cur_p| to |q| will be computed as $\gamma+\beta(q)-\alpha(|cur_p|)$,
+where $\beta(q)=\beta(|cur_p|)-l_0+l$. The actual length will be the background
+plus $l_1$, so the length from |cur_p| to |cur_p| should be $\gamma+l_0+l_1-l$.
+If the post-break text of the discretionary is empty, a break may also
+discard~|q|; in that unusual case we subtract the length of~|q| and any
+other nodes that will be discarded after the discretionary break.
+
+The value of $l_0$ need not be computed, since |line_break| will put
+it into the global variable |disc_width| before calling |try_break|.
+
+@<Glob...@>=
+static scaled @!disc_width; /*the length of discretionary material preceding a break*/
+
+@ @<Compute the discretionary |break...@>=
+{@+t=replace_count(cur_p);v=cur_p;s=post_break(cur_p);
+while (t > 0)
+ {@+decr(t);v=link(v);
+ @<Subtract the width of node |v| from |break_width|@>;
+ }
+while (s!=null)
+ {@+@<Add the width of node |s| to |break_width|@>;
+ s=link(s);
+ }
+break_width[1]=break_width[1]+disc_width;
+if (post_break(cur_p)==null) s=link(v);
+ /*nodes may be discardable after the break*/
+}
+
+@ Replacement texts and discretionary texts are supposed to contain
+only character nodes, kern nodes, ligature nodes, and box or rule nodes.
+
+@<Subtract the width of node |v|...@>=
+if (is_char_node(v))
+ {@+f=font(v);
+ break_width[1]=break_width[1]-char_width(f, char_info(f, character(v)));
+ }
+else switch (type(v)) {
+ case ligature_node: {@+f=font(lig_char(v));@/
+ break_width[1]=@|break_width[1]-
+ char_width(f, char_info(f, character(lig_char(v))));
+ } @+break;
+ case hlist_node: case vlist_node: case rule_node:
+ case kern_node:
+ break_width[1]=break_width[1]-width(v);@+break;
+ default:confusion("disc1");
+@:this can't happen disc1}{\quad disc1@>
+ }
+
+@ @<Add the width of node |s| to |b...@>=
+if (is_char_node(s))
+ {@+f=font(s);
+ break_width[1]=@|break_width[1]+char_width(f, char_info(f, character(s)));
+ }
+else switch (type(s)) {
+ case ligature_node: {@+f=font(lig_char(s));
+ break_width[1]=break_width[1]+
+ char_width(f, char_info(f, character(lig_char(s))));
+ } @+break;
+ case hlist_node: case vlist_node: case rule_node:
+ case kern_node:
+ break_width[1]=break_width[1]+width(s);@+break;
+ default:confusion("disc2");
+@:this can't happen disc2}{\quad disc2@>
+ }
+
+@ We use the fact that |type(active)!=delta_node|.
+
+@d convert_to_break_width(A) @|
+ mem[prev_r+A].sc=@|@t\hskip10pt@>mem[prev_r+A].sc
+ -cur_active_width[A]+break_width[A]
+@d store_break_width(A) active_width[A]=break_width[A]
+@d new_delta_to_break_width(A) @|
+ mem[q+A].sc=break_width[A]-cur_active_width[A]
+
+@<Insert a delta node to prepare for breaks at |cur_p|@>=
+if (type(prev_r)==delta_node) /*modify an existing delta node*/
+ {@+do_all_six(convert_to_break_width);
+ }
+else if (prev_r==active) /*no delta node needed at the beginning*/
+ {@+do_all_six(store_break_width);
+ }
+else{@+q=get_node(delta_node_size);link(q)=r;type(q)=delta_node;@/
+ subtype(q)=0; /*the |subtype| is not used*/
+ do_all_six(new_delta_to_break_width);
+ link(prev_r)=q;prev_prev_r=prev_r;prev_r=q;
+ }
+
+@ When the following code is performed, we will have just inserted at
+least one active node before |r|, so |type(prev_r)!=delta_node|.
+
+@d new_delta_from_break_width(A) @|mem[q+A].sc=
+ cur_active_width[A]-break_width[A]
+
+@<Insert a delta node to prepare for the next active node@>=
+if (r!=last_active)
+ {@+q=get_node(delta_node_size);link(q)=r;type(q)=delta_node;@/
+ subtype(q)=0; /*the |subtype| is not used*/
+ do_all_six(new_delta_from_break_width);
+ link(prev_r)=q;prev_prev_r=prev_r;prev_r=q;
+ }
+
+@ When we create an active node, we also create the corresponding
+passive node.
+
+@<Insert a new active node from |best_place[fit_class]| to |cur_p|@>=
+{@+q=get_node(passive_node_size);
+link(q)=passive;passive=q;cur_break(q)=cur_p;
+#ifdef @!STAT
+incr(pass_number);serial(q)=pass_number;
+#endif
+@;@/
+prev_break(q)=best_place[fit_class];@/
+q=get_node(active_node_size);break_node(q)=passive;
+line_number(q)=best_pl_line[fit_class]+1;
+fitness(q)=fit_class;type(q)=break_type;
+total_demerits(q)=minimal_demerits[fit_class];
+link(q)=r;link(prev_r)=q;prev_r=q;
+#ifdef @!STAT
+if (tracing_paragraphs > 0)
+ @<Print a symbolic description of the new break node@>;
+#endif
+@;@/
+}
+
+@ @<Print a symbolic description of the new break node@>=
+{@+print_nl("@@@@");print_int(serial(passive));
+@.\AT\AT@>
+print(": line ");print_int(line_number(q)-1);
+print_char('.');print_int(fit_class);
+if (break_type==hyphenated) print_char('-');
+print(" t=");print_int(total_demerits(q));
+print(" -> @@@@");
+if (prev_break(passive)==null) print_char('0');
+else print_int(serial(prev_break(passive)));
+}
+
+@ The length of lines depends on whether the user has specified
+\.{\\parshape} or \.{\\hangindent}. If |par_shape_ptr| is not null, it
+points to a $(2n+1)$-word record in |mem|, where the |info| in the first
+word contains the value of |n|, and the other $2n$ words contain the left
+margins and line lengths for the first |n| lines of the paragraph; the
+specifications for line |n| apply to all subsequent lines. If
+|par_shape_ptr==null|, the shape of the paragraph depends on the value of
+|n==hang_after|; if |n >= 0|, hanging indentation takes place on lines |n+1|,
+|n+2|, \dots, otherwise it takes place on lines 1, \dots, $\vert
+n\vert$. When hanging indentation is active, the left margin is
+|hang_indent|, if |hang_indent >= 0|, else it is 0; the line length is
+$|hsize|-\vert|hang_indent|\vert$. The normal setting is
+|par_shape_ptr==null|, |hang_after==1|, and |hang_indent==0|.
+Note that if |hang_indent==0|, the value of |hang_after| is irrelevant.
+@^length of lines@> @^hanging indentation@>
+
+@<Glob...@>=
+static halfword @!easy_line; /*line numbers | > easy_line| are equivalent in break nodes*/
+static halfword @!last_special_line; /*line numbers | > last_special_line| all have
+ the same width*/
+static scaled @!first_width; /*the width of all lines | <= last_special_line|, if
+ no \.{\\parshape} has been specified*/
+static scaled @!second_width; /*the width of all lines | > last_special_line|*/
+static scaled @!first_indent; /*left margin to go with |first_width|*/
+static scaled @!second_indent; /*left margin to go with |second_width|*/
+
+@ We compute the values of |easy_line| and the other local variables relating
+to line length when the |line_break| procedure is initializing itself.
+
+@<Get ready to start...@>=
+if (par_shape_ptr==null)
+ if (hang_indent==0)
+ {@+last_special_line=0;second_width=hsize;
+ second_indent=0;
+ }
+ else@<Set line length parameters in preparation for hanging indentation@>@;
+else{@+last_special_line=info(par_shape_ptr)-1;
+ second_width=mem[par_shape_ptr+2*(last_special_line+1)].sc;
+ second_indent=mem[par_shape_ptr+2*last_special_line+1].sc;
+ }
+if (looseness==0) easy_line=last_special_line;
+else easy_line=max_halfword
+
+@ @<Set line length parameters in preparation for hanging indentation@>=
+{@+last_special_line=abs(hang_after);
+if (hang_after < 0)
+ {@+first_width=hsize-abs(hang_indent);
+ if (hang_indent >= 0) first_indent=hang_indent;
+ else first_indent=0;
+ second_width=hsize;second_indent=0;
+ }
+else{@+first_width=hsize;first_indent=0;
+ second_width=hsize-abs(hang_indent);
+ if (hang_indent >= 0) second_indent=hang_indent;
+ else second_indent=0;
+ }
+}
+
+@ When we come to the following code, we have just encountered the first
+active node~|r| whose |line_number| field contains |l|. Thus we want to
+compute the length of the $l\mskip1mu$th line of the current paragraph. Furthermore,
+we want to set |old_l| to the last number in the class of line numbers
+equivalent to~|l|.
+
+@<Compute the new line width@>=
+if (l > easy_line)
+ {@+line_width=second_width;old_l=max_halfword-1;
+ }
+else{@+old_l=l;
+ if (l > last_special_line) line_width=second_width;
+ else if (par_shape_ptr==null) line_width=first_width;
+ else line_width=mem[par_shape_ptr+2*l@,].sc;
+ }
+
+@ The remaining part of |try_break| deals with the calculation of
+demerits for a break from |r| to |cur_p|.
+
+The first thing to do is calculate the badness, |b|. This value will always
+be between zero and |inf_bad+1|; the latter value occurs only in the
+case of lines from |r| to |cur_p| that cannot shrink enough to fit the necessary
+width. In such cases, node |r| will be deactivated.
+We also deactivate node~|r| when a break at~|cur_p| is forced, since future
+breaks must go through a forced break.
+
+@<Consider the demerits for a line from |r| to |cur_p|...@>=
+{@+artificial_demerits=false;@/
+@^inner loop@>
+shortfall=line_width-cur_active_width[1]; /*we're this much too short*/
+if (shortfall > 0)
+ @<Set the value of |b| to the badness for stretching the line, and compute
+the corresponding |fit_class|@>@;
+else@<Set the value of |b| to the badness for shrinking the line, and compute
+the corresponding |fit_class|@>;
+if ((b > inf_bad)||(pi==eject_penalty))
+ @<Prepare to deactivate node~|r|, and |goto deactivate| unless there is
+a reason to consider lines of text from |r| to |cur_p|@>@;
+else{@+prev_r=r;
+ if (b > threshold) goto resume;
+ node_r_stays_active=true;
+ }
+@<Record a new feasible break@>;
+if (node_r_stays_active) goto resume; /*|prev_r| has been set to |r|*/
+deactivate: @<Deactivate node |r|@>;
+}
+
+@ When a line must stretch, the available stretchability can be found in the
+subarray |cur_active_width[2 dotdot 5]|, in units of points, fil, fill, and filll.
+
+The present section is part of \TeX's inner loop, and it is most often performed
+when the badness is infinite; therefore it is worth while to make a quick
+test for large width excess and small stretchability, before calling the
+|badness| subroutine.
+@^inner loop@>
+
+@<Set the value of |b| to the badness for stretching...@>=
+if ((cur_active_width[3]!=0)||(cur_active_width[4]!=0)||@|
+ (cur_active_width[5]!=0))
+ {@+b=0;fit_class=decent_fit; /*infinite stretch*/
+ }
+else{@+if (shortfall > 7230584) if (cur_active_width[2] < 1663497)
+ {@+b=inf_bad;fit_class=very_loose_fit;goto done1;
+ }
+ b=badness(shortfall, cur_active_width[2]);
+ if (b > 12)
+ if (b > 99) fit_class=very_loose_fit;
+ else fit_class=loose_fit;
+ else fit_class=decent_fit;
+ done1: ;
+ }
+
+@ Shrinkability is never infinite in a paragraph;
+we can shrink the line from |r| to |cur_p| by at most |cur_active_width[6]|.
+
+@<Set the value of |b| to the badness for shrinking...@>=
+{@+if (-shortfall > cur_active_width[6]) b=inf_bad+1;
+else b=badness(-shortfall, cur_active_width[6]);
+if (b > 12) fit_class=tight_fit;@+else fit_class=decent_fit;
+}
+
+@ During the final pass, we dare not lose all active nodes, lest we lose
+touch with the line breaks already found. The code shown here makes sure
+that such a catastrophe does not happen, by permitting overfull boxes as
+a last resort. This particular part of \TeX\ was a source of several subtle
+bugs before the correct program logic was finally discovered; readers
+who seek to ``improve'' \TeX\ should therefore think thrice before daring
+to make any changes here.
+@^overfull boxes@>
+
+@<Prepare to deactivate node~|r|, and |goto deactivate| unless...@>=
+{@+if (final_pass&&(minimum_demerits==awful_bad)&&@|
+ (link(r)==last_active)&&
+ (prev_r==active))
+ artificial_demerits=true; /*set demerits zero, this break is forced*/
+else if (b > threshold) goto deactivate;
+node_r_stays_active=false;
+}
+
+@ When we get to this part of the code, the line from |r| to |cur_p| is
+feasible, its badness is~|b|, and its fitness classification is |fit_class|.
+We don't want to make an active node for this break yet, but we will
+compute the total demerits and record them in the |minimal_demerits| array,
+if such a break is the current champion among all ways to get to |cur_p|
+in a given line-number class and fitness class.
+
+@<Record a new feasible break@>=
+if (artificial_demerits) d=0;
+else@<Compute the demerits, |d|, from |r| to |cur_p|@>;
+#ifdef @!STAT
+if (tracing_paragraphs > 0)
+ @<Print a symbolic description of this feasible break@>;
+#endif
+@;@/
+d=d+total_demerits(r); /*this is the minimum total demerits
+ from the beginning to |cur_p| via |r|*/
+if (d <= minimal_demerits[fit_class])
+ {@+minimal_demerits[fit_class]=d;
+ best_place[fit_class]=break_node(r);best_pl_line[fit_class]=l;
+ if (d < minimum_demerits) minimum_demerits=d;
+ }
+
+@ @<Print a symbolic description of this feasible break@>=
+{@+if (printed_node!=cur_p)
+ @<Print the list between |printed_node| and |cur_p|, then set |printed_node:=cur_p|@>;
+print_nl("@@");
+@.\AT@>
+if (cur_p==null) print_esc("par");
+else if (type(cur_p)!=glue_node)
+ {@+if (type(cur_p)==penalty_node) print_esc("penalty");
+ else if (type(cur_p)==disc_node) print_esc("discretionary");
+ else if (type(cur_p)==kern_node) print_esc("kern");
+ else print_esc("math");
+ }
+print(" via @@@@");
+if (break_node(r)==null) print_char('0');
+else print_int(serial(break_node(r)));
+print(" b=");
+if (b > inf_bad) print_char('*');@+else print_int(b);
+@.*\relax@>
+print(" p=");print_int(pi);print(" d=");
+if (artificial_demerits) print_char('*');@+else print_int(d);
+}
+
+@ @<Print the list between |printed_node| and |cur_p|...@>=
+{@+print_nl("");
+if (cur_p==null) short_display(link(printed_node));
+else{@+save_link=link(cur_p);
+ link(cur_p)=null;print_nl("");short_display(link(printed_node));
+ link(cur_p)=save_link;
+ }
+printed_node=cur_p;
+}
+
+@ When the data for a discretionary break is being displayed, we will have
+printed the |pre_break| and |post_break| lists; we want to skip over the
+third list, so that the discretionary data will not appear twice. The
+following code is performed at the very end of |try_break|.
+
+@<Update the value of |printed_node|...@>=
+if (cur_p==printed_node) if (cur_p!=null) if (type(cur_p)==disc_node)
+ {@+t=replace_count(cur_p);
+ while (t > 0)
+ {@+decr(t);printed_node=link(printed_node);
+ }
+ }
+
+@ @<Compute the demerits, |d|, from |r| to |cur_p|@>=
+{@+d=line_penalty+b;
+if (abs(d) >= 10000) d=100000000;@+else d=d*d;
+if (pi!=0)
+ if (pi > 0) d=d+pi*pi;
+ else if (pi > eject_penalty) d=d-pi*pi;
+if ((break_type==hyphenated)&&(type(r)==hyphenated))
+ if (cur_p!=null) d=d+double_hyphen_demerits;
+ else d=d+final_hyphen_demerits;
+if (abs(fit_class-fitness(r)) > 1) d=d+adj_demerits;
+}
+
+@ When an active node disappears, we must delete an adjacent delta node if the
+active node was at the beginning or the end of the active list, or if it
+was surrounded by delta nodes. We also must preserve the property that
+|cur_active_width| represents the length of material from |link(prev_r)|
+to~|cur_p|.
+
+@d combine_two_deltas(A) @|mem[prev_r+A].sc=mem[prev_r+A].sc+mem[r+A].sc
+@d downdate_width(A) @|cur_active_width[A]=cur_active_width[A]-
+ mem[prev_r+A].sc
+
+@<Deactivate node |r|@>=
+link(prev_r)=link(r);free_node(r, active_node_size);
+if (prev_r==active) @<Update the active widths, since the first active node
+has been deleted@>@;
+else if (type(prev_r)==delta_node)
+ {@+r=link(prev_r);
+ if (r==last_active)
+ {@+do_all_six(downdate_width);
+ link(prev_prev_r)=last_active;
+ free_node(prev_r, delta_node_size);prev_r=prev_prev_r;
+ }
+ else if (type(r)==delta_node)
+ {@+do_all_six(update_width);
+ do_all_six(combine_two_deltas);
+ link(prev_r)=link(r);free_node(r, delta_node_size);
+ }
+ }
+
+@ The following code uses the fact that |type(last_active)!=delta_node|. If the
+active list has just become empty, we do not need to update the
+|active_width| array, since it will be initialized when an active
+node is next inserted.
+
+@d update_active(A) active_width[A]=active_width[A]+mem[r+A].sc
+
+@<Update the active widths,...@>=
+{@+r=link(active);
+if (type(r)==delta_node)
+ {@+do_all_six(update_active);
+ do_all_six(copy_to_cur_active);
+ link(active)=link(r);free_node(r, delta_node_size);
+ }
+}
+
+@* Breaking paragraphs into lines, continued.
+So far we have gotten a little way into the |line_break| routine, having
+covered its important |try_break| subroutine. Now let's consider the
+rest of the process.
+
+The main loop of |line_break| traverses the given hlist,
+starting at |link(temp_head)|, and calls |try_break| at each legal
+breakpoint. A variable called |auto_breaking| is set to true except
+within math formulas, since glue nodes are not legal breakpoints when
+they appear in formulas.
+
+The current node of interest in the hlist is pointed to by |cur_p|. Another
+variable, |prev_p|, is usually one step behind |cur_p|, but the real
+meaning of |prev_p| is this: If |type(cur_p)==glue_node| then |cur_p| is a legal
+breakpoint if and only if |auto_breaking| is true and |prev_p| does not
+point to a glue node, penalty node, explicit kern node, or math node.
+
+The following declarations provide for a few other local variables that are
+used in special calculations.
+
+@<Local variables for line breaking@>=
+bool @!auto_breaking; /*is node |cur_p| outside a formula?*/
+pointer @!prev_p; /*helps to determine when glue nodes are breakpoints*/
+pointer @!q, @!r, @!s, @!prev_s; /*miscellaneous nodes of temporary interest*/
+internal_font_number @!f; /*used when calculating character widths*/
+
+@ The `\ignorespaces|loop|\unskip' in the following code is performed at most
+thrice per call of |line_break|, since it is actually a pass over the
+entire paragraph.
+
+@<Find optimal breakpoints@>=
+threshold=pretolerance;
+if (threshold >= 0)
+ {
+#ifdef @!STAT
+if (tracing_paragraphs > 0)
+ {@+begin_diagnostic();print_nl("@@firstpass");@+} @;
+#endif
+@;@/
+ second_pass=false;final_pass=false;
+ }
+else{@+threshold=tolerance;second_pass=true;
+ final_pass=(emergency_stretch <= 0);
+#ifdef @!STAT
+ if (tracing_paragraphs > 0) begin_diagnostic();
+#endif
+@;
+ }
+loop@+{@+if (threshold > inf_bad) threshold=inf_bad;
+ if (second_pass) @<Initialize for hyphenating a paragraph@>;
+ @<Create an active breakpoint representing the beginning of the paragraph@>;
+ cur_p=link(temp_head);auto_breaking=true;@/
+ prev_p=cur_p; /*glue at beginning is not a legal breakpoint*/
+ while ((cur_p!=null)&&(link(active)!=last_active))
+ @<Call |try_break| if |cur_p| is a legal breakpoint; on the second pass,
+also try to hyphenate the next word, if |cur_p| is a glue node; then advance
+|cur_p| to the next node of the paragraph that could possibly be a legal breakpoint@>;
+ if (cur_p==null)
+ @<Try the final line break at the end of the paragraph, and |goto done|
+if the desired breakpoints have been found@>;
+ @<Clean up the memory by removing the break nodes@>;
+ if (!second_pass)
+ {
+#ifdef @!STAT
+if (tracing_paragraphs > 0) print_nl("@@secondpass");@;
+#endif
+ threshold=tolerance;second_pass=true;final_pass=(emergency_stretch <= 0);
+ } /*if at first you don't succeed, \dots*/
+ else{
+#ifdef @!STAT
+if (tracing_paragraphs > 0)
+ print_nl("@@emergencypass");@;
+#endif
+ background[2]=background[2]+emergency_stretch;final_pass=true;
+ }
+ }
+done:
+#ifdef @!STAT
+if (tracing_paragraphs > 0)
+ {@+end_diagnostic(true);normalize_selector();
+ }
+#endif
+
+@ The active node that represents the starting point does not need a
+corresponding passive node.
+
+@d store_background(A) active_width[A]=background[A]
+
+@<Create an active breakpoint representing the beginning of the paragraph@>=
+q=get_node(active_node_size);
+type(q)=unhyphenated;fitness(q)=decent_fit;
+link(q)=last_active;break_node(q)=null;
+line_number(q)=prev_graf+1;total_demerits(q)=0;link(active)=q;
+do_all_six(store_background);@/
+passive=null;printed_node=temp_head;pass_number=0;
+font_in_short_display=null_font
+
+@ @<Clean...@>=
+q=link(active);
+while (q!=last_active)
+ {@+cur_p=link(q);
+ if (type(q)==delta_node) free_node(q, delta_node_size);
+ else free_node(q, active_node_size);
+ q=cur_p;
+ }
+q=passive;
+while (q!=null)
+ {@+cur_p=link(q);
+ free_node(q, passive_node_size);
+ q=cur_p;
+ }
+
+@ Here is the main switch in the |line_break| routine, where legal breaks
+are determined. As we move through the hlist, we need to keep the |active_width|
+array up to date, so that the badness of individual lines is readily calculated
+by |try_break|. It is convenient to use the short name |act_width| for
+the component of active width that represents real width as opposed to glue.
+
+@d act_width active_width[1] /*length from first active node to current node*/
+@d kern_break {@+if (!is_char_node(link(cur_p))&&auto_breaking)
+ if (type(link(cur_p))==glue_node) try_break(0, unhyphenated);
+ act_width=act_width+width(cur_p);
+ }
+
+@<Call |try_break| if |cur_p| is a legal breakpoint...@>=
+{@+if (is_char_node(cur_p))
+ @<Advance \(c)|cur_p| to the node following the present string of characters@>;
+switch (type(cur_p)) {
+case hlist_node: case vlist_node: case rule_node: act_width=act_width+width(cur_p);@+break;
+case whatsit_node: @<Advance \(p)past a whatsit node in the \(l)|line_break|
+loop@>@;@+break;
+case glue_node: {@+@<If node |cur_p| is a legal breakpoint, call |try_break|;
+then update the active widths by including the glue in |glue_ptr(cur_p)|@>;
+ if (second_pass&&auto_breaking)
+ @<Try to hyphenate the following word@>;
+ } @+break;
+case kern_node: if (subtype(cur_p)==explicit) kern_break@;
+ else act_width=act_width+width(cur_p);@+break;
+case ligature_node: {@+f=font(lig_char(cur_p));
+ act_width=act_width+char_width(f, char_info(f, character(lig_char(cur_p))));
+ } @+break;
+case disc_node: @<Try to break after a discretionary fragment, then |goto
+done5|@>@;
+case math_node: {@+auto_breaking=(subtype(cur_p)==after);kern_break;
+ } @+break;
+case penalty_node: try_break(penalty(cur_p), unhyphenated);@+break;
+case mark_node: case ins_node: case adjust_node: do_nothing;@+break;
+default:confusion("paragraph");
+@:this can't happen paragraph}{\quad paragraph@>
+} @/
+prev_p=cur_p;cur_p=link(cur_p);
+done5: ;}
+
+@ The code that passes over the characters of words in a paragraph is
+part of \TeX's inner loop, so it has been streamlined for speed. We use
+the fact that `\.{\\parfillskip}' glue appears at the end of each paragraph;
+it is therefore unnecessary to check if |link(cur_p)==null| when |cur_p| is a
+character node.
+@^inner loop@>
+
+@<Advance \(c)|cur_p| to the node following the present string...@>=
+{@+prev_p=cur_p;
+@/do@+{f=font(cur_p);
+act_width=act_width+char_width(f, char_info(f, character(cur_p)));
+cur_p=link(cur_p);
+}@+ while (!(!is_char_node(cur_p)));
+}
+
+@ When node |cur_p| is a glue node, we look at |prev_p| to see whether or not
+a breakpoint is legal at |cur_p|, as explained above.
+
+@<If node |cur_p| is a legal breakpoint, call...@>=
+if (auto_breaking)
+ {@+if (is_char_node(prev_p)) try_break(0, unhyphenated);
+ else if (precedes_break(prev_p)) try_break(0, unhyphenated);
+ else if ((type(prev_p)==kern_node)&&(subtype(prev_p)!=explicit))
+ try_break(0, unhyphenated);
+ }
+check_shrinkage(glue_ptr(cur_p));q=glue_ptr(cur_p);
+act_width=act_width+width(q);@|
+active_width[2+stretch_order(q)]=@|
+ active_width[2+stretch_order(q)]+stretch(q);@/
+active_width[6]=active_width[6]+shrink(q)
+
+@ The following code knows that discretionary texts contain
+only character nodes, kern nodes, box nodes, rule nodes, and ligature nodes.
+
+@<Try to break after a discretionary fragment...@>=
+{@+s=pre_break(cur_p);disc_width=0;
+if (s==null) try_break(ex_hyphen_penalty, hyphenated);
+else{@+@/do@+{@<Add the width of node |s| to |disc_width|@>;
+ s=link(s);
+ }@+ while (!(s==null));
+ act_width=act_width+disc_width;
+ try_break(hyphen_penalty, hyphenated);
+ act_width=act_width-disc_width;
+ }
+r=replace_count(cur_p);s=link(cur_p);
+while (r > 0)
+ {@+@<Add the width of node |s| to |act_width|@>;
+ decr(r);s=link(s);
+ }
+prev_p=cur_p;cur_p=s;goto done5;
+}
+
+@ @<Add the width of node |s| to |disc_width|@>=
+if (is_char_node(s))
+ {@+f=font(s);
+ disc_width=disc_width+char_width(f, char_info(f, character(s)));
+ }
+else switch (type(s)) {
+ case ligature_node: {@+f=font(lig_char(s));
+ disc_width=disc_width+
+ char_width(f, char_info(f, character(lig_char(s))));
+ } @+break;
+ case hlist_node: case vlist_node: case rule_node:
+ case kern_node:
+ disc_width=disc_width+width(s);@+break;
+ default:confusion("disc3");
+@:this can't happen disc3}{\quad disc3@>
+ }
+
+@ @<Add the width of node |s| to |act_width|@>=
+if (is_char_node(s))
+ {@+f=font(s);
+ act_width=act_width+char_width(f, char_info(f, character(s)));
+ }
+else switch (type(s)) {
+ case ligature_node: {@+f=font(lig_char(s));
+ act_width=act_width+
+ char_width(f, char_info(f, character(lig_char(s))));
+ } @+break;
+ case hlist_node: case vlist_node: case rule_node:
+ case kern_node:
+ act_width=act_width+width(s);@+break;
+ default:confusion("disc4");
+@:this can't happen disc4}{\quad disc4@>
+ }
+
+@ The forced line break at the paragraph's end will reduce the list of
+breakpoints so that all active nodes represent breaks at |cur_p==null|.
+On the first pass, we insist on finding an active node that has the
+correct ``looseness.'' On the final pass, there will be at least one active
+node, and we will match the desired looseness as well as we can.
+
+The global variable |best_bet| will be set to the active node for the best
+way to break the paragraph, and a few other variables are used to
+help determine what is best.
+
+@<Glob...@>=
+static pointer @!best_bet; /*use this passive node and its predecessors*/
+static int @!fewest_demerits; /*the demerits associated with |best_bet|*/
+static halfword @!best_line; /*line number following the last line of the new paragraph*/
+static int @!actual_looseness; /*the difference between |line_number(best_bet)|
+ and the optimum |best_line|*/
+static int @!line_diff; /*the difference between the current line number and
+ the optimum |best_line|*/
+
+@ @<Try the final line break at the end of the paragraph...@>=
+{@+try_break(eject_penalty, hyphenated);
+if (link(active)!=last_active)
+ {@+@<Find an active node with fewest demerits@>;
+ if (looseness==0) goto done;
+ @<Find the best active node for the desired looseness@>;
+ if ((actual_looseness==looseness)||final_pass) goto done;
+ }
+}
+
+@ @<Find an active node...@>=
+r=link(active);fewest_demerits=awful_bad;
+@/do@+{if (type(r)!=delta_node) if (total_demerits(r) < fewest_demerits)
+ {@+fewest_demerits=total_demerits(r);best_bet=r;
+ }
+r=link(r);
+}@+ while (!(r==last_active));
+best_line=line_number(best_bet)
+
+@ The adjustment for a desired looseness is a slightly more complicated
+version of the loop just considered. Note that if a paragraph is broken
+into segments by displayed equations, each segment will be subject to the
+looseness calculation, independently of the other segments.
+
+@<Find the best active node...@>=
+{@+r=link(active);actual_looseness=0;
+@/do@+{if (type(r)!=delta_node)
+ {@+line_diff=line_number(r)-best_line;
+ if (((line_diff < actual_looseness)&&(looseness <= line_diff))||@|
+ ((line_diff > actual_looseness)&&(looseness >= line_diff)))
+ {@+best_bet=r;actual_looseness=line_diff;
+ fewest_demerits=total_demerits(r);
+ }
+ else if ((line_diff==actual_looseness)&&@|
+ (total_demerits(r) < fewest_demerits))
+ {@+best_bet=r;fewest_demerits=total_demerits(r);
+ }
+ }
+r=link(r);
+}@+ while (!(r==last_active));
+best_line=line_number(best_bet);
+}
+
+@ Once the best sequence of breakpoints has been found (hurray), we call on the
+procedure |post_line_break| to finish the remainder of the work.
+(By introducing this subprocedure, we are able to keep |line_break|
+from getting extremely long.)
+
+@<Break the paragraph at the chosen...@>=
+post_line_break(final_widow_penalty)
+
+@ The total number of lines that will be set by |post_line_break|
+is |best_line-prev_graf-1|. The last breakpoint is specified by
+|break_node(best_bet)|, and this passive node points to the other breakpoints
+via the |prev_break| links. The finishing-up phase starts by linking the
+relevant passive nodes in forward order, changing |prev_break| to
+|next_break|. (The |next_break| fields actually reside in the same memory
+space as the |prev_break| fields did, but we give them a new name because
+of their new significance.) Then the lines are justified, one by one.
+
+@d next_break prev_break /*new name for |prev_break| after links are reversed*/
+
+@<Declare subprocedures for |line_break|@>=
+static void post_line_break(int @!final_widow_penalty)
+{@+
+pointer q, @!r, @!s; /*temporary registers for list manipulation*/
+bool @!disc_break; /*was the current break at a discretionary node?*/
+bool @!post_disc_break; /*and did it have a nonempty post-break part?*/
+scaled @!cur_width; /*width of line number |cur_line|*/
+scaled @!cur_indent; /*left margin of line number |cur_line|*/
+quarterword @!t; /*used for replacement counts in discretionary nodes*/
+int @!pen; /*use when calculating penalties between lines*/
+halfword @!cur_line; /*the current line number being justified*/
+@<Reverse the links of the relevant passive nodes, setting |cur_p| to the
+first breakpoint@>;
+cur_line=prev_graf+1;
+@/do@+{@<Justify the line ending at breakpoint |cur_p|, and append it to the
+current vertical list, together with associated penalties and other insertions@>;
+incr(cur_line);cur_p=next_break(cur_p);
+if (cur_p!=null) if (!post_disc_break)
+ @<Prune unwanted nodes at the beginning of the next line@>;
+}@+ while (!(cur_p==null));
+if ((cur_line!=best_line)||(link(temp_head)!=null))
+ confusion("line breaking");
+@:this can't happen line breaking}{\quad line breaking@>
+prev_graf=best_line-1;
+}
+
+@ The job of reversing links in a list is conveniently regarded as the job
+of taking items off one stack and putting them on another. In this case we
+take them off a stack pointed to by |q| and having |prev_break| fields;
+we put them on a stack pointed to by |cur_p| and having |next_break| fields.
+Node |r| is the passive node being moved from stack to stack.
+
+@<Reverse the links of the relevant passive nodes...@>=
+q=break_node(best_bet);cur_p=null;
+@/do@+{r=q;q=prev_break(q);next_break(r)=cur_p;cur_p=r;
+}@+ while (!(q==null))
+
+@ Glue and penalty and kern and math nodes are deleted at the beginning of
+a line, except in the anomalous case that the node to be deleted is actually
+one of the chosen breakpoints. Otherwise
+the pruning done here is designed to match
+the lookahead computation in |try_break|, where the |break_width| values
+are computed for non-discretionary breakpoints.
+
+@<Prune unwanted nodes at the beginning of the next line@>=
+{@+r=temp_head;
+loop@+{@+q=link(r);
+ if (q==cur_break(cur_p)) goto done1;
+ /*|cur_break(cur_p)| is the next breakpoint*/
+ /*now |q| cannot be |null|*/
+ if (is_char_node(q)) goto done1;
+ if (non_discardable(q)) goto done1;
+ if (type(q)==kern_node) if (subtype(q)!=explicit) goto done1;
+ r=q; /*now |type(q)==glue_node|, |kern_node|, |math_node|, or |penalty_node|*/
+ }
+done1: if (r!=temp_head)
+ {@+link(r)=null;flush_node_list(link(temp_head));
+ link(temp_head)=q;
+ }
+}
+
+@ The current line to be justified appears in a horizontal list starting
+at |link(temp_head)| and ending at |cur_break(cur_p)|. If |cur_break(cur_p)| is
+a glue node, we reset the glue to equal the |right_skip| glue; otherwise
+we append the |right_skip| glue at the right. If |cur_break(cur_p)| is a
+discretionary node, we modify the list so that the discretionary break
+is compulsory, and we set |disc_break| to |true|. We also append
+the |left_skip| glue at the left of the line, unless it is zero.
+
+@<Justify the line ending at breakpoint |cur_p|, and append it...@>=
+@<Modify the end of the line to reflect the nature of the break and to include
+\.{\\rightskip}; also set the proper value of |disc_break|@>;
+@<Put the \(l)\.{\\leftskip} glue at the left and detach this line@>;
+@<Call the packaging subroutine, setting |just_box| to the justified box@>;
+@<Append the new box to the current vertical list, followed by the list of
+special nodes taken out of the box by the packager@>;
+@<Append a penalty node, if a nonzero penalty is appropriate@>@;
+
+@ At the end of the following code, |q| will point to the final node on the
+list about to be justified.
+
+@<Modify the end of the line...@>=
+q=cur_break(cur_p);disc_break=false;post_disc_break=false;
+if (q!=null) /*|q| cannot be a |char_node|*/
+ if (type(q)==glue_node)
+ {@+delete_glue_ref(glue_ptr(q));
+ glue_ptr(q)=right_skip;
+ subtype(q)=right_skip_code+1;add_glue_ref(right_skip);
+ goto done;
+ }
+ else{@+if (type(q)==disc_node)
+ @<Change discretionary to compulsory and set |disc_break:=true|@>@;
+ else if ((type(q)==math_node)||(type(q)==kern_node)) width(q)=0;
+ }
+else{@+q=temp_head;
+ while (link(q)!=null) q=link(q);
+ }
+@<Put the \(r)\.{\\rightskip} glue after node |q|@>;
+done:
+
+@ @<Change discretionary to compulsory...@>=
+{@+t=replace_count(q);
+@<Destroy the |t| nodes following |q|, and make |r| point to the following
+node@>;
+if (post_break(q)!=null) @<Transplant the post-break list@>;
+if (pre_break(q)!=null) @<Transplant the pre-break list@>;
+link(q)=r;disc_break=true;
+}
+
+@ @<Destroy the |t| nodes following |q|...@>=
+if (t==0) r=link(q);
+else{@+r=q;
+ while (t > 1)
+ {@+r=link(r);decr(t);
+ }
+ s=link(r);
+ r=link(s);link(s)=null;
+ flush_node_list(link(q));replace_count(q)=0;
+ }
+
+@ We move the post-break list from inside node |q| to the main list by
+re\-attaching it just before the present node |r|, then resetting |r|.
+
+@<Transplant the post-break list@>=
+{@+s=post_break(q);
+while (link(s)!=null) s=link(s);
+link(s)=r;r=post_break(q);post_break(q)=null;post_disc_break=true;
+}
+
+@ We move the pre-break list from inside node |q| to the main list by
+re\-attaching it just after the present node |q|, then resetting |q|.
+
+@<Transplant the pre-break list@>=
+{@+s=pre_break(q);link(q)=s;
+while (link(s)!=null) s=link(s);
+pre_break(q)=null;q=s;
+}
+
+@ @<Put the \(r)\.{\\rightskip} glue after node |q|@>=
+r=new_param_glue(right_skip_code);link(r)=link(q);link(q)=r;q=r
+
+@ The following code begins with |q| at the end of the list to be
+justified. It ends with |q| at the beginning of that list, and with
+|link(temp_head)| pointing to the remainder of the paragraph, if any.
+
+@<Put the \(l)\.{\\leftskip} glue at the left...@>=
+r=link(q);link(q)=null;q=link(temp_head);link(temp_head)=r;
+if (left_skip!=zero_glue)
+ {@+r=new_param_glue(left_skip_code);
+ link(r)=q;q=r;
+ }
+
+@ @<Append the new box to the current vertical list...@>=
+append_to_vlist(just_box);
+if (adjust_head!=adjust_tail)
+ {@+link(tail)=link(adjust_head);tail=adjust_tail;
+ }
+adjust_tail=null
+
+@ Now |q| points to the hlist that represents the current line of the
+paragraph. We need to compute the appropriate line width, pack the
+line into a box of this size, and shift the box by the appropriate
+amount of indentation.
+
+@<Call the packaging subroutine...@>=
+if (cur_line > last_special_line)
+ {@+cur_width=second_width;cur_indent=second_indent;
+ }
+else if (par_shape_ptr==null)
+ {@+cur_width=first_width;cur_indent=first_indent;
+ }
+else{@+cur_width=mem[par_shape_ptr+2*cur_line].sc;
+ cur_indent=mem[par_shape_ptr+2*cur_line-1].sc;
+ }
+adjust_tail=adjust_head;just_box=hpack(q, cur_width, exactly);
+shift_amount(just_box)=cur_indent
+
+@ Penalties between the lines of a paragraph come from club and widow lines,
+from the |inter_line_penalty| parameter, and from lines that end at
+discretionary breaks. Breaking between lines of a two-line paragraph gets
+both club-line and widow-line penalties. The local variable |pen| will
+be set to the sum of all relevant penalties for the current line, except
+that the final line is never penalized.
+
+@<Append a penalty node, if a nonzero penalty is appropriate@>=
+if (cur_line+1!=best_line)
+ {@+pen=inter_line_penalty;
+ if (cur_line==prev_graf+1) pen=pen+club_penalty;
+ if (cur_line+2==best_line) pen=pen+final_widow_penalty;
+ if (disc_break) pen=pen+broken_penalty;
+ if (pen!=0)
+ {@+r=new_penalty(pen);
+ link(tail)=r;tail=r;
+ }
+ }
+
+@* Pre-hyphenation.
+When the line-breaking routine is unable to find a feasible sequence of
+breakpoints, it makes a second pass over the paragraph, attempting to
+hyphenate the hyphenatable words. The goal of hyphenation is to insert
+discretionary material into the paragraph so that there are more
+potential places to break.
+
+The general rules for hyphenation are somewhat complex and technical,
+because we want to be able to hyphenate words that are preceded or
+followed by punctuation marks, and because we want the rules to work
+for languages other than English. We also must contend with the fact
+that hyphens might radically alter the ligature and kerning structure
+of a word.
+
+A sequence of characters will be considered for hyphenation only if it
+belongs to a ``potentially hyphenatable part'' of the current paragraph.
+This is a sequence of nodes $p_0p_1\ldots p_m$ where $p_0$ is a glue node,
+$p_1\ldots p_{m-1}$ are either character or ligature or whatsit or
+implicit kern nodes, and $p_m$ is a glue or penalty or insertion or adjust
+or mark or whatsit or explicit kern node. (Therefore hyphenation is
+disabled by boxes, math formulas, and discretionary nodes already inserted
+by the user.) The ligature nodes among $p_1\ldots p_{m-1}$ are effectively
+expanded into the original non-ligature characters; the kern nodes and
+whatsits are ignored. Each character |c| is now classified as either a
+nonletter (if |lc_code(c)==0|), a lowercase letter (if
+|lc_code(c)==c|), or an uppercase letter (otherwise); an uppercase letter
+is treated as if it were |lc_code(c)| for purposes of hyphenation. The
+characters generated by $p_1\ldots p_{m-1}$ may begin with nonletters; let
+$c_1$ be the first letter that is not in the middle of a ligature. Whatsit
+nodes preceding $c_1$ are ignored; a whatsit found after $c_1$ will be the
+terminating node $p_m$. All characters that do not have the same font as
+$c_1$ will be treated as nonletters. The |hyphen_char| for that font
+must be between 0 and 255, otherwise hyphenation will not be attempted.
+\TeX\ looks ahead for as many consecutive letters $c_1\ldots c_n$ as
+possible; however, |n| must be less than 64, so a character that would
+otherwise be $c_{64}$ is effectively not a letter. Furthermore $c_n$ must
+not be in the middle of a ligature. In this way we obtain a string of
+letters $c_1\ldots c_n$ that are generated by nodes $p_a\ldots p_b$, where
+|1 <= a <= b+1 <= m|. If |n >= l_hyf+r_hyf|, this string qualifies for hyphenation;
+however, |uc_hyph| must be positive, if $c_1$ is uppercase.
+
+The hyphenation process takes place in three stages. First, the candidate
+sequence $c_1\ldots c_n$ is found; then potential positions for hyphens
+are determined by referring to hyphenation tables; and finally, the nodes
+$p_a\ldots p_b$ are replaced by a new sequence of nodes that includes the
+discretionary breaks found.
+
+Fortunately, we do not have to do all this calculation very often, because
+of the way it has been taken out of \TeX's inner loop. For example, when
+the second edition of the author's 700-page book {\sl Seminumerical
+Algorithms} was typeset by \TeX, only about 1.2 hyphenations needed to be
+@^Knuth, Donald Ervin@>
+tried per paragraph, since the line breaking algorithm needed to use two
+passes on only about 5 per cent of the paragraphs.
+
+@<Initialize for hyphenating...@>=
+{
+#ifdef @!INIT
+if (trie_not_ready) init_trie();
+#endif
+@;@/
+cur_lang=init_cur_lang;l_hyf=init_l_hyf;r_hyf=init_r_hyf;
+set_hyph_index;
+}
+
+@ The letters $c_1\ldots c_n$ that are candidates for hyphenation are placed
+into an array called |hc|; the number |n| is placed into |hn|; pointers to
+nodes $p_{a-1}$ and~$p_b$ in the description above are placed into variables
+|ha| and |hb|; and the font number is placed into |hf|.
+
+@<Glob...@>=
+static int16_t @!hc[66]; /*word to be hyphenated*/
+static int @!hn; /*the number of positions occupied in |hc|;
+ not always a |small_number|*/
+static pointer @!ha, @!hb; /*nodes |ha dotdot hb| should be replaced by the hyphenated result*/
+static internal_font_number @!hf; /*font number of the letters in |hc|*/
+static int16_t @!hu[64]; /*like |hc|, before conversion to lowercase*/
+static int @!hyf_char; /*hyphen character of the relevant font*/
+static ASCII_code @!cur_lang, @!init_cur_lang; /*current hyphenation table of interest*/
+static int @!l_hyf, @!r_hyf, @!init_l_hyf, @!init_r_hyf; /*limits on fragment sizes*/
+static halfword @!hyf_bchar; /*boundary character after $c_n$*/
+
+@ Hyphenation routines need a few more local variables.
+
+@<Local variables for line...@>=
+small_number @!j; /*an index into |hc| or |hu|*/
+int @!c; /*character being considered for hyphenation*/
+
+@ When the following code is activated, the |line_break| procedure is in its
+second pass, and |cur_p| points to a glue node.
+
+@<Try to hyphenate...@>=
+{@+prev_s=cur_p;s=link(prev_s);
+if (s!=null)
+ {@+@<Skip to node |ha|, or |goto done1| if no hyphenation should be attempted@>;
+ if (l_hyf+r_hyf > 63) goto done1;
+ @<Skip to node |hb|, putting letters into |hu| and |hc|@>;
+ @<Check that the nodes following |hb| permit hyphenation and that at least
+|l_hyf+r_hyf| letters have been found, otherwise |goto done1|@>;
+ hyphenate();
+ }
+done1: ;}
+
+@ @<Declare subprocedures for |line_break|@>=
+@t\4@>@<Declare the function called |reconstitute|@>@;
+static void hyphenate(void)
+{@+
+@<Local variables for hyphenation@>@;
+@<Find hyphen locations for the word in |hc|, or |return|@>;
+@<If no hyphens were found, |return|@>;
+@<Replace nodes |ha..hb| by a sequence of nodes that includes the discretionary
+hyphens@>;
+}
+
+@ The first thing we need to do is find the node |ha| just before the
+first letter.
+
+@<Skip to node |ha|, or |goto done1|...@>=
+loop@+{@+if (is_char_node(s))
+ {@+c=qo(character(s));hf=font(s);
+ }
+ else if (type(s)==ligature_node)
+ if (lig_ptr(s)==null) goto resume;
+ else{@+q=lig_ptr(s);c=qo(character(q));hf=font(q);
+ }
+ else if ((type(s)==kern_node)&&(subtype(s)==normal)) goto resume;
+ else if (type(s)==whatsit_node)
+ {@+@<Advance \(p)past a whatsit node in the \(p)pre-hyphenation loop@>;
+ goto resume;
+ }
+ else goto done1;
+ set_lc_code(c);
+ if (hc[0]!=0)
+ if ((hc[0]==c)||(uc_hyph > 0)) goto done2;
+ else goto done1;
+resume: prev_s=s;s=link(prev_s);
+ }
+done2: hyf_char=hyphen_char[hf];
+if (hyf_char < 0) goto done1;
+if (hyf_char > 255) goto done1;
+ha=prev_s
+
+@ The word to be hyphenated is now moved to the |hu| and |hc| arrays.
+
+@<Skip to node |hb|, putting letters...@>=
+hn=0;
+loop@+{@+if (is_char_node(s))
+ {@+if (font(s)!=hf) goto done3;
+ hyf_bchar=character(s);c=qo(hyf_bchar);
+ set_lc_code(c);
+ if (hc[0]==0) goto done3;
+ if (hn==63) goto done3;
+ hb=s;incr(hn);hu[hn]=c;hc[hn]=hc[0];hyf_bchar=non_char;
+ }
+ else if (type(s)==ligature_node)
+ @<Move the characters of a ligature node to |hu| and |hc|; but |goto done3|
+if they are not all letters@>@;
+ else if ((type(s)==kern_node)&&(subtype(s)==normal))
+ {@+hb=s;
+ hyf_bchar=font_bchar[hf];
+ }
+ else goto done3;
+ s=link(s);
+ }
+done3:
+
+@ We let |j| be the index of the character being stored when a ligature node
+is being expanded, since we do not want to advance |hn| until we are sure
+that the entire ligature consists of letters. Note that it is possible
+to get to |done3| with |hn==0| and |hb| not set to any value.
+
+@<Move the characters of a ligature node to |hu| and |hc|...@>=
+{@+if (font(lig_char(s))!=hf) goto done3;
+j=hn;q=lig_ptr(s);@+if (q > null) hyf_bchar=character(q);
+while (q > null)
+ {@+c=qo(character(q));
+ set_lc_code(c);
+ if (hc[0]==0) goto done3;
+ if (j==63) goto done3;
+ incr(j);hu[j]=c;hc[j]=hc[0];@/
+ q=link(q);
+ }
+hb=s;hn=j;
+if (odd(subtype(s))) hyf_bchar=font_bchar[hf];@+else hyf_bchar=non_char;
+}
+
+@ @<Check that the nodes following |hb| permit hyphenation...@>=
+if (hn < l_hyf+r_hyf) goto done1; /*|l_hyf| and |r_hyf| are | >= 1|*/
+loop@+{@+if (!(is_char_node(s)))
+ switch (type(s)) {
+ case ligature_node: do_nothing;@+break;
+ case kern_node: if (subtype(s)!=normal) goto done4;@+break;
+ case whatsit_node: case glue_node:
+ case penalty_node: case ins_node: case adjust_node:
+ case mark_node:
+ goto done4;
+ default:goto done1;
+ }
+ s=link(s);
+ }
+done4:
+
+@* Post-hyphenation.
+If a hyphen may be inserted between |hc[j]| and |hc[j+1]|, the hyphenation
+procedure will set |hyf[j]| to some small odd number. But before we look
+at \TeX's hyphenation procedure, which is independent of the rest of the
+line-breaking algorithm, let us consider what we will do with the hyphens
+it finds, since it is better to work on this part of the program before
+forgetting what |ha| and |hb|, etc., are all about.
+
+@<Glob...@>=
+static int8_t @!hyf[65]; /*odd values indicate discretionary hyphens*/
+static pointer @!init_list; /*list of punctuation characters preceding the word*/
+static bool @!init_lig; /*does |init_list| represent a ligature?*/
+static bool @!init_lft; /*if so, did the ligature involve a left boundary?*/
+
+@ @<Local variables for hyphenation@>=
+int @!i, @!j, @!l; /*indices into |hc| or |hu|*/
+pointer @!q, @!r, @!s; /*temporary registers for list manipulation*/
+halfword @!bchar; /*boundary character of hyphenated word, or |non_char|*/
+
+@ \TeX\ will never insert a hyphen that has fewer than
+\.{\\lefthyphenmin} letters before it or fewer than
+\.{\\righthyphenmin} after it; hence, a short word has
+comparatively little chance of being hyphenated. If no hyphens have
+been found, we can save time by not having to make any changes to the
+paragraph.
+
+@<If no hyphens were found, |return|@>=
+for (j=l_hyf; j<=hn-r_hyf; j++) if (odd(hyf[j])) goto found1;
+return;
+found1:
+
+@ If hyphens are in fact going to be inserted, \TeX\ first deletes the
+subsequence of nodes between |ha| and~|hb|. An attempt is made to
+preserve the effect that implicit boundary characters and punctuation marks
+had on ligatures inside the hyphenated word, by storing a left boundary or
+preceding character in |hu[0]| and by storing a possible right boundary
+in |bchar|. We set |j=0| if |hu[0]| is to be part of the reconstruction;
+otherwise |j=1|.
+The variable |s| will point to the tail of the current hlist, and
+|q| will point to the node following |hb|, so that
+things can be hooked up after we reconstitute the hyphenated word.
+
+@<Replace nodes |ha..hb| by a sequence of nodes...@>=
+q=link(hb);link(hb)=null;r=link(ha);link(ha)=null;bchar=hyf_bchar;
+if (is_char_node(ha))
+ if (font(ha)!=hf) goto found2;
+ else{@+init_list=ha;init_lig=false;hu[0]=qo(character(ha));
+ }
+else if (type(ha)==ligature_node)
+ if (font(lig_char(ha))!=hf) goto found2;
+ else{@+init_list=lig_ptr(ha);init_lig=true;init_lft=(subtype(ha) > 1);
+ hu[0]=qo(character(lig_char(ha)));
+ if (init_list==null) if (init_lft)
+ {@+hu[0]=256;init_lig=false;
+ } /*in this case a ligature will be reconstructed from scratch*/
+ free_node(ha, small_node_size);
+ }
+else{@+ /*no punctuation found; look for left boundary*/
+ if (!is_char_node(r)) if (type(r)==ligature_node)
+ if (subtype(r) > 1) goto found2;
+ j=1;s=ha;init_list=null;goto common_ending;
+ }
+s=cur_p; /*we have |cur_p!=ha| because |type(cur_p)==glue_node|*/
+while (link(s)!=ha) s=link(s);
+j=0;goto common_ending;
+found2: s=ha;j=0;hu[0]=256;init_lig=false;init_list=null;
+common_ending: flush_node_list(r);
+@<Reconstitute nodes for the hyphenated word, inserting discretionary hyphens@>;
+flush_list(init_list)
+
+@ We must now face the fact that the battle is not over, even though the
+{\def\!{\kern-1pt}%
+hyphens have been found: The process of reconstituting a word can be nontrivial
+because ligatures might change when a hyphen is present. {\sl The \TeX book\/}
+discusses the difficulties of the word ``difficult'', and
+the discretionary material surrounding a
+hyphen can be considerably more complex than that. Suppose
+\.{abcdef} is a word in a font for which the only ligatures are \.{b\!c},
+\.{c\!d}, \.{d\!e}, and \.{e\!f}. If this word permits hyphenation
+between \.b and \.c, the two patterns with and without hyphenation are
+$\.a\,\.b\,\.-\,\.{c\!d}\,\.{e\!f}$ and $\.a\,\.{b\!c}\,\.{d\!e}\,\.f$.
+Thus the insertion of a hyphen might cause effects to ripple arbitrarily
+far into the rest of the word. A further complication arises if additional
+hyphens appear together with such rippling, e.g., if the word in the
+example just given could also be hyphenated between \.c and \.d; \TeX\
+avoids this by simply ignoring the additional hyphens in such weird cases.}
+
+Still further complications arise in the presence of ligatures that do not
+delete the original characters. When punctuation precedes the word being
+hyphenated, \TeX's method is not perfect under all possible scenarios,
+because punctuation marks and letters can propagate information back and forth.
+For example, suppose the original pre-hyphenation pair
+\.{*a} changes to \.{*y} via a \.{\@@=:} ligature, which changes to \.{xy}
+via a \.{=:\@@} ligature; if $p_{a-1}=\.x$ and $p_a=\.y$, the reconstitution
+procedure isn't smart enough to obtain \.{xy} again. In such cases the
+font designer should include a ligature that goes from \.{xa} to \.{xy}.
+
+@ The processing is facilitated by a subroutine called |reconstitute|. Given
+a string of characters $x_j\ldots x_n$, there is a smallest index $m\ge j$
+such that the ``translation'' of $x_j\ldots x_n$ by ligatures and kerning
+has the form $y_1\ldots y_t$ followed by the translation of $x_{m+1}\ldots x_n$,
+where $y_1\ldots y_t$ is some nonempty sequence of character, ligature, and
+kern nodes. We call $x_j\ldots x_m$ a ``cut prefix'' of $x_j\ldots x_n$.
+For example, if $x_1x_2x_3=\.{fly}$, and if the font contains `fl' as a
+ligature and a kern between `fl' and `y', then $m=2$, $t=2$, and $y_1$ will
+be a ligature node for `fl' followed by an appropriate kern node~$y_2$.
+In the most common case, $x_j$~forms no ligature with $x_{j+1}$ and we
+simply have $m=j$, $y_1=x_j$. If $m<n$ we can repeat the procedure on
+$x_{m+1}\ldots x_n$ until the entire translation has been found.
+
+The |reconstitute| function returns the integer $m$ and puts the nodes
+$y_1\ldots y_t$ into a linked list starting at |link(hold_head)|,
+getting the input $x_j\ldots x_n$ from the |hu| array. If $x_j=256$,
+we consider $x_j$ to be an implicit left boundary character; in this
+case |j| must be strictly less than~|n|. There is a
+parameter |bchar|, which is either 256 or an implicit right boundary character
+assumed to be present just following~$x_n$. (The value |hu[n+1]| is never
+explicitly examined, but the algorithm imagines that |bchar| is there.)
+
+If there exists an index |k| in the range $j\le k\le m$ such that |hyf[k]|
+is odd and such that the result of |reconstitute| would have been different
+if $x_{k+1}$ had been |hchar|, then |reconstitute| sets |hyphen_passed|
+to the smallest such~|k|. Otherwise it sets |hyphen_passed| to zero.
+
+A special convention is used in the case |j==0|: Then we assume that the
+translation of |hu[0]| appears in a special list of charnodes starting at
+|init_list|; moreover, if |init_lig| is |true|, then |hu[0]| will be
+a ligature character, involving a left boundary if |init_lft| is |true|.
+This facility is provided for cases when a hyphenated
+word is preceded by punctuation (like single or double quotes) that might
+affect the translation of the beginning of the word.
+
+@<Glob...@>=
+static small_number @!hyphen_passed; /*first hyphen in a ligature, if any*/
+
+@ @<Declare the function called |reconstitute|@>=
+static
+ small_number reconstitute(small_number @!j, small_number @!n, halfword @!bchar, halfword @!hchar)
+{@+
+pointer @!p; /*temporary register for list manipulation*/
+pointer @!t; /*a node being appended to*/
+four_quarters @!q; /*character information or a lig/kern instruction*/
+halfword @!cur_rh; /*hyphen character for ligature testing*/
+halfword @!test_char; /*hyphen or other character for ligature testing*/
+scaled @!w; /*amount of kerning*/
+font_index @!k; /*position of current lig/kern instruction*/
+hyphen_passed=0;t=hold_head;w=0;link(hold_head)=null;
+ /*at this point |ligature_present==lft_hit==rt_hit==false|*/
+@<Set up data structures with the cursor following position |j|@>;
+resume: @<If there's a ligature or kern at the cursor position, update the
+data structures, possibly advancing~|j|; continue until the cursor moves@>;
+@<Append a ligature and/or kern to the translation; |goto resume| if the
+stack of inserted ligatures is nonempty@>;
+return j;
+}
+
+@ The reconstitution procedure shares many of the global data structures
+by which \TeX\ has processed the words before they were hyphenated.
+There is an implied ``cursor'' between characters |cur_l| and |cur_r|;
+these characters will be tested for possible ligature activity. If
+|ligature_present| then |cur_l| is a ligature character formed from the
+original characters following |cur_q| in the current translation list.
+There is a ``ligature stack'' between the cursor and character |j+1|,
+consisting of pseudo-ligature nodes linked together by their |link| fields.
+This stack is normally empty unless a ligature command has created a new
+character that will need to be processed later. A pseudo-ligature is
+a special node having a |character| field that represents a potential
+ligature and a |lig_ptr| field that points to a |char_node| or is |null|.
+We have
+$$|cur_r|=\cases{|character(lig_stack)|,&if |lig_stack > null|;\cr
+ \hbox{|qi(hu[j+1])|},&if |lig_stack==null| and |j < n|;\cr
+ bchar,&if |lig_stack==null| and |j==n|.\cr}$$
+
+@<Glob...@>=
+static halfword @!cur_l, @!cur_r; /*characters before and after the cursor*/
+static pointer @!cur_q; /*where a ligature should be detached*/
+static pointer @!lig_stack; /*unfinished business to the right of the cursor*/
+static bool @!ligature_present; /*should a ligature node be made for |cur_l|?*/
+static bool @!lft_hit, @!rt_hit; /*did we hit a ligature with a boundary character?*/
+
+@ @d append_charnode_to_t(A) {@+link(t)=get_avail();t=link(t);
+ font(t)=hf;character(t)=A;
+ }
+@d set_cur_r {@+if (j < n) cur_r=qi(hu[j+1]);@+else cur_r=bchar;
+ if (odd(hyf[j])) cur_rh=hchar;@+else cur_rh=non_char;
+ }
+
+@<Set up data structures with the cursor following position |j|@>=
+cur_l=qi(hu[j]);cur_q=t;
+if (j==0)
+ {@+ligature_present=init_lig;p=init_list;
+ if (ligature_present) lft_hit=init_lft;
+ while (p > null)
+ {@+append_charnode_to_t(character(p));p=link(p);
+ }
+ }
+else if (cur_l < non_char) append_charnode_to_t(cur_l);
+lig_stack=null;set_cur_r
+
+@ We may want to look at the lig/kern program twice, once for a hyphen
+and once for a normal letter. (The hyphen might appear after the letter
+in the program, so we'd better not try to look for both at once.)
+
+@<If there's a ligature or kern at the cursor position, update...@>=
+if (cur_l==non_char)
+ {@+k=bchar_label[hf];
+ if (k==non_address) goto done;@+else q=font_info[k].qqqq;
+ }
+else{@+q=char_info(hf, cur_l);
+ if (char_tag(q)!=lig_tag) goto done;
+ k=lig_kern_start(hf, q);q=font_info[k].qqqq;
+ if (skip_byte(q) > stop_flag)
+ {@+k=lig_kern_restart(hf, q);q=font_info[k].qqqq;
+ }
+ } /*now |k| is the starting address of the lig/kern program*/
+if (cur_rh < non_char) test_char=cur_rh;@+else test_char=cur_r;
+loop@+{@+if (next_char(q)==test_char) if (skip_byte(q) <= stop_flag)
+ if (cur_rh < non_char)
+ {@+hyphen_passed=j;hchar=non_char;cur_rh=non_char;
+ goto resume;
+ }
+ else{@+if (hchar < non_char) if (odd(hyf[j]))
+ {@+hyphen_passed=j;hchar=non_char;
+ }
+ if (op_byte(q) < kern_flag)
+ @<Carry out a ligature replacement, updating the cursor structure and
+possibly advancing~|j|; |goto resume| if the cursor doesn't advance, otherwise
+|goto done|@>;
+ w=char_kern(hf, q);goto done; /*this kern will be inserted below*/
+ }
+ if (skip_byte(q) >= stop_flag)
+ if (cur_rh==non_char) goto done;
+ else{@+cur_rh=non_char;goto resume;
+ }
+ k=k+qo(skip_byte(q))+1;q=font_info[k].qqqq;
+ }
+done:
+
+@ @d wrap_lig(A) if (ligature_present)
+ {@+p=new_ligature(hf, cur_l, link(cur_q));
+ if (lft_hit)
+ {@+subtype(p)=2;lft_hit=false;
+ }
+ if (A) if (lig_stack==null)
+ {@+incr(subtype(p));rt_hit=false;
+ }
+ link(cur_q)=p;t=p;ligature_present=false;
+ }
+@d pop_lig_stack {@+if (lig_ptr(lig_stack) > null)
+ {@+link(t)=lig_ptr(lig_stack); /*this is a charnode for |hu[j+1]|*/
+ t=link(t);incr(j);
+ }
+ p=lig_stack;lig_stack=link(p);free_node(p, small_node_size);
+ if (lig_stack==null) set_cur_r@;@+else cur_r=character(lig_stack);
+ } /*if |lig_stack| isn't |null| we have |cur_rh==non_char|*/
+
+@<Append a ligature and/or kern to the translation...@>=
+wrap_lig(rt_hit);
+if (w!=0)
+ {@+link(t)=new_kern(w);t=link(t);w=0;
+ }
+if (lig_stack > null)
+ {@+cur_q=t;cur_l=character(lig_stack);ligature_present=true;
+ pop_lig_stack;goto resume;
+ }
+
+@ @<Carry out a ligature replacement, updating the cursor structure...@>=
+{@+if (cur_l==non_char) lft_hit=true;
+if (j==n) if (lig_stack==null) rt_hit=true;
+check_interrupt; /*allow a way out in case there's an infinite ligature loop*/
+switch (op_byte(q)) {
+case qi(1): case qi(5): {@+cur_l=rem_byte(q); /*\.{=:\@@}, \.{=:\@@>}*/
+ ligature_present=true;
+ } @+break;
+case qi(2): case qi(6): {@+cur_r=rem_byte(q); /*\.{\@@=:}, \.{\@@=:>}*/
+ if (lig_stack > null) character(lig_stack)=cur_r;
+ else{@+lig_stack=new_lig_item(cur_r);
+ if (j==n) bchar=non_char;
+ else{@+p=get_avail();lig_ptr(lig_stack)=p;
+ character(p)=qi(hu[j+1]);font(p)=hf;
+ }
+ }
+ } @+break;
+case qi(3): {@+cur_r=rem_byte(q); /*\.{\@@=:\@@}*/
+ p=lig_stack;lig_stack=new_lig_item(cur_r);link(lig_stack)=p;
+ } @+break;
+case qi(7): case qi(11): {@+wrap_lig(false); /*\.{\@@=:\@@>}, \.{\@@=:\@@>>}*/
+ cur_q=t;cur_l=rem_byte(q);ligature_present=true;
+ } @+break;
+default:{@+cur_l=rem_byte(q);ligature_present=true; /*\.{=:}*/
+ if (lig_stack > null) pop_lig_stack@;
+ else if (j==n) goto done;
+ else{@+append_charnode_to_t(cur_r);incr(j);set_cur_r;
+ }
+ }
+}
+if (op_byte(q) > qi(4)) if (op_byte(q)!=qi(7)) goto done;
+goto resume;
+}
+
+@ Okay, we're ready to insert the potential hyphenations that were found.
+When the following program is executed, we want to append the word
+|hu[1 dotdot hn]| after node |ha|, and node |q| should be appended to the result.
+During this process, the variable |i| will be a temporary
+index into |hu|; the variable |j| will be an index to our current position
+in |hu|; the variable |l| will be the counterpart of |j|, in a discretionary
+branch; the variable |r| will point to new nodes being created; and
+we need a few new local variables:
+
+@<Local variables for hyph...@>=
+pointer @!major_tail, @!minor_tail; /*the end of lists in the main and
+ discretionary branches being reconstructed*/
+ASCII_code @!c; /*character temporarily replaced by a hyphen*/
+int @!c_loc; /*where that character came from*/
+int @!r_count; /*replacement count for discretionary*/
+pointer @!hyf_node; /*the hyphen, if it exists*/
+
+@ When the following code is performed, |hyf[0]| and |hyf[hn]| will be zero.
+
+@<Reconstitute nodes for the hyphenated word...@>=
+@/do@+{l=j;j=reconstitute(j, hn, bchar, qi(hyf_char))+1;
+if (hyphen_passed==0)
+ {@+link(s)=link(hold_head);
+ while (link(s) > null) s=link(s);
+ if (odd(hyf[j-1]))
+ {@+l=j;hyphen_passed=j-1;link(hold_head)=null;
+ }
+ }
+if (hyphen_passed > 0)
+ @<Create and append a discretionary node as an alternative to the unhyphenated
+word, and continue to develop both branches until they become equivalent@>;
+}@+ while (!(j > hn));
+link(s)=q
+
+@ In this repeat loop we will insert another discretionary if |hyf[j-1]| is
+odd, when both branches of the previous discretionary end at position |j-1|.
+Strictly speaking, we aren't justified in doing this, because we don't know
+that a hyphen after |j-1| is truly independent of those branches. But in almost
+all applications we would rather not lose a potentially valuable hyphenation
+point. (Consider the word `difficult', where the letter `c' is in position |j|.)
+
+@d advance_major_tail {@+major_tail=link(major_tail);incr(r_count);
+ }
+
+@<Create and append a discretionary node as an alternative...@>=
+@/do@+{r=get_node(small_node_size);
+link(r)=link(hold_head);type(r)=disc_node;
+major_tail=r;r_count=0;
+while (link(major_tail) > null) advance_major_tail;
+i=hyphen_passed;hyf[i]=0;
+@<Put the \(c)characters |hu[l..i]| and a hyphen into |pre_break(r)|@>;
+@<Put the \(c)characters |hu[i+1..]| into |post_break(r)|, appending to this
+list and to |major_tail| until synchronization has been achieved@>;
+@<Move pointer |s| to the end of the current list, and set |replace_count(r)|
+appropriately@>;
+hyphen_passed=j-1;link(hold_head)=null;
+}@+ while (!(!odd(hyf[j-1])))
+
+@ The new hyphen might combine with the previous character via ligature
+or kern. At this point we have |l-1 <= i < j| and |i < hn|.
+
+@<Put the \(c)characters |hu[l..i]| and a hyphen into |pre_break(r)|@>=
+minor_tail=null;pre_break(r)=null;hyf_node=new_character(hf, hyf_char);
+if (hyf_node!=null)
+ {@+incr(i);c=hu[i];hu[i]=hyf_char;free_avail(hyf_node);
+ }
+while (l <= i)
+ {@+l=reconstitute(l, i, font_bchar[hf], non_char)+1;
+ if (link(hold_head) > null)
+ {@+if (minor_tail==null) pre_break(r)=link(hold_head);
+ else link(minor_tail)=link(hold_head);
+ minor_tail=link(hold_head);
+ while (link(minor_tail) > null) minor_tail=link(minor_tail);
+ }
+ }
+if (hyf_node!=null)
+ {@+hu[i]=c; /*restore the character in the hyphen position*/
+ l=i;decr(i);
+ }
+
+@ The synchronization algorithm begins with |l==i+1 <= j|.
+
+@<Put the \(c)characters |hu[i+1..]| into |post_break(r)|...@>=
+minor_tail=null;post_break(r)=null;c_loc=0;
+if (bchar_label[hf]!=non_address) /*put left boundary at beginning of new line*/
+ {@+decr(l);c=hu[l];c_loc=l;hu[l]=256;
+ }
+while (l < j)
+ {@+@/do@+{l=reconstitute(l, hn, bchar, non_char)+1;
+ if (c_loc > 0)
+ {@+hu[c_loc]=c;c_loc=0;
+ }
+ if (link(hold_head) > null)
+ {@+if (minor_tail==null) post_break(r)=link(hold_head);
+ else link(minor_tail)=link(hold_head);
+ minor_tail=link(hold_head);
+ while (link(minor_tail) > null) minor_tail=link(minor_tail);
+ }
+ }@+ while (!(l >= j));
+ while (l > j)
+ @<Append characters of |hu[j..]| to |major_tail|, advancing~|j|@>;
+ }
+
+@ @<Append characters of |hu[j..]|...@>=
+{@+j=reconstitute(j, hn, bchar, non_char)+1;
+link(major_tail)=link(hold_head);
+while (link(major_tail) > null) advance_major_tail;
+}
+
+@ Ligature insertion can cause a word to grow exponentially in size. Therefore
+we must test the size of |r_count| here, even though the hyphenated text
+was at most 63 characters long.
+
+@<Move pointer |s| to the end of the current list...@>=
+if (r_count > 127) /*we have to forget the discretionary hyphen*/
+ {@+link(s)=link(r);link(r)=null;flush_node_list(r);
+ }
+else{@+link(s)=r;replace_count(r)=r_count;
+ }
+s=major_tail
+
+@* Hyphenation.
+When a word |hc[1 dotdot hn]| has been set up to contain a candidate for hyphenation,
+\TeX\ first looks to see if it is in the user's exception dictionary. If not,
+hyphens are inserted based on patterns that appear within the given word,
+using an algorithm due to Frank~M. Liang.
+@^Liang, Franklin Mark@>
+
+Let's consider Liang's method first, since it is much more interesting than the
+exception-lookup routine. The algorithm begins by setting |hyf[j]| to zero
+for all |j|, and invalid characters are inserted into |hc[0]|
+and |hc[hn+1]| to serve as delimiters. Then a reasonably fast method is
+used to see which of a given set of patterns occurs in the word
+|hc[0 dotdot(hn+1)]|. Each pattern $p_1\ldots p_k$ of length |k| has an associated
+sequence of |k+1| numbers $n_0\ldots n_k$; and if the pattern occurs in
+|hc[(j+1)dotdot(j+k)]|, \TeX\ will set |hyf[j+i]=@tmax@>(hyf[j+i],@t$n\_i$@>)| for
+|0 <= i <= k|. After this has been done for each pattern that occurs, a
+discretionary hyphen will be inserted between |hc[j]| and |hc[j+1]| when
+|hyf[j]| is odd, as we have already seen.
+
+The set of patterns $p_1\ldots p_k$ and associated numbers $n_0\ldots n_k$
+depends, of course, on the language whose words are being hyphenated, and
+on the degree of hyphenation that is desired. A method for finding
+appropriate |p|'s and |n|'s, from a given dictionary of words and acceptable
+hyphenations, is discussed in Liang's Ph.D. thesis (Stanford University,
+1983); \TeX\ simply starts with the patterns and works from there.
+
+@ The patterns are stored in a compact table that is also efficient for
+retrieval, using a variant of ``trie memory'' [cf.\ {\sl The Art of
+Computer Programming \bf3} (1973), 481--505]. We can find each pattern
+$p_1\ldots p_k$ by letting $z_0$ be one greater than the relevant language
+index and then, for |1 <= i <= k|,
+setting |@t$z\_i$@>=trie_link@t$(z\_{i-1})+p\_i$@>|; the pattern will be
+identified by the number $z_k$. Since all the pattern information is
+packed together into a single |trie_link| array, it is necessary to
+prevent confusion between the data from inequivalent patterns, so another
+table is provided such that |trie_char@t$(z\_i)=p\_i$@>| for all |i|. There
+is also a table |trie_op|$(z_k)$ to identify the numbers $n_0\ldots n_k$
+associated with $p_1\ldots p_k$.
+
+Comparatively few different number sequences $n_0\ldots n_k$ actually occur,
+since most of the |n|'s are generally zero. Therefore the number sequences
+are encoded in such a way that |trie_op|$(z_k)$ is only one byte long.
+If |trie_op(@t$z\_k$@>)!=min_quarterword|, when $p_1\ldots p_k$ has matched
+the letters in |hc[(l-k+1)dotdot l@,]| of language |t|,
+we perform all of the required operations
+for this pattern by carrying out the following little program: Set
+|v=trie_op(@t$z\_k$@>)|. Then set |v=v+op_start[t]|,
+|hyf[l-hyf_distance[v]]=@tmax@>(hyf[l-hyf_distance[v]], hyf_num[v])|,
+and |v=hyf_next[v]|; repeat, if necessary, until |v==min_quarterword|.
+
+@<Types...@>=
+typedef int32_t trie_pointer; /*an index into |trie|*/
+
+@ @d trie_link(A) trie[A].rh /*``downward'' link in a trie*/
+@d trie_char(A) trie[A].b1 /*character matched at this trie location*/
+@d trie_op(A) trie[A].b0 /*program for hyphenation at this trie location*/
+
+@<Glob...@>=
+static two_halves @!trie[trie_size+1]; /*|trie_link|, |trie_char|, |trie_op|*/
+static small_number @!hyf_distance0[trie_op_size],
+ *const @!hyf_distance = @!hyf_distance0-1; /*position |k-j| of $n_j$*/
+static small_number @!hyf_num0[trie_op_size],
+ *const @!hyf_num = @!hyf_num0-1; /*value of $n_j$*/
+static quarterword @!hyf_next0[trie_op_size],
+ *const @!hyf_next = @!hyf_next0-1; /*continuation code*/
+static uint16_t @!op_start[256]; /*offset for current language*/
+
+@ @<Local variables for hyph...@>=
+trie_pointer @!z; /*an index into |trie|*/
+int @!v; /*an index into |hyf_distance|, etc.*/
+
+@ Assuming that these auxiliary tables have been set up properly, the
+hyphenation algorithm is quite short. In the following code we set |hc[hn+2]|
+to the impossible value 256, in order to guarantee that |hc[hn+3]| will
+never be fetched.
+
+@<Find hyphen locations for the word in |hc|...@>=
+for (j=0; j<=hn; j++) hyf[j]=0;
+@<Look for the word |hc[1..hn]| in the exception table, and |goto found| (with
+|hyf| containing the hyphens) if an entry is found@>;
+if (trie_char(cur_lang+1)!=qi(cur_lang)) return; /*no patterns for |cur_lang|*/
+hc[0]=0;hc[hn+1]=0;hc[hn+2]=256; /*insert delimiters*/
+for (j=0; j<=hn-r_hyf+1; j++)
+ {@+z=trie_link(cur_lang+1)+hc[j];l=j;
+ while (hc[l]==qo(trie_char(z)))
+ {@+if (trie_op(z)!=min_quarterword)
+ @<Store \(m)maximum values in the |hyf| table@>;
+ incr(l);z=trie_link(z)+hc[l];
+ }
+ }
+found: for (j=0; j<=l_hyf-1; j++) hyf[j]=0;
+for (j=0; j<=r_hyf-1; j++) hyf[hn-j]=0
+
+@ @<Store \(m)maximum values in the |hyf| table@>=
+{@+v=trie_op(z);
+@/do@+{v=v+op_start[cur_lang];i=l-hyf_distance[v];
+if (hyf_num[v] > hyf[i]) hyf[i]=hyf_num[v];
+v=hyf_next[v];
+}@+ while (!(v==min_quarterword));
+}
+
+@ The exception table that is built by \TeX's \.{\\hyphenation} primitive is
+organized as an ordered hash table [cf.\ Amble and Knuth, {\sl The Computer
+@^Amble, Ole@> @^Knuth, Donald Ervin@>
+Journal\/ \bf17} (1974), 135--142] using linear probing. If $\alpha$ and
+$\beta$ are words, we will say that $\alpha<\beta$ if $\vert\alpha\vert<
+\vert\beta\vert$ or if $\vert\alpha\vert=\vert\beta\vert$ and
+$\alpha$ is lexicographically smaller than $\beta$. (The notation $\vert
+\alpha\vert$ stands for the length of $\alpha$.) The idea of ordered hashing
+is to arrange the table so that a given word $\alpha$ can be sought by computing
+a hash address $h=h(\alpha)$ and then looking in table positions |h|, |h-1|,
+\dots, until encountering the first word $\le\alpha$. If this word is
+different from $\alpha$, we can conclude that $\alpha$ is not in the table.
+
+The words in the table point to lists in |mem| that specify hyphen positions
+in their |info| fields. The list for $c_1\ldots c_n$ contains the number |k| if
+the word $c_1\ldots c_n$ has a discretionary hyphen between $c_k$ and
+$c_{k+1}$.
+
+@<Types...@>=
+typedef int16_t hyph_pointer; /*an index into the ordered hash table*/
+
+@ @<Glob...@>=
+static str_number @!hyph_word[hyph_size+1]; /*exception words*/
+static pointer @!hyph_list[hyph_size+1]; /*lists of hyphen positions*/
+static hyph_pointer @!hyph_count; /*the number of words in the exception dictionary*/
+
+@ @<Local variables for init...@>=
+int @!z; /*runs through the exception dictionary*/
+
+@ @<Set init...@>=
+for (z=0; z<=hyph_size; z++)
+ {@+hyph_word[z]=0;hyph_list[z]=null;
+ }
+hyph_count=0;
+
+@ The algorithm for exception lookup is quite simple, as soon as we have
+a few more local variables to work with.
+
+@<Local variables for hyph...@>=
+hyph_pointer @!h; /*an index into |hyph_word| and |hyph_list|*/
+str_number @!k; /*an index into |str_start|*/
+pool_pointer @!u; /*an index into |str_pool|*/
+
+@ First we compute the hash code |h|, then we search until we either
+find the word or we don't. Words from different languages are kept
+separate by appending the language code to the string.
+
+@<Look for the word |hc[1...@>=
+h=hc[1];incr(hn);hc[hn]=cur_lang;
+for (j=2; j<=hn; j++) h=(h+h+hc[j])%hyph_size;
+loop@+{@+@<If the string |hyph_word[h]| is less than \(hc)|hc[1..hn]|, |goto
+not_found|; but if the two strings are equal, set |hyf| to the hyphen positions
+and |goto found|@>;
+ if (h > 0) decr(h);@+else h=hyph_size;
+ }
+not_found: decr(hn)
+
+@ @<If the string |hyph_word[h]| is less than \(hc)...@>=
+k=hyph_word[h];if (k==0) goto not_found;
+if (length(k) < hn) goto not_found;
+if (length(k)==hn)
+ {@+j=1;u=str_start[k];
+ @/do@+{if (so(str_pool[u]) < hc[j]) goto not_found;
+ if (so(str_pool[u]) > hc[j]) goto done;
+ incr(j);incr(u);
+ }@+ while (!(j > hn));
+ @<Insert hyphens as specified in |hyph_list[h]|@>;
+ decr(hn);goto found;
+ }
+done:
+
+@ @<Insert hyphens as specified...@>=
+s=hyph_list[h];
+while (s!=null)
+ {@+hyf[info(s)]=1;s=link(s);
+ }
+
+@ @<Search |hyph_list| for pointers to |p|@>=
+for (q=0; q<=hyph_size; q++)
+ {@+if (hyph_list[q]==p)
+ {@+print_nl("HYPH(");print_int(q);print_char(')');
+ }
+ }
+
+@ We have now completed the hyphenation routine, so the |line_break| procedure
+is finished at last. Since the hyphenation exception table is fresh in our
+minds, it's a good time to deal with the routine that adds new entries to it.
+
+When \TeX\ has scanned `\.{\\hyphenation}', it calls on a procedure named
+|new_hyph_exceptions| to do the right thing.
+
+@d set_cur_lang if (language <= 0) cur_lang=0;
+ else if (language > 255) cur_lang=0;
+ else cur_lang=language
+
+@p static void new_hyph_exceptions(void) /*enters new exceptions*/
+{@+
+int n; /*length of current word; not always a |small_number|*/
+int @!j; /*an index into |hc|*/
+hyph_pointer @!h; /*an index into |hyph_word| and |hyph_list|*/
+str_number @!k; /*an index into |str_start|*/
+pointer @!p; /*head of a list of hyphen positions*/
+pointer @!q; /*used when creating a new node for list |p|*/
+str_number @!s, @!t; /*strings being compared or stored*/
+pool_pointer @!u, @!v; /*indices into |str_pool|*/
+scan_left_brace(); /*a left brace must follow \.{\\hyphenation}*/
+set_cur_lang;
+#ifdef @!INIT
+if (trie_not_ready)
+ {@+hyph_index=0;goto not_found1;
+ }
+#endif
+set_hyph_index;
+not_found1:
+@<Enter as many hyphenation exceptions as are listed, until coming to a right
+brace; then |return|@>;
+}
+
+@ @<Enter as many...@>=
+n=0;p=null;
+loop@+{@+get_x_token();
+ reswitch: switch (cur_cmd) {
+ case letter: case other_char: case char_given: @<Append a new letter or
+hyphen@>@;@+break;
+ case char_num: {@+scan_char_num();cur_chr=cur_val;cur_cmd=char_given;
+ goto reswitch;
+ }
+ case spacer: case right_brace: {@+if (n > 1) @<Enter a hyphenation exception@>;
+ if (cur_cmd==right_brace) return;
+ n=0;p=null;
+ } @+break;
+ default:@<Give improper \.{\\hyphenation} error@>@;
+ }
+ }
+
+@ @<Give improper \.{\\hyph...@>=
+{@+print_err("Improper ");print_esc("hyphenation");
+@.Improper \\hyphenation...@>
+ print(" will be flushed");
+help2("Hyphenation exceptions must contain only letters",@/
+ "and hyphens. But continue; I'll forgive and forget.");
+error();
+}
+
+@ @<Append a new letter or hyphen@>=
+if (cur_chr=='-') @<Append the value |n| to list |p|@>@;
+else{@+set_lc_code(cur_chr);
+ if (hc[0]==0)
+ {@+print_err("Not a letter");
+@.Not a letter@>
+ help2("Letters in \\hyphenation words must have \\lccode>0.",@/
+ "Proceed; I'll ignore the character I just read.");
+ error();
+ }
+ else if (n < 63)
+ {@+incr(n);hc[n]=hc[0];
+ }
+ }
+
+@ @<Append the value |n| to list |p|@>=
+{@+if (n < 63)
+ {@+q=get_avail();link(q)=p;info(q)=n;p=q;
+ }
+}
+
+@ @<Enter a hyphenation exception@>=
+{@+incr(n);hc[n]=cur_lang;str_room(n);h=0;
+for (j=1; j<=n; j++)
+ {@+h=(h+h+hc[j])%hyph_size;
+ append_char(hc[j]);
+ }
+s=make_string();
+@<Insert the \(p)pair |(s,p)| into the exception table@>;
+}
+
+@ @<Insert the \(p)pair |(s,p)|...@>=
+if (hyph_count==hyph_size) overflow("exception dictionary", hyph_size);
+@:TeX capacity exceeded exception dictionary}{\quad exception dictionary@>
+incr(hyph_count);
+while (hyph_word[h]!=0)
+ {@+@<If the string |hyph_word[h]| is less than \(or)or equal to |s|, interchange
+|(hyph_word[h],hyph_list[h])| with |(s,p)|@>;
+ if (h > 0) decr(h);@+else h=hyph_size;
+ }
+hyph_word[h]=s;hyph_list[h]=p
+
+@ @<If the string |hyph_word[h]| is less than \(or)...@>=
+k=hyph_word[h];
+if (length(k) < length(s)) goto found;
+if (length(k) > length(s)) goto not_found;
+u=str_start[k];v=str_start[s];
+@/do@+{if (str_pool[u] < str_pool[v]) goto found;
+if (str_pool[u] > str_pool[v]) goto not_found;
+incr(u);incr(v);
+}@+ while (!(u==str_start[k+1]));
+found: q=hyph_list[h];hyph_list[h]=p;p=q;@/
+t=hyph_word[h];hyph_word[h]=s;s=t;
+not_found:
+
+@* Initializing the hyphenation tables.
+The trie for \TeX's hyphenation algorithm is built from a sequence of
+patterns following a \.{\\patterns} specification. Such a specification
+is allowed only in \.{INITEX}, since the extra memory for auxiliary tables
+and for the initialization program itself would only clutter up the
+production version of \TeX\ with a lot of deadwood.
+
+The first step is to build a trie that is linked, instead of packed
+into sequential storage, so that insertions are readily made.
+After all patterns have been processed, \.{INITEX}
+compresses the linked trie by identifying common subtries. Finally the
+trie is packed into the efficient sequential form that the hyphenation
+algorithm actually uses.
+
+@<Declare subprocedures for |line_break|@>=
+#ifdef @!INIT
+@<Declare procedures for preprocessing hyphenation patterns@>@;
+#endif
+
+@ Before we discuss trie building in detail, let's consider the simpler
+problem of creating the |hyf_distance|, |hyf_num|, and |hyf_next| arrays.
+
+Suppose, for example, that \TeX\ reads the pattern `\.{ab2cde1}'. This is
+a pattern of length 5, with $n_0\ldots n_5=0\,0\,2\,0\,0\,1$ in the
+notation above. We want the corresponding |trie_op| code |v| to have
+|hyf_distance[v]==3|, |hyf_num[v]==2|, and |hyf_next[v]==@t$v^\prime$@>|,
+where the auxiliary |trie_op| code $v^\prime$ has
+|hyf_distance[@t$v^\prime$@>]==0|, |hyf_num[@t$v^\prime$@>]==1|, and
+|hyf_next[@t$v^\prime$@>]==min_quarterword|.
+
+\TeX\ computes an appropriate value |v| with the |new_trie_op| subroutine
+below, by setting
+$$\hbox{|@t$v^\prime$@>=new_trie_op(0, 1, min_quarterword)|,\qquad
+|v=new_trie_op(3, 2,@t$v^\prime$@>)|.}$$
+This subroutine looks up its three
+parameters in a special hash table, assigning a new value only if these
+three have not appeared before for the current language.
+
+The hash table is called |trie_op_hash|, and the number of entries it contains
+is |trie_op_ptr|.
+
+@<Glob...@>=
+#ifdef @!INIT
+static uint16_t @!trie_op_hash0[trie_op_size+trie_op_size+1],
+ *const @!trie_op_hash = @!trie_op_hash0+trie_op_size;
+ /*trie op codes for quadruples*/
+static quarterword @!trie_used[256];
+ /*largest opcode used so far for this language*/
+static ASCII_code @!trie_op_lang0[trie_op_size],
+ *const @!trie_op_lang = @!trie_op_lang0-1;
+ /*language part of a hashed quadruple*/
+static quarterword @!trie_op_val0[trie_op_size],
+ *const @!trie_op_val = @!trie_op_val0-1;
+ /*opcode corresponding to a hashed quadruple*/
+static int @!trie_op_ptr; /*number of stored ops so far*/
+#endif
+
+@ It's tempting to remove the |overflow| stops in the following procedure;
+|new_trie_op| could return |min_quarterword| (thereby simply ignoring
+part of a hyphenation pattern) instead of aborting the job. However, that would
+lead to different hyphenation results on different installations of \TeX\
+using the same patterns. The |overflow| stops are necessary for portability
+of patterns.
+
+@<Declare procedures for preprocessing hyph...@>=
+static quarterword new_trie_op(small_number @!d, small_number @!n, quarterword @!v)
+{@+
+int h; /*trial hash location*/
+quarterword @!u; /*trial op code*/
+int @!l; /*pointer to stored data*/
+h=abs(n+313*d+361*v+1009*cur_lang)%(trie_op_size+trie_op_size)
+ -trie_op_size;
+loop@+{@+l=trie_op_hash[h];
+ if (l==0) /*empty position found for a new op*/
+ {@+if (trie_op_ptr==trie_op_size)
+ overflow("pattern memory ops", trie_op_size);
+ u=trie_used[cur_lang];
+ if (u==max_quarterword)
+ overflow("pattern memory ops per language",
+ max_quarterword-min_quarterword);
+ incr(trie_op_ptr);incr(u);trie_used[cur_lang]=u;
+ hyf_distance[trie_op_ptr]=d;
+ hyf_num[trie_op_ptr]=n;hyf_next[trie_op_ptr]=v;
+ trie_op_lang[trie_op_ptr]=cur_lang;trie_op_hash[h]=trie_op_ptr;
+ trie_op_val[trie_op_ptr]=u;return u;
+ }
+ if ((hyf_distance[l]==d)&&(hyf_num[l]==n)&&(hyf_next[l]==v)
+ &&(trie_op_lang[l]==cur_lang))
+ {@+return trie_op_val[l];
+ }
+ if (h > -trie_op_size) decr(h);@+else h=trie_op_size;
+ }
+}
+
+@ After |new_trie_op| has compressed the necessary opcode information,
+plenty of information is available to unscramble the data into the
+final form needed by our hyphenation algorithm.
+
+@<Sort \(t)the hyphenation op tables into proper order@>=
+op_start[0]=-min_quarterword;
+for (j=1; j<=255; j++) op_start[j]=op_start[j-1]+qo(trie_used[j-1]);
+for (j=1; j<=trie_op_ptr; j++)
+ trie_op_hash[j]=op_start[trie_op_lang[j]]+trie_op_val[j]; /*destination*/
+for (j=1; j<=trie_op_ptr; j++) while (trie_op_hash[j] > j)
+ {@+k=trie_op_hash[j];@/
+ t=hyf_distance[k];hyf_distance[k]=hyf_distance[j];hyf_distance[j]=t;@/
+ t=hyf_num[k];hyf_num[k]=hyf_num[j];hyf_num[j]=t;@/
+ t=hyf_next[k];hyf_next[k]=hyf_next[j];hyf_next[j]=t;@/
+ trie_op_hash[j]=trie_op_hash[k];trie_op_hash[k]=k;
+ }
+
+@ Before we forget how to initialize the data structures that have been
+mentioned so far, let's write down the code that gets them started.
+
+@<Initialize table entries...@>=
+for (k=-trie_op_size; k<=trie_op_size; k++) trie_op_hash[k]=0;
+for (k=0; k<=255; k++) trie_used[k]=min_quarterword;
+trie_op_ptr=0;
+
+@ The linked trie that is used to preprocess hyphenation patterns appears
+in several global arrays. Each node represents an instruction of the form
+``if you see character |c|, then perform operation |o|, move to the
+next character, and go to node |l|; otherwise go to node |r|.''
+The four quantities |c|, |o|, |l|, and |r| are stored in four arrays
+|trie_c|, |trie_o|, |trie_l|, and |trie_r|. The root of the trie
+is |trie_l[0]|, and the number of nodes is |trie_ptr|. Null trie
+pointers are represented by zero. To initialize the trie, we simply
+set |trie_l[0]| and |trie_ptr| to zero. We also set |trie_c[0]| to some
+arbitrary value, since the algorithm may access it.
+
+The algorithms maintain the condition
+$$\hbox{|trie_c[trie_r[z]] > trie_c[z]|\qquad
+whenever |z!=0| and |trie_r[z]!=0|};$$ in other words, sibling nodes are
+ordered by their |c| fields.
+
+@d trie_root trie_l[0] /*root of the linked trie*/
+
+@<Glob...@>=
+#ifdef @!INIT
+static packed_ASCII_code @!trie_c[trie_size+1];
+ /*characters to match*/
+@t\hskip10pt@>static quarterword @!trie_o[trie_size+1];
+ /*operations to perform*/
+@t\hskip10pt@>static trie_pointer @!trie_l[trie_size+1];
+ /*left subtrie links*/
+@t\hskip10pt@>static trie_pointer @!trie_r[trie_size+1];
+ /*right subtrie links*/
+@t\hskip10pt@>static trie_pointer @!trie_ptr; /*the number of nodes in the trie*/
+@t\hskip10pt@>static trie_pointer @!trie_hash[trie_size+1];
+ /*used to identify equivalent subtries*/
+#endif
+
+@ Let us suppose that a linked trie has already been constructed.
+Experience shows that we can often reduce its size by recognizing common
+subtries; therefore another hash table is introduced for this purpose,
+somewhat similar to |trie_op_hash|. The new hash table will be
+initialized to zero.
+
+The function |trie_node(p)| returns |p| if |p| is distinct from other nodes
+that it has seen, otherwise it returns the number of the first equivalent
+node that it has seen.
+
+Notice that we might make subtries equivalent even if they correspond to
+patterns for different languages, in which the trie ops might mean quite
+different things. That's perfectly all right.
+
+@<Declare procedures for preprocessing hyph...@>=
+static trie_pointer trie_node(trie_pointer @!p) /*converts
+ to a canonical form*/
+{@+
+trie_pointer h; /*trial hash location*/
+trie_pointer @!q; /*trial trie node*/
+h=abs(trie_c[p]+1009*trie_o[p]+@|
+ 2718*trie_l[p]+3142*trie_r[p])%trie_size;
+loop@+{@+q=trie_hash[h];
+ if (q==0)
+ {@+trie_hash[h]=p;return p;
+ }
+ if ((trie_c[q]==trie_c[p])&&(trie_o[q]==trie_o[p])&&@|
+ (trie_l[q]==trie_l[p])&&(trie_r[q]==trie_r[p]))
+ {@+return q;
+ }
+ if (h > 0) decr(h);@+else h=trie_size;
+ }
+}
+
+@ A neat recursive procedure is now able to compress a trie by
+traversing it and applying |trie_node| to its nodes in ``bottom up''
+fashion. We will compress the entire trie by clearing |trie_hash| to
+zero and then saying `|trie_root=compress_trie(trie_root)|'.
+@^recursion@>
+
+@<Declare procedures for preprocessing hyph...@>=
+static trie_pointer compress_trie(trie_pointer @!p)
+{@+if (p==0) return 0;
+else{@+trie_l[p]=compress_trie(trie_l[p]);
+ trie_r[p]=compress_trie(trie_r[p]);
+ return trie_node(p);
+ }
+}
+
+@ The compressed trie will be packed into the |trie| array using a
+``top-down first-fit'' procedure. This is a little tricky, so the reader
+should pay close attention: The |trie_hash| array is cleared to zero
+again and renamed |trie_ref| for this phase of the operation; later on,
+|trie_ref[p]| will be nonzero only if the linked trie node |p| is the
+smallest character
+in a family and if the characters |c| of that family have been allocated to
+locations |trie_ref[p]+c| in the |trie| array. Locations of |trie| that
+are in use will have |trie_link==0|, while the unused holes in |trie|
+will be doubly linked with |trie_link| pointing to the next larger vacant
+location and |trie_back| pointing to the next smaller one. This double
+linking will have been carried out only as far as |trie_max|, where
+|trie_max| is the largest index of |trie| that will be needed.
+To save time at the low end of the trie, we maintain array entries
+|trie_min[c]| pointing to the smallest hole that is greater than~|c|.
+Another array |trie_taken| tells whether or not a given location is
+equal to |trie_ref[p]| for some |p|; this array is used to ensure that
+distinct nodes in the compressed trie will have distinct |trie_ref|
+entries.
+
+@d trie_ref trie_hash /*where linked trie families go into |trie|*/
+@d trie_back(A) trie[A].lh /*backward links in |trie| holes*/
+
+@<Glob...@>=
+#ifdef @!INIT
+static bool @!trie_taken0[trie_size],
+ *const @!trie_taken = @!trie_taken0-1;
+ /*does a family start here?*/
+@t\hskip10pt@>static trie_pointer @!trie_min[256];
+ /*the first possible slot for each character*/
+@t\hskip10pt@>static trie_pointer @!trie_max; /*largest location used in |trie|*/
+@t\hskip10pt@>static bool @!trie_not_ready; /*is the trie still in linked form?*/
+#endif
+
+@ Each time \.{\\patterns} appears, it contributes further patterns to
+the future trie, which will be built only when hyphenation is attempted or
+when a format file is dumped. The boolean variable |trie_not_ready|
+will change to |false| when the trie is compressed; this will disable
+further patterns.
+
+@<Initialize table entries...@>=
+trie_not_ready=true;trie_root=0;trie_c[0]=si(0);trie_ptr=0;
+
+@ Here is how the trie-compression data structures are initialized.
+If storage is tight, it would be possible to overlap |trie_op_hash|,
+|trie_op_lang|, and |trie_op_val| with |trie|, |trie_hash|, and |trie_taken|,
+because we finish with the former just before we need the latter.
+
+@<Get ready to compress the trie@>=
+@<Sort \(t)the hyphenation...@>;
+for (p=0; p<=trie_size; p++) trie_hash[p]=0;
+hyph_root=compress_trie(hyph_root);
+trie_root=compress_trie(trie_root); /*identify equivalent subtries*/
+for (p=0; p<=trie_ptr; p++) trie_ref[p]=0;
+for (p=0; p<=255; p++) trie_min[p]=p+1;
+trie_link(0)=1;trie_max=0
+
+@ The |first_fit| procedure finds the smallest hole |z| in |trie| such that
+a trie family starting at a given node |p| will fit into vacant positions
+starting at |z|. If |c==trie_c[p]|, this means that location |z-c| must
+not already be taken by some other family, and that |z-c+@t$c^\prime$@>|
+must be vacant for all characters $c^\prime$ in the family. The procedure
+sets |trie_ref[p]| to |z-c| when the first fit has been found.
+
+@<Declare procedures for preprocessing hyph...@>=
+static void first_fit(trie_pointer @!p) /*packs a family into |trie|*/
+{@+
+trie_pointer h; /*candidate for |trie_ref[p]|*/
+trie_pointer @!z; /*runs through holes*/
+trie_pointer @!q; /*runs through the family starting at |p|*/
+ASCII_code @!c; /*smallest character in the family*/
+trie_pointer @!l, @!r; /*left and right neighbors*/
+int @!ll; /*upper limit of |trie_min| updating*/
+c=so(trie_c[p]);
+z=trie_min[c]; /*get the first conceivably good hole*/
+loop@+{@+h=z-c;@/
+ @<Ensure that |trie_max>=h+256|@>;
+ if (trie_taken[h]) goto not_found;
+ @<If all characters of the family fit relative to |h|, then |goto found|,\30\
+otherwise |goto not_found|@>;
+ not_found: z=trie_link(z); /*move to the next hole*/
+ }
+found: @<Pack the family into |trie| relative to |h|@>;
+}
+
+@ By making sure that |trie_max| is at least |h+256|, we can be sure that
+|trie_max > z|, since |h==z-c|. It follows that location |trie_max| will
+never be occupied in |trie|, and we will have |trie_max >= trie_link(z)|.
+
+@<Ensure that |trie_max>=h+256|@>=
+if (trie_max < h+256)
+ {@+if (trie_size <= h+256) overflow("pattern memory", trie_size);
+@:TeX capacity exceeded pattern memory}{\quad pattern memory@>
+ @/do@+{incr(trie_max);trie_taken[trie_max]=false;
+ trie_link(trie_max)=trie_max+1;trie_back(trie_max)=trie_max-1;
+ }@+ while (!(trie_max==h+256));
+ }
+
+@ @<If all characters of the family fit relative to |h|...@>=
+q=trie_r[p];
+while (q > 0)
+ {@+if (trie_link(h+so(trie_c[q]))==0) goto not_found;
+ q=trie_r[q];
+ }
+goto found
+
+@ @<Pack the family into |trie| relative to |h|@>=
+trie_taken[h]=true;trie_ref[p]=h;q=p;
+@/do@+{z=h+so(trie_c[q]);l=trie_back(z);r=trie_link(z);
+trie_back(r)=l;trie_link(l)=r;trie_link(z)=0;
+if (l < 256)
+ {@+if (z < 256) ll=z;@+else ll=256;
+ @/do@+{trie_min[l]=r;incr(l);
+ }@+ while (!(l==ll));
+ }
+q=trie_r[q];
+}@+ while (!(q==0))
+
+@ To pack the entire linked trie, we use the following recursive procedure.
+@^recursion@>
+
+@<Declare procedures for preprocessing hyph...@>=
+static void trie_pack(trie_pointer @!p) /*pack subtries of a family*/
+{@+trie_pointer q; /*a local variable that need not be saved on recursive calls*/
+@/do@+{q=trie_l[p];
+if ((q > 0)&&(trie_ref[q]==0))
+ {@+first_fit(q);trie_pack(q);
+ }
+p=trie_r[p];
+}@+ while (!(p==0));
+}
+
+@ When the whole trie has been allocated into the sequential table, we
+must go through it once again so that |trie| contains the correct
+information. Null pointers in the linked trie will be represented by the
+value~0, which properly implements an ``empty'' family.
+
+@<Move the data into |trie|@>=
+h.rh=0;h.b0=min_quarterword;h.b1=min_quarterword; /*|trie_link=0|,
+ |trie_op=min_quarterword|, |trie_char=qi(0)|*/
+if (trie_max==0) /*no patterns were given*/
+ {@+for (r=0; r<=256; r++) trie[r]=h;
+ trie_max=256;
+ }
+else{@+if (hyph_root > 0) trie_fix(hyph_root);
+ if (trie_root > 0) trie_fix(trie_root); /*this fixes the non-holes in |trie|*/
+ r=0; /*now we will zero out all the holes*/
+ @/do@+{s=trie_link(r);trie[r]=h;r=s;
+ }@+ while (!(r > trie_max));
+ }
+trie_char(0)=qi('?'); /*make |trie_char(c)!=c| for all |c|*/
+
+@ The fixing-up procedure is, of course, recursive. Since the linked trie
+usually has overlapping subtries, the same data may be moved several
+times; but that causes no harm, and at most as much work is done as it
+took to build the uncompressed trie.
+@^recursion@>
+
+@<Declare procedures for preprocessing hyph...@>=
+static void trie_fix(trie_pointer @!p) /*moves |p| and its siblings into |trie|*/
+{@+trie_pointer q; /*a local variable that need not be saved on recursive calls*/
+ASCII_code @!c; /*another one that need not be saved*/
+trie_pointer @!z; /*|trie| reference; this local variable must be saved*/
+z=trie_ref[p];
+@/do@+{q=trie_l[p];c=so(trie_c[p]);
+trie_link(z+c)=trie_ref[q];trie_char(z+c)=qi(c);trie_op(z+c)=trie_o[p];
+if (q > 0) trie_fix(q);
+p=trie_r[p];
+}@+ while (!(p==0));
+}
+
+@ Now let's go back to the easier problem, of building the linked
+trie. When \.{INITEX} has scanned the `\.{\\patterns}' control
+sequence, it calls on |new_patterns| to do the right thing.
+
+@<Declare procedures for preprocessing hyph...@>=
+static void new_patterns(void) /*initializes the hyphenation pattern data*/
+{@+
+int k, @!l; /*indices into |hc| and |hyf|;
+ not always in |small_number| range*/
+bool @!digit_sensed; /*should the next digit be treated as a letter?*/
+quarterword @!v; /*trie op code*/
+trie_pointer @!p, @!q; /*nodes of trie traversed during insertion*/
+bool @!first_child; /*is |p==trie_l[q]|?*/
+int @!c; /*character being inserted*/
+if (trie_not_ready)
+ {@+set_cur_lang;scan_left_brace(); /*a left brace must follow \.{\\patterns}*/
+ @<Enter all of the patterns into a linked trie, until coming to a right
+brace@>;
+ if (saving_hyph_codes > 0)
+ @<Store hyphenation codes for current language@>;
+ }
+else{@+print_err("Too late for ");print_esc("patterns");
+ help1("All patterns must be given before typesetting begins.");
+ error();link(garbage)=scan_toks(false, false);flush_list(def_ref);
+ }
+}
+
+@ Novices are not supposed to be using \.{\\patterns}, so the error
+messages are terse. (Note that all error messages appear in \TeX's string
+pool, even if they are used only by \.{INITEX}.)
+
+@<Enter all of the patterns into a linked trie...@>=
+k=0;hyf[0]=0;digit_sensed=false;
+loop@+{@+get_x_token();
+ switch (cur_cmd) {
+ case letter: case other_char: @<Append a new letter or a hyphen level@>@;@+break;
+ case spacer: case right_brace: {@+if (k > 0)
+ @<Insert a new pattern into the linked trie@>;
+ if (cur_cmd==right_brace) goto done;
+ k=0;hyf[0]=0;digit_sensed=false;
+ } @+break;
+ default:{@+print_err("Bad ");print_esc("patterns");
+@.Bad \\patterns@>
+ help1("(See Appendix H.)");error();
+ }
+ }
+ }
+done:
+
+@ @<Append a new letter or a hyphen level@>=
+if (digit_sensed||(cur_chr < '0')||(cur_chr > '9'))
+ {@+if (cur_chr=='.') cur_chr=0; /*edge-of-word delimiter*/
+ else{@+cur_chr=lc_code(cur_chr);
+ if (cur_chr==0)
+ {@+print_err("Nonletter");
+@.Nonletter@>
+ help1("(See Appendix H.)");error();
+ }
+ }
+ if (k < 63)
+ {@+incr(k);hc[k]=cur_chr;hyf[k]=0;digit_sensed=false;
+ }
+ }
+else if (k < 63)
+ {@+hyf[k]=cur_chr-'0';digit_sensed=true;
+ }
+
+@ When the following code comes into play, the pattern $p_1\ldots p_k$
+appears in |hc[1 dotdot k]|, and the corresponding sequence of numbers $n_0\ldots
+n_k$ appears in |hyf[0 dotdot k]|.
+
+@<Insert a new pattern into the linked trie@>=
+{@+@<Compute the trie op code, |v|, and set |l:=0|@>;
+q=0;hc[0]=cur_lang;
+while (l <= k)
+ {@+c=hc[l];incr(l);p=trie_l[q];first_child=true;
+ while ((p > 0)&&(c > so(trie_c[p])))
+ {@+q=p;p=trie_r[q];first_child=false;
+ }
+ if ((p==0)||(c < so(trie_c[p])))
+ @<Insert a new trie node between |q| and |p|, and make |p| point to it@>;
+ q=p; /*now node |q| represents $p_1\ldots p_{l-1}$*/
+ }
+if (trie_o[q]!=min_quarterword)
+ {@+print_err("Duplicate pattern");
+@.Duplicate pattern@>
+ help1("(See Appendix H.)");error();
+ }
+trie_o[q]=v;
+}
+
+@ @<Insert a new trie node between |q| and |p|...@>=
+{@+if (trie_ptr==trie_size) overflow("pattern memory", trie_size);
+@:TeX capacity exceeded pattern memory}{\quad pattern memory@>
+incr(trie_ptr);trie_r[trie_ptr]=p;p=trie_ptr;trie_l[p]=0;
+if (first_child) trie_l[q]=p;@+else trie_r[q]=p;
+trie_c[p]=si(c);trie_o[p]=min_quarterword;
+}
+
+@ @<Compute the trie op code, |v|...@>=
+if (hc[1]==0) hyf[0]=0;
+if (hc[k]==0) hyf[k]=0;
+l=k;v=min_quarterword;
+loop@+{@+if (hyf[l]!=0) v=new_trie_op(k-l, hyf[l], v);
+ if (l > 0) decr(l);@+else goto done1;
+ }
+done1:
+
+@ Finally we put everything together: Here is how the trie gets to its
+final, efficient form.
+The following packing routine is rigged so that the root of the linked
+tree gets mapped into location 1 of |trie|, as required by the hyphenation
+algorithm. This happens because the first call of |first_fit| will
+``take'' location~1.
+
+@<Declare procedures for preprocessing hyphenation patterns@>=
+static void init_trie(void)
+{@+int @!p; /*pointer for initialization*/
+int @!j, @!k, @!t; /*all-purpose registers for initialization*/
+int @!r, @!s; /*used to clean up the packed |trie|*/
+two_halves @!h; /*template used to zero out |trie|'s holes*/
+@<Local variables to save the profiling context@>@;
+@<Charge the time used here on |init_trie|@>@;
+@<Get ready to compress the trie@>;
+if (trie_root!=0)
+ {@+first_fit(trie_root);trie_pack(trie_root);
+ }
+if (hyph_root!=0) @<Pack all stored |hyph_codes|@>;
+@<Move the data into |trie|@>;
+trie_not_ready=false;
+@<restore the previous current file, line, and command@>@;
+}
+
+@* Breaking vertical lists into pages.
+The |vsplit| procedure, which implements \TeX's \.{\\vsplit} operation,
+is considerably simpler than |line_break| because it doesn't have to
+worry about hyphenation, and because its mission is to discover a single
+break instead of an optimum sequence of breakpoints. But before we get
+into the details of |vsplit|, we need to consider a few more basic things.
+
+@ A subroutine called |prune_page_top| takes a pointer to a vlist and
+returns a pointer to a modified vlist in which all glue, kern, and penalty nodes
+have been deleted before the first box or rule node. However, the first
+box or rule is actually preceded by a newly created glue node designed so that
+the topmost baseline will be at distance |split_top_skip| from the top,
+whenever this is possible without backspacing.
+
+When the second argument |s| is |false| the deleted nodes are destroyed,
+otherwise they are collected in a list starting at |split_disc|.
+
+In this routine and those that follow, we make use of the fact that a
+vertical list contains no character nodes, hence the |type| field exists
+for each node in the list.
+@^data structure assumptions@>
+
+@p static pointer prune_page_top(pointer @!p, bool @!s)
+ /*adjust top after page break*/
+{@+pointer prev_p; /*lags one step behind |p|*/
+pointer @!q, @!r; /*temporary variables for list manipulation*/
+prev_p=temp_head;link(temp_head)=p;
+while (p!=null)
+ switch (type(p)) {
+ case hlist_node: case vlist_node: case rule_node: @<Insert glue for |split_top_skip|
+and set~|p:=null|@>@;@+break;
+ case whatsit_node: case mark_node: case ins_node: {@+prev_p=p;p=link(prev_p);
+ } @+break;
+ case glue_node: case kern_node: case penalty_node: {@+q=p;p=link(q);link(q)=null;
+ link(prev_p)=p;
+ if (s)
+ {@+if (split_disc==null) split_disc=q;@+else link(r)=q;
+ r=q;
+ }
+ else flush_node_list(q);
+ } @+break;
+ default:confusion("pruning");
+@:this can't happen pruning}{\quad pruning@>
+ }
+return link(temp_head);
+}
+
+@ @<Insert glue for |split_top_skip|...@>=
+{@+q=new_skip_param(split_top_skip_code);link(prev_p)=q;link(q)=p;
+ /*now |temp_ptr==glue_ptr(q)|*/
+if (width(temp_ptr) > height(p)) width(temp_ptr)=width(temp_ptr)-height(p);
+else width(temp_ptr)=0;
+p=null;
+}
+
+@ The next subroutine finds the best place to break a given vertical list
+so as to obtain a box of height~|h|, with maximum depth~|d|.
+A pointer to the beginning of the vertical list is given,
+and a pointer to the optimum breakpoint is returned. The list is effectively
+followed by a forced break, i.e., a penalty node with the |eject_penalty|;
+if the best break occurs at this artificial node, the value |null| is returned.
+
+An array of six |scaled| distances is used to keep track of the height
+from the beginning of the list to the current place, just as in |line_break|.
+In fact, we use one of the same arrays, only changing its name to reflect
+its new significance.
+
+@d active_height active_width /*new name for the six distance variables*/
+@d cur_height active_height[1] /*the natural height*/
+@d set_height_zero(A) active_height[A]=0 /*initialize the height to zero*/
+@#
+@p static pointer vert_break(pointer @!p, scaled @!h, scaled @!d)
+ /*finds optimum page break*/
+{@+
+pointer prev_p; /*if |p| is a glue node, |type(prev_p)| determines
+ whether |p| is a legal breakpoint*/
+pointer @!q, @!r; /*glue specifications*/
+int @!pi; /*penalty value*/
+int @!b; /*badness at a trial breakpoint*/
+int @!least_cost; /*the smallest badness plus penalties found so far*/
+pointer @!best_place; /*the most recent break that leads to |least_cost|*/
+scaled @!prev_dp; /*depth of previous box in the list*/
+small_number @!t; /*|type| of the node following a kern*/
+prev_p=p; /*an initial glue node is not a legal breakpoint*/
+least_cost=awful_bad;do_all_six(set_height_zero);prev_dp=0;
+loop@+{@+@<If node |p| is a legal breakpoint, check if this break is the best
+known, and |goto done| if |p| is null or if the page-so-far is already too
+full to accept more stuff@>;
+ prev_p=p;p=link(prev_p);
+ }
+done: return best_place;
+}
+
+@ A global variable |best_height_plus_depth| will be set to the natural size
+of the box that corresponds to the optimum breakpoint found by |vert_break|.
+(This value is used by the insertion-splitting algorithm of the page builder.)
+
+@<Glob...@>=
+static scaled @!best_height_plus_depth; /*height of the best box, without stretching or
+ shrinking*/
+
+@ A subtle point to be noted here is that the maximum depth~|d| might be
+negative, so |cur_height| and |prev_dp| might need to be corrected even
+after a glue or kern node.
+
+@<If node |p| is a legal breakpoint, check...@>=
+if (p==null) pi=eject_penalty;
+else@<Use node |p| to update the current height and depth measurements; if
+this node is not a legal breakpoint, |goto not_found| or |update_heights|,
+otherwise set |pi| to the associated penalty at the break@>;
+@<Check if node |p| is a new champion breakpoint; then \(go)|goto done| if
+|p| is a forced break or if the page-so-far is already too full@>;
+if ((type(p) < glue_node)||(type(p) > kern_node)) goto not_found;
+update_heights: @<Update the current height and depth measurements with respect
+to a glue or kern node~|p|@>;
+not_found: if (prev_dp > d)
+ {@+cur_height=cur_height+prev_dp-d;
+ prev_dp=d;
+ }
+
+@ @<Use node |p| to update the current height and depth measurements...@>=
+switch (type(p)) {
+case hlist_node: case vlist_node: case rule_node: {@+@t@>@;@/
+ cur_height=cur_height+prev_dp+height(p);prev_dp=depth(p);
+ goto not_found;
+ }
+case whatsit_node: @<Process whatsit |p| in |vert_break| loop, |goto not_found|@>;
+case glue_node: if (precedes_break(prev_p)) pi=0;
+ else goto update_heights;@+break;
+case kern_node: {@+if (link(p)==null) t=penalty_node;
+ else t=type(link(p));
+ if (t==glue_node) pi=0;@+else goto update_heights;
+ } @+break;
+case penalty_node: pi=penalty(p);@+break;
+case mark_node: case ins_node: goto not_found;
+default:confusion("vertbreak");
+@:this can't happen vertbreak}{\quad vertbreak@>
+}
+
+@ @d deplorable 100000 /*more than |inf_bad|, but less than |awful_bad|*/
+
+@<Check if node |p| is a new champion breakpoint; then \(go)...@>=
+if (pi < inf_penalty)
+ {@+@<Compute the badness, |b|, using |awful_bad| if the box is too full@>;
+ if (b < awful_bad)
+ if (pi <= eject_penalty) b=pi;
+ else if (b < inf_bad) b=b+pi;
+ else b=deplorable;
+ if (b <= least_cost)
+ {@+best_place=p;least_cost=b;
+ best_height_plus_depth=cur_height+prev_dp;
+ }
+ if ((b==awful_bad)||(pi <= eject_penalty)) goto done;
+ }
+
+@ @<Compute the badness, |b|, using |awful_bad| if the box is too full@>=
+if (cur_height < h)
+ if ((active_height[3]!=0)||(active_height[4]!=0)||
+ (active_height[5]!=0)) b=0;
+ else b=badness(h-cur_height, active_height[2]);
+else if (cur_height-h > active_height[6]) b=awful_bad;
+else b=badness(cur_height-h, active_height[6])
+
+@ Vertical lists that are subject to the |vert_break| procedure should not
+contain infinite shrinkability, since that would permit any amount of
+information to ``fit'' on one page.
+
+@<Update the current height and depth measurements with...@>=
+if (type(p)==kern_node) q=p;
+else{@+q=glue_ptr(p);
+ active_height[2+stretch_order(q)]=@|
+ active_height[2+stretch_order(q)]+stretch(q);@/
+ active_height[6]=active_height[6]+shrink(q);
+ if ((shrink_order(q)!=normal)&&(shrink(q)!=0))
+ {@+@t@>@;@/
+ print_err("Infinite glue shrinkage found in box being split");@/
+@.Infinite glue shrinkage...@>
+ help4("The box you are \\vsplitting contains some infinitely",@/
+ "shrinkable glue, e.g., `\\vss' or `\\vskip 0pt minus 1fil'.",@/
+ "Such glue doesn't belong there; but you can safely proceed,",@/
+ "since the offensive shrinkability has been made finite.");
+ error();r=new_spec(q);shrink_order(r)=normal;delete_glue_ref(q);
+ glue_ptr(p)=r;q=r;
+ }
+ }
+cur_height=cur_height+prev_dp+width(q);prev_dp=0
+
+@ Now we are ready to consider |vsplit| itself. Most of
+its work is accomplished by the two subroutines that we have just considered.
+
+Given the number of a vlist box |n|, and given a desired page height |h|,
+the |vsplit| function finds the best initial segment of the vlist and
+returns a box for a page of height~|h|. The remainder of the vlist, if
+any, replaces the original box, after removing glue and penalties and
+adjusting for |split_top_skip|. Mark nodes in the split-off box are used to
+set the values of |split_first_mark| and |split_bot_mark|; we use the
+fact that |split_first_mark==null| if and only if |split_bot_mark==null|.
+
+The original box becomes ``void'' if and only if it has been entirely
+extracted. The extracted box is ``void'' if and only if the original
+box was void (or if it was, erroneously, an hlist box).
+
+@p @t\4@>@<Declare the function called |do_marks|@>@;
+static pointer vsplit(halfword @!n, scaled @!h)
+ /*extracts a page of height |h| from box |n|*/
+{@+
+pointer v; /*the box to be split*/
+pointer p; /*runs through the vlist*/
+pointer q; /*points to where the break occurs*/
+cur_val=n;fetch_box(v);
+flush_node_list(split_disc);split_disc=null;
+if (sa_mark!=null)
+ if (do_marks(vsplit_init, 0, sa_mark)) sa_mark=null;
+if (split_first_mark!=null)
+ {@+delete_token_ref(split_first_mark);split_first_mark=null;
+ delete_token_ref(split_bot_mark);split_bot_mark=null;
+ }
+@<Dispense with trivial cases of void or bad boxes@>;
+q=vert_break(list_ptr(v), h, split_max_depth);
+@<Look at all the marks in nodes before the break, and set the final link
+to |null| at the break@>;
+q=prune_page_top(q, saving_vdiscards > 0);
+p=list_ptr(v);free_node(v, box_node_size);
+if (q!=null) q=vpack(q, natural);
+change_box(q); /*the |eq_level| of the box stays the same*/
+return vpackage(p, h, exactly, split_max_depth);
+}
+
+@ @<Dispense with trivial cases of void or bad boxes@>=
+if (v==null)
+ {@+return null;
+ }
+if (type(v)!=vlist_node)
+ {@+print_err("");print_esc("vsplit");print(" needs a ");
+ print_esc("vbox");
+@:vsplit\_}{\.{\\vsplit needs a \\vbox}@>
+ help2("The box you are trying to split is an \\hbox.",@/
+ "I can't split such a box, so I'll leave it alone.");
+ error();return null;
+ }
+
+@ It's possible that the box begins with a penalty node that is the
+``best'' break, so we must be careful to handle this special case correctly.
+
+@<Look at all the marks...@>=
+p=list_ptr(v);
+if (p==q) list_ptr(v)=null;
+else loop@+{@+if (type(p)==mark_node)
+ if (mark_class(p)!=0) @<Update the current marks for |vsplit|@>@;
+ else if (split_first_mark==null)
+ {@+split_first_mark=mark_ptr(p);
+ split_bot_mark=split_first_mark;
+ token_ref_count(split_first_mark)=@|
+ token_ref_count(split_first_mark)+2;
+ }
+ else{@+delete_token_ref(split_bot_mark);
+ split_bot_mark=mark_ptr(p);
+ add_token_ref(split_bot_mark);
+ }
+ if (link(p)==q)
+ {@+link(p)=null;goto done;
+ }
+ p=link(p);
+ }
+done:
+
+@* The page builder.
+When \TeX\ appends new material to its main vlist in vertical mode, it uses
+a method something like |vsplit| to decide where a page ends, except that
+the calculations are done ``on line'' as new items come in.
+The main complication in this process is that insertions must be put
+into their boxes and removed from the vlist, in a more-or-less optimum manner.
+
+We shall use the term ``current page'' for that part of the main vlist that
+is being considered as a candidate for being broken off and sent to the
+user's output routine. The current page starts at |link(page_head)|, and
+it ends at |page_tail|. We have |page_head==page_tail| if this list is empty.
+@^current page@>
+
+Utter chaos would reign if the user kept changing page specifications
+while a page is being constructed, so the page builder keeps the pertinent
+specifications frozen as soon as the page receives its first box or
+insertion. The global variable |page_contents| is |empty| when the
+current page contains only mark nodes and content-less whatsit nodes; it
+is |inserts_only| if the page contains only insertion nodes in addition to
+marks and whatsits. Glue nodes, kern nodes, and penalty nodes are
+discarded until a box or rule node appears, at which time |page_contents|
+changes to |box_there|. As soon as |page_contents| becomes non-|empty|,
+the current |vsize| and |max_depth| are squirreled away into |page_goal|
+and |page_max_depth|; the latter values will be used until the page has
+been forwarded to the user's output routine. The \.{\\topskip} adjustment
+is made when |page_contents| changes to |box_there|.
+
+Although |page_goal| starts out equal to |vsize|, it is decreased by the
+scaled natural height-plus-depth of the insertions considered so far, and by
+the \.{\\skip} corrections for those insertions. Therefore it represents
+the size into which the non-inserted material should fit, assuming that
+all insertions in the current page have been made.
+
+The global variables |best_page_break| and |least_page_cost| correspond
+respectively to the local variables |best_place| and |least_cost| in the
+|vert_break| routine that we have already studied; i.e., they record the
+location and value of the best place currently known for breaking the
+current page. The value of |page_goal| at the time of the best break is
+stored in |best_size|.
+
+@d inserts_only 1
+ /*|page_contents| when an insert node has been contributed, but no boxes*/
+@d box_there 2 /*|page_contents| when a box or rule has been contributed*/
+
+@<Glob...@>=
+static pointer @!page_tail; /*the final node on the current page*/
+static int @!page_contents; /*what is on the current page so far?*/
+static scaled @!page_max_depth; /*maximum box depth on page being built*/
+static pointer @!best_page_break; /*break here to get the best page known so far*/
+static int @!least_page_cost; /*the score for this currently best page*/
+static scaled @!best_size; /*its |page_goal|*/
+
+@ The page builder has another data structure to keep track of insertions.
+This is a list of four-word nodes, starting and ending at |page_ins_head|.
+That is, the first element of the list is node |r@t$\_1$@>==link(page_ins_head)|;
+node $r_j$ is followed by |r@t$\_{j+1}$@>==link(r@t$\_j$@>)|; and if there are
+|n| items we have |r@t$\_{n+1}$@>==page_ins_head|. The |subtype| field of
+each node in this list refers to an insertion number; for example, `\.{\\insert
+250}' would correspond to a node whose |subtype| is |qi(250)|
+(the same as the |subtype| field of the relevant |ins_node|). These |subtype|
+fields are in increasing order, and |subtype(page_ins_head)==
+qi(255)|, so |page_ins_head| serves as a convenient sentinel
+at the end of the list. A record is present for each insertion number that
+appears in the current page.
+
+The |type| field in these nodes distinguishes two possibilities that
+might occur as we look ahead before deciding on the optimum page break.
+If |type(r)==inserting|, then |height(r)| contains the total of the
+height-plus-depth dimensions of the box and all its inserts seen so far.
+If |type(r)==split_up|, then no more insertions will be made into this box,
+because at least one previous insertion was too big to fit on the current
+page; |broken_ptr(r)| points to the node where that insertion will be
+split, if \TeX\ decides to split it, |broken_ins(r)| points to the
+insertion node that was tentatively split, and |height(r)| includes also the
+natural height plus depth of the part that would be split off.
+
+In both cases, |last_ins_ptr(r)| points to the last |ins_node|
+encountered for box |qo(subtype(r))| that would be at least partially
+inserted on the next page; and |best_ins_ptr(r)| points to the last
+such |ins_node| that should actually be inserted, to get the page with
+minimum badness among all page breaks considered so far. We have
+|best_ins_ptr(r)==null| if and only if no insertion for this box should
+be made to produce this optimum page.
+
+The data structure definitions here use the fact that the |@!height| field
+appears in the fourth word of a box node.
+@^data structure assumptions@>
+
+@d page_ins_node_size 4 /*number of words for a page insertion node*/
+@d inserting 0 /*an insertion class that has not yet overflowed*/
+@d split_up 1 /*an overflowed insertion class*/
+@d broken_ptr(A) link(A+1)
+ /*an insertion for this class will break here if anywhere*/
+@d broken_ins(A) info(A+1) /*this insertion might break at |broken_ptr|*/
+@d last_ins_ptr(A) link(A+2) /*the most recent insertion for this |subtype|*/
+@d best_ins_ptr(A) info(A+2) /*the optimum most recent insertion*/
+
+@<Initialize the special list heads...@>=
+subtype(page_ins_head)=qi(255);
+type(page_ins_head)=split_up;link(page_ins_head)=page_ins_head;
+
+@ An array |page_so_far| records the heights and depths of everything
+on the current page. This array contains six |scaled| numbers, like the
+similar arrays already considered in |line_break| and |vert_break|; and it
+also contains |page_goal| and |page_depth|, since these values are
+all accessible to the user via |set_page_dimen| commands. The
+value of |page_so_far[1]| is also called |page_total|. The stretch
+and shrink components of the \.{\\skip} corrections for each insertion are
+included in |page_so_far|, but the natural space components of these
+corrections are not, since they have been subtracted from |page_goal|.
+
+The variable |page_depth| records the depth of the current page; it has been
+adjusted so that it is at most |page_max_depth|. The variable
+|last_glue| points to the glue specification of the most recent node
+contributed from the contribution list, if this was a glue node; otherwise
+|last_glue==max_halfword|. (If the contribution list is nonempty,
+however, the value of |last_glue| is not necessarily accurate.)
+The variables |last_penalty|, |last_kern|, and |last_node_type|
+are similar. And
+finally, |insert_penalties| holds the sum of the penalties associated with
+all split and floating insertions.
+
+@d page_goal page_so_far[0] /*desired height of information on page being built*/
+@d page_total page_so_far[1] /*height of the current page*/
+@d page_shrink page_so_far[6] /*shrinkability of the current page*/
+@d page_depth page_so_far[7] /*depth of the current page*/
+
+@<Glob...@>=
+static scaled @!page_so_far[8]; /*height and glue of the current page*/
+static pointer @!last_glue; /*used to implement \.{\\lastskip}*/
+static int @!last_penalty; /*used to implement \.{\\lastpenalty}*/
+static scaled @!last_kern; /*used to implement \.{\\lastkern}*/
+static int @!last_node_type; /*used to implement \.{\\lastnodetype}*/
+static int @!insert_penalties; /*sum of the penalties for insertions
+ that were held over*/
+
+@ @<Put each...@>=
+primitive("pagegoal", set_page_dimen, 0);
+@!@:page\_goal\_}{\.{\\pagegoal} primitive@>
+primitive("pagetotal", set_page_dimen, 1);
+@!@:page\_total\_}{\.{\\pagetotal} primitive@>
+primitive("pagestretch", set_page_dimen, 2);
+@!@:page\_stretch\_}{\.{\\pagestretch} primitive@>
+primitive("pagefilstretch", set_page_dimen, 3);
+@!@:page\_fil\_stretch\_}{\.{\\pagefilstretch} primitive@>
+primitive("pagefillstretch", set_page_dimen, 4);
+@!@:page\_fill\_stretch\_}{\.{\\pagefillstretch} primitive@>
+primitive("pagefilllstretch", set_page_dimen, 5);
+@!@:page\_filll\_stretch\_}{\.{\\pagefilllstretch} primitive@>
+primitive("pageshrink", set_page_dimen, 6);
+@!@:page\_shrink\_}{\.{\\pageshrink} primitive@>
+primitive("pagedepth", set_page_dimen, 7);
+@!@:page\_depth\_}{\.{\\pagedepth} primitive@>
+
+@ @<Cases of |print_cmd_chr|...@>=
+case set_page_dimen: switch (chr_code) {
+case 0: print_esc("pagegoal");@+break;
+case 1: print_esc("pagetotal");@+break;
+case 2: print_esc("pagestretch");@+break;
+case 3: print_esc("pagefilstretch");@+break;
+case 4: print_esc("pagefillstretch");@+break;
+case 5: print_esc("pagefilllstretch");@+break;
+case 6: print_esc("pageshrink");@+break;
+default:print_esc("pagedepth");
+} @+break;
+
+@ @d print_plus(A, B) if (page_so_far[A]!=0)
+ {@+print(" plus ");print_scaled(page_so_far[A]);print(B);@+}
+
+@p static void print_totals(void)
+{@+print_scaled(page_total);
+print_plus(2,"");
+print_plus(3,"fil");
+print_plus(4,"fill");
+print_plus(5,"filll");
+if (page_shrink!=0)
+ {@+print(" minus ");print_scaled(page_shrink);
+ }
+}
+
+@ @<Show the status of the current page@>=
+if (page_head!=page_tail)
+ {@+print_nl("### current page:");
+ if (output_active) print(" (held over for next output)");
+@.held over for next output@>
+ show_box(link(page_head));
+ if (page_contents > empty)
+ {@+print_nl("total height ");print_totals();
+@:total\_height}{\.{total height}@>
+ print_nl(" goal height ");print_scaled(page_goal);
+@.goal height@>
+ r=link(page_ins_head);
+ while (r!=page_ins_head)
+ {@+print_ln();print_esc("insert");t=qo(subtype(r));
+ print_int(t);print(" adds ");
+ if (count(t)==1000) t=height(r);
+ else t=x_over_n(height(r), 1000)*count(t);
+ print_scaled(t);
+ if (type(r)==split_up)
+ {@+q=page_head;t=0;
+ @/do@+{q=link(q);
+ if ((type(q)==ins_node)&&(subtype(q)==subtype(r))) incr(t);
+ }@+ while (!(q==broken_ins(r)));
+ print(", #");print_int(t);print(" might split");
+ }
+ r=link(r);
+ }
+ }
+ }
+
+@ Here is a procedure that is called when the |page_contents| is changing
+from |empty| to |inserts_only| or |box_there|.
+
+@d set_page_so_far_zero(A) page_so_far[A]=0
+
+@p static void freeze_page_specs(small_number @!s)
+{@+page_contents=s;
+page_goal=vsize;page_max_depth=max_depth;
+page_depth=0;do_all_six(set_page_so_far_zero);
+least_page_cost=awful_bad;
+#ifdef @!STAT
+if (tracing_pages > 0)
+ {@+begin_diagnostic();
+ print_nl("%% goal height=");print_scaled(page_goal);
+@.goal height@>
+ print(", max depth=");print_scaled(page_max_depth);
+ end_diagnostic(false);
+ } @;
+#endif
+@;@/
+}
+
+@ Pages are built by appending nodes to the current list in \TeX's
+vertical mode, which is at the outermost level of the semantic nest. This
+vlist is split into two parts; the ``current page'' that we have been
+talking so much about already, and the ``contribution list'' that receives
+new nodes as they are created. The current page contains everything that
+the page builder has accounted for in its data structures, as described
+above, while the contribution list contains other things that have been
+generated by other parts of \TeX\ but have not yet been
+seen by the page builder.
+The contribution list starts at |link(contrib_head)|, and it ends at the
+current node in \TeX's vertical mode.
+
+When \TeX\ has appended new material in vertical mode, it calls the procedure
+|build_page|, which tries to catch up by moving nodes from the contribution
+list to the current page. This procedure will succeed in its goal of
+emptying the contribution list, unless a page break is discovered, i.e.,
+unless the current page has grown to the point where the optimum next
+page break has been determined. In the latter case, the nodes after the
+optimum break will go back onto the contribution list, and control will
+effectively pass to the user's output routine.
+
+We make |type(page_head)==glue_node|, so that an initial glue node on
+the current page will not be considered a valid breakpoint.
+
+@<Initialize the special list...@>=
+type(page_head)=glue_node;subtype(page_head)=normal;
+
+@ The global variable |output_active| is true during the time the
+user's output routine is driving \TeX.
+
+@<Glob...@>=
+static bool @!output_active; /*are we in the midst of an output routine?*/
+
+@ @<Set init...@>=
+output_active=false;insert_penalties=0;
+
+@ The page builder is ready to start a fresh page if we initialize
+the following state variables. (However, the page insertion list is initialized
+elsewhere.)
+
+@<Start a new current page@>=
+page_contents=empty;page_tail=page_head;link(page_head)=null;@/
+last_glue=max_halfword;last_penalty=0;last_kern=0;
+last_node_type=-1;
+page_depth=0;page_max_depth=0
+
+@ At certain times box 255 is supposed to be void (i.e., |null|),
+or an insertion box is supposed to be ready to accept a vertical list.
+If not, an error message is printed, and the following subroutine
+flushes the unwanted contents, reporting them to the user.
+
+@p static void box_error(eight_bits @!n)
+{@+error();begin_diagnostic();
+print_nl("The following box has been deleted:");
+@.The following...deleted@>
+show_box(box(n));end_diagnostic(true);
+flush_node_list(box(n));box(n)=null;
+}
+
+@ The following procedure guarantees that a given box register
+does not contain an \.{\\hbox}.
+
+@p static void ensure_vbox(eight_bits @!n)
+{@+pointer p; /*the box register contents*/
+p=box(n);
+if (p!=null) if (type(p)==hlist_node)
+ {@+print_err("Insertions can only be added to a vbox");
+@.Insertions can only...@>
+ help3("Tut tut: You're trying to \\insert into a",@/
+ "\\box register that now contains an \\hbox.",@/
+ "Proceed, and I'll discard its present contents.");
+ box_error(n);
+ }
+}
+
+@ \TeX\ is not always in vertical mode at the time |build_page|
+is called; the current mode reflects what \TeX\ should return to, after
+the contribution list has been emptied. A call on |build_page| should
+be immediately followed by `|goto big_switch|', which is \TeX's central
+control point.
+
+@p @t\4@>@<Declare the procedure called |fire_up|@>@;@/
+static void build_page(void) /*append contributions to the current page*/
+{@+
+pointer p; /*the node being appended*/
+pointer @!q, @!r; /*nodes being examined*/
+int @!b, @!c; /*badness and cost of current page*/
+int @!pi; /*penalty to be added to the badness*/
+int @!n; /*insertion box number*/
+scaled @!delta, @!h, @!w; /*sizes used for insertion calculations*/
+@<Local variables to save the profiling context@>@;
+if ((link(contrib_head)==null)||output_active) return;
+@<Charge the time used here on |build_page|@>@;
+@/do@+{resume: p=link(contrib_head);@/
+@<Update the values of |last_glue|, |last_penalty|, and |last_kern|@>;
+@<Move node |p| to the current page; if it is time for a page break, put the
+nodes following the break back onto the contribution list, and |return| to
+the user's output routine if there is one@>;
+}@+ while (!(link(contrib_head)==null));
+@<Make the contribution list empty by setting its tail to |contrib_head|@>;
+@<restore the previous current file, line, and command@>@;
+}
+
+@ @d contrib_tail nest[0].tail_field /*tail of the contribution list*/
+
+@<Make the contribution list empty...@>=
+if (nest_ptr==0) tail=contrib_head; /*vertical mode*/
+else contrib_tail=contrib_head /*other modes*/
+
+@ @<Update the values of |last_glue|...@>=
+if (last_glue!=max_halfword) delete_glue_ref(last_glue);
+last_penalty=0;last_kern=0;
+last_node_type=type(p)+1;
+if (type(p)==glue_node)
+ {@+last_glue=glue_ptr(p);add_glue_ref(last_glue);
+ }
+else{@+last_glue=max_halfword;
+ if (type(p)==penalty_node) last_penalty=penalty(p);
+ else if (type(p)==kern_node) last_kern=width(p);
+ }
+
+@ The code here is an example of a many-way switch into routines that
+merge together in different places. Some people call this unstructured
+programming, but the author doesn't see much wrong with it, as long as
+@^Knuth, Donald Ervin@>
+the various labels have a well-understood meaning.
+
+@<Move node |p| to the current page;...@>=
+@<If the current page is empty and node |p| is to be deleted, |goto done1|;
+otherwise use node |p| to update the state of the current page; if this node
+is an insertion, |goto contribute|; otherwise if this node is not a legal
+breakpoint, |goto contribute| or |update_heights|; otherwise set |pi| to the
+penalty associated with this breakpoint@>;
+@<Check if node |p| is a new champion breakpoint; then \(if)if it is time
+for a page break, prepare for output, and either fire up the user's output
+routine and |return| or ship out the page and |goto done|@>;
+if ((type(p) < glue_node)||(type(p) > kern_node)) goto contribute;
+update_heights: @<Update the current page measurements with respect to the
+glue or kern specified by node~|p|@>;
+contribute: @<Make sure that |page_max_depth| is not exceeded@>;
+@<Link node |p| into the current page and |goto done|@>;
+done1: @<Recycle node |p|@>;
+done:
+
+@ @<Link node |p| into the current page and |goto done|@>=
+link(page_tail)=p;page_tail=p;
+link(contrib_head)=link(p);link(p)=null;goto done
+
+@ @<Recycle node |p|@>=
+link(contrib_head)=link(p);link(p)=null;
+if (saving_vdiscards > 0)
+ {@+if (page_disc==null) page_disc=p;@+else link(tail_page_disc)=p;
+ tail_page_disc=p;
+ }
+else flush_node_list(p)
+
+@ The title of this section is already so long, it seems best to avoid
+making it more accurate but still longer, by mentioning the fact that a
+kern node at the end of the contribution list will not be contributed until
+we know its successor.
+
+@<If the current page is empty...@>=
+switch (type(p)) {
+case hlist_node: case vlist_node: case rule_node: if (page_contents < box_there)
+ @<Initialize the current page, insert the \.{\\topskip} glue ahead of
+|p|, and |goto resume|@>@;
+ else@<Prepare to move a box or rule node to the current page, then |goto
+contribute|@>@;@+break;
+case whatsit_node: @<Prepare to move whatsit |p| to the current page, then
+|goto contribute|@>;
+case glue_node: if (page_contents < box_there) goto done1;
+ else if (precedes_break(page_tail)) pi=0;
+ else goto update_heights;@+break;
+case kern_node: if (page_contents < box_there) goto done1;
+ else if (link(p)==null) return;
+ else if (type(link(p))==glue_node) pi=0;
+ else goto update_heights;@+break;
+case penalty_node: if (page_contents < box_there) goto done1;@+else pi=penalty(p);@+break;
+case mark_node: goto contribute;
+case ins_node: @<Append an insertion to the current page and |goto contribute|@>@;
+default:confusion("page");
+@:this can't happen page}{\quad page@>
+}
+
+@ @<Initialize the current page, insert the \.{\\topskip} glue...@>=
+{@+if (page_contents==empty) freeze_page_specs(box_there);
+else page_contents=box_there;
+q=new_skip_param(top_skip_code); /*now |temp_ptr==glue_ptr(q)|*/
+if (width(temp_ptr) > height(p)) width(temp_ptr)=width(temp_ptr)-height(p);
+else width(temp_ptr)=0;
+link(q)=p;link(contrib_head)=q;goto resume;
+}
+
+@ @<Prepare to move a box or rule node to the current page...@>=
+{@+page_total=page_total+page_depth+height(p);
+page_depth=depth(p);
+goto contribute;
+}
+
+@ @<Make sure that |page_max_depth| is not exceeded@>=
+if (page_depth > page_max_depth)
+ {@+page_total=@|
+ page_total+page_depth-page_max_depth;@/
+ page_depth=page_max_depth;
+ }
+
+@ @<Update the current page measurements with respect to the glue...@>=
+if (type(p)==kern_node) q=p;
+else{@+q=glue_ptr(p);
+ page_so_far[2+stretch_order(q)]=@|
+ page_so_far[2+stretch_order(q)]+stretch(q);@/
+ page_shrink=page_shrink+shrink(q);
+ if ((shrink_order(q)!=normal)&&(shrink(q)!=0))
+ {@+@t@>@;@/
+ print_err("Infinite glue shrinkage found on current page");@/
+@.Infinite glue shrinkage...@>
+ help4("The page about to be output contains some infinitely",@/
+ "shrinkable glue, e.g., `\\vss' or `\\vskip 0pt minus 1fil'.",@/
+ "Such glue doesn't belong there; but you can safely proceed,",@/
+ "since the offensive shrinkability has been made finite.");
+ error();
+ r=new_spec(q);shrink_order(r)=normal;delete_glue_ref(q);
+ glue_ptr(p)=r;q=r;
+ }
+ }
+page_total=page_total+page_depth+width(q);page_depth=0
+
+@ @<Check if node |p| is a new champion breakpoint; then \(if)...@>=
+if (pi < inf_penalty)
+ {@+@<Compute the badness, |b|, of the current page, using |awful_bad| if
+the box is too full@>;
+ if (b < awful_bad)
+ if (pi <= eject_penalty) c=pi;
+ else if (b < inf_bad) c=b+pi+insert_penalties;
+ else c=deplorable;
+ else c=b;
+ if (insert_penalties >= 10000) c=awful_bad;
+#ifdef @!STAT
+ if (tracing_pages > 0) @<Display the page break cost@>;
+#endif
+@;@/
+ if (c <= least_page_cost)
+ {@+best_page_break=p;best_size=page_goal;
+ least_page_cost=c;
+ r=link(page_ins_head);
+ while (r!=page_ins_head)
+ {@+best_ins_ptr(r)=last_ins_ptr(r);
+ r=link(r);
+ }
+ }
+ if ((c==awful_bad)||(pi <= eject_penalty))
+ {@+fire_up(p); /*output the current page at the best place*/
+ if (output_active)
+ { @<restore the previous current file, line, and command@>@;
+ return; /*user's output routine will act*/
+ }
+ goto done; /*the page has been shipped out by default output routine*/
+ }
+ }
+
+@ @<Display the page break cost@>=
+{@+begin_diagnostic();print_nl("%");
+print(" t=");print_totals();@/
+print(" g=");print_scaled(page_goal);@/
+print(" b=");
+if (b==awful_bad) print_char('*');@+else print_int(b);
+@.*\relax@>
+print(" p=");print_int(pi);
+print(" c=");
+if (c==awful_bad) print_char('*');@+else print_int(c);
+if (c <= least_page_cost) print_char('#');
+end_diagnostic(false);
+}
+
+@ @<Compute the badness, |b|, of the current page...@>=
+if (page_total < page_goal)
+ if ((page_so_far[3]!=0)||(page_so_far[4]!=0)||@|
+ (page_so_far[5]!=0)) b=0;
+ else b=badness(page_goal-page_total, page_so_far[2]);
+else if (page_total-page_goal > page_shrink) b=awful_bad;
+else b=badness(page_total-page_goal, page_shrink)
+
+@ @<Append an insertion to the current page and |goto contribute|@>=
+{@+if (page_contents==empty) freeze_page_specs(inserts_only);
+n=subtype(p);r=page_ins_head;
+while (n >= subtype(link(r))) r=link(r);
+n=qo(n);
+if (subtype(r)!=qi(n))
+ @<Create a page insertion node with |subtype(r)=qi(n)|, and include the
+glue correction for box |n| in the current page state@>;
+if (type(r)==split_up) insert_penalties=insert_penalties+float_cost(p);
+else{@+last_ins_ptr(r)=p;
+ delta=page_goal-page_total-page_depth+page_shrink;
+ /*this much room is left if we shrink the maximum*/
+ if (count(n)==1000) h=height(p);
+ else h=x_over_n(height(p), 1000)*count(n); /*this much room is needed*/
+ if (((h <= 0)||(h <= delta))&&(height(p)+height(r) <= dimen(n)))
+ {@+page_goal=page_goal-h;height(r)=height(r)+height(p);
+ }
+ else@<Find the best way to split the insertion, and change |type(r)| to
+|split_up|@>;
+ }
+goto contribute;
+}
+
+@ We take note of the value of \.{\\skip} |n| and the height plus depth
+of \.{\\box}~|n| only when the first \.{\\insert}~|n| node is
+encountered for a new page. A user who changes the contents of \.{\\box}~|n|
+after that first \.{\\insert}~|n| had better be either extremely careful
+or extremely lucky, or both.
+
+@<Create a page insertion node...@>=
+{@+q=get_node(page_ins_node_size);link(q)=link(r);link(r)=q;r=q;
+subtype(r)=qi(n);type(r)=inserting;ensure_vbox(n);
+if (box(n)==null) height(r)=0;
+else height(r)=height(box(n))+depth(box(n));
+best_ins_ptr(r)=null;@/
+q=skip(n);
+if (count(n)==1000) h=height(r);
+else h=x_over_n(height(r), 1000)*count(n);
+page_goal=page_goal-h-width(q);@/
+page_so_far[2+stretch_order(q)]=@|page_so_far[2+stretch_order(q)]+stretch(q);@/
+page_shrink=page_shrink+shrink(q);
+if ((shrink_order(q)!=normal)&&(shrink(q)!=0))
+ {@+print_err("Infinite glue shrinkage inserted from ");print_esc("skip");
+@.Infinite glue shrinkage...@>
+ print_int(n);
+ help3("The correction glue for page breaking with insertions",@/
+ "must have finite shrinkability. But you may proceed,",@/
+ "since the offensive shrinkability has been made finite.");
+ error();
+ }
+}
+
+@ Here is the code that will split a long footnote between pages, in an
+emergency. The current situation deserves to be recapitulated: Node |p|
+is an insertion into box |n|; the insertion will not fit, in its entirety,
+either because it would make the total contents of box |n| greater than
+\.{\\dimen} |n|, or because it would make the incremental amount of growth
+|h| greater than the available space |delta|, or both. (This amount |h| has
+been weighted by the insertion scaling factor, i.e., by \.{\\count} |n|
+over 1000.) Now we will choose the best way to break the vlist of the
+insertion, using the same criteria as in the \.{\\vsplit} operation.
+
+@<Find the best way to split the insertion...@>=
+{@+if (count(n) <= 0) w=max_dimen;
+else{@+w=page_goal-page_total-page_depth;
+ if (count(n)!=1000) w=x_over_n(w, count(n))*1000;
+ }
+if (w > dimen(n)-height(r)) w=dimen(n)-height(r);
+q=vert_break(ins_ptr(p), w, depth(p));
+height(r)=height(r)+best_height_plus_depth;
+#ifdef @!STAT
+if (tracing_pages > 0) @<Display the insertion split cost@>;
+#endif
+@;@/
+if (count(n)!=1000)
+ best_height_plus_depth=x_over_n(best_height_plus_depth, 1000)*count(n);
+page_goal=page_goal-best_height_plus_depth;
+type(r)=split_up;broken_ptr(r)=q;broken_ins(r)=p;
+if (q==null) insert_penalties=insert_penalties+eject_penalty;
+else if (type(q)==penalty_node) insert_penalties=insert_penalties+penalty(q);
+}
+
+@ @<Display the insertion split cost@>=
+{@+begin_diagnostic();print_nl("% split");print_int(n);
+@.split@>
+print(" to ");print_scaled(w);
+print_char(',');print_scaled(best_height_plus_depth);@/
+print(" p=");
+if (q==null) print_int(eject_penalty);
+else if (type(q)==penalty_node) print_int(penalty(q));
+else print_char('0');
+end_diagnostic(false);
+}
+
+@ When the page builder has looked at as much material as could appear before
+the next page break, it makes its decision. The break that gave minimum
+badness will be used to put a completed ``page'' into box 255, with insertions
+appended to their other boxes.
+
+We also set the values of |top_mark|, |first_mark|, and |bot_mark|. The
+program uses the fact that |bot_mark!=null| implies |first_mark!=null|;
+it also knows that |bot_mark==null| implies |top_mark==first_mark==null|.
+
+The |fire_up| subroutine prepares to output the current page at the best
+place; then it fires up the user's output routine, if there is one,
+or it simply ships out the page. There is one parameter, |c|, which represents
+the node that was being contributed to the page when the decision to
+force an output was made.
+
+@<Declare the procedure called |fire_up|@>=
+static void fire_up(pointer @!c)
+{@+
+pointer p, @!q, @!r, @!s; /*nodes being examined and/or changed*/
+pointer @!prev_p; /*predecessor of |p|*/
+int @!n; /*insertion box number*/
+bool @!wait; /*should the present insertion be held over?*/
+int @!save_vbadness; /*saved value of |vbadness|*/
+scaled @!save_vfuzz; /*saved value of |vfuzz|*/
+pointer @!save_split_top_skip; /*saved value of |split_top_skip|*/
+@<Set the value of |output_penalty|@>;
+if (sa_mark!=null)
+ if (do_marks(fire_up_init, 0, sa_mark)) sa_mark=null;
+if (bot_mark!=null)
+ {@+if (top_mark!=null) delete_token_ref(top_mark);
+ top_mark=bot_mark;add_token_ref(top_mark);
+ delete_token_ref(first_mark);first_mark=null;
+ }
+@<Put the \(o)optimal current page into box 255, update |first_mark| and |bot_mark|,
+append insertions to their boxes, and put the remaining nodes back on the
+contribution list@>;
+if (sa_mark!=null)
+ if (do_marks(fire_up_done, 0, sa_mark)) sa_mark=null;
+if ((top_mark!=null)&&(first_mark==null))
+ {@+first_mark=top_mark;add_token_ref(top_mark);
+ }
+if (output_routine!=null)
+ if (dead_cycles >= max_dead_cycles)
+ @<Explain that too many dead cycles have occurred in a row@>@;
+ else@<Fire up the user's output routine and |return|@>;
+@<Perform the default output routine@>;
+}
+
+@ @<Set the value of |output_penalty|@>=
+if (type(best_page_break)==penalty_node)
+ {@+geq_word_define(int_base+output_penalty_code, penalty(best_page_break));
+ penalty(best_page_break)=inf_penalty;
+ }
+else geq_word_define(int_base+output_penalty_code, inf_penalty)
+
+@ As the page is finally being prepared for output,
+pointer |p| runs through the vlist, with |prev_p| trailing behind;
+pointer |q| is the tail of a list of insertions that
+are being held over for a subsequent page.
+
+@<Put the \(o)optimal current page into box 255...@>=
+if (c==best_page_break) best_page_break=null; /*|c| not yet linked in*/
+@<Ensure that box 255 is empty before output@>;
+insert_penalties=0; /*this will count the number of insertions held over*/
+save_split_top_skip=split_top_skip;
+if (holding_inserts <= 0)
+ @<Prepare all the boxes involved in insertions to act as queues@>;
+q=hold_head;link(q)=null;prev_p=page_head;p=link(prev_p);
+while (p!=best_page_break)
+ {@+if (type(p)==ins_node)
+ {@+if (holding_inserts <= 0)
+ @<Either insert the material specified by node |p| into the appropriate
+box, or hold it for the next page; also delete node |p| from the current page@>;
+ }
+ else if (type(p)==mark_node)
+ if (mark_class(p)!=0) @<Update the current marks for |fire_up|@>@;
+ else@<Update the values of |first_mark| and |bot_mark|@>;
+ prev_p=p;p=link(prev_p);
+ }
+split_top_skip=save_split_top_skip;
+@<Break the current page at node |p|, put it in box~255, and put the remaining
+nodes on the contribution list@>;
+@<Delete \(t)the page-insertion nodes@>@;
+
+@ @<Ensure that box 255 is empty before output@>=
+if (box(255)!=null)
+ {@+print_err("");print_esc("box");print("255 is not void");
+@:box255}{\.{\\box255 is not void}@>
+ help2("You shouldn't use \\box255 except in \\output routines.",@/
+ "Proceed, and I'll discard its present contents.");
+ box_error(255);
+ }
+
+@ @<Update the values of |first_mark| and |bot_mark|@>=
+{@+if (first_mark==null)
+ {@+first_mark=mark_ptr(p);
+ add_token_ref(first_mark);
+ }
+if (bot_mark!=null) delete_token_ref(bot_mark);
+bot_mark=mark_ptr(p);add_token_ref(bot_mark);
+}
+
+@ When the following code is executed, the current page runs from node
+|link(page_head)| to node |prev_p|, and the nodes from |p| to |page_tail|
+are to be placed back at the front of the contribution list. Furthermore
+the heldover insertions appear in a list from |link(hold_head)| to |q|; we
+will put them into the current page list for safekeeping while the user's
+output routine is active. We might have |q==hold_head|; and |p==null| if
+and only if |prev_p==page_tail|. Error messages are suppressed within
+|vpackage|, since the box might appear to be overfull or underfull simply
+because the stretch and shrink from the \.{\\skip} registers for inserts
+are not actually present in the box.
+
+@<Break the current page at node |p|, put it...@>=
+if (p!=null)
+ {@+if (link(contrib_head)==null)
+ if (nest_ptr==0) tail=page_tail;
+ else contrib_tail=page_tail;
+ link(page_tail)=link(contrib_head);
+ link(contrib_head)=p;
+ link(prev_p)=null;
+ }
+save_vbadness=vbadness;vbadness=inf_bad;
+save_vfuzz=vfuzz;vfuzz=max_dimen; /*inhibit error messages*/
+box(255)=vpackage(link(page_head), best_size, exactly, page_max_depth);
+vbadness=save_vbadness;vfuzz=save_vfuzz;
+if (last_glue!=max_halfword) delete_glue_ref(last_glue);
+@<Start a new current page@>; /*this sets |last_glue=max_halfword|*/
+if (q!=hold_head)
+ {@+link(page_head)=link(hold_head);page_tail=q;
+ }
+
+@ If many insertions are supposed to go into the same box, we want to know
+the position of the last node in that box, so that we don't need to waste time
+when linking further information into it. The |last_ins_ptr| fields of the
+page insertion nodes are therefore used for this purpose during the
+packaging phase.
+
+@<Prepare all the boxes involved in insertions to act as queues@>=
+{@+r=link(page_ins_head);
+while (r!=page_ins_head)
+ {@+if (best_ins_ptr(r)!=null)
+ {@+n=qo(subtype(r));ensure_vbox(n);
+ if (box(n)==null) box(n)=new_null_box();
+ p=box(n)+list_offset;
+ while (link(p)!=null) p=link(p);
+ last_ins_ptr(r)=p;
+ }
+ r=link(r);
+ }
+}
+
+@ @<Delete \(t)the page-insertion nodes@>=
+r=link(page_ins_head);
+while (r!=page_ins_head)
+ {@+q=link(r);free_node(r, page_ins_node_size);r=q;
+ }
+link(page_ins_head)=page_ins_head
+
+@ We will set |best_ins_ptr=null| and package the box corresponding to
+insertion node~|r|, just after making the final insertion into that box.
+If this final insertion is `|split_up|', the remainder after splitting
+and pruning (if any) will be carried over to the next page.
+
+@<Either insert the material specified by node |p| into...@>=
+{@+r=link(page_ins_head);
+while (subtype(r)!=subtype(p)) r=link(r);
+if (best_ins_ptr(r)==null) wait=true;
+else{@+wait=false;s=last_ins_ptr(r);link(s)=ins_ptr(p);
+ if (best_ins_ptr(r)==p)
+ @<Wrap up the box specified by node |r|, splitting node |p| if called
+for; set |wait:=true| if node |p| holds a remainder after splitting@>@;
+ else{@+while (link(s)!=null) s=link(s);
+ last_ins_ptr(r)=s;
+ }
+ }
+@<Either append the insertion node |p| after node |q|, and remove it from
+the current page, or delete |node(p)|@>;
+}
+
+@ @<Wrap up the box specified by node |r|, splitting node |p| if...@>=
+{@+if (type(r)==split_up)
+ if ((broken_ins(r)==p)&&(broken_ptr(r)!=null))
+ {@+while (link(s)!=broken_ptr(r)) s=link(s);
+ link(s)=null;
+ split_top_skip=split_top_ptr(p);
+ ins_ptr(p)=prune_page_top(broken_ptr(r), false);
+ if (ins_ptr(p)!=null)
+ {@+temp_ptr=vpack(ins_ptr(p), natural);
+ height(p)=height(temp_ptr)+depth(temp_ptr);
+ free_node(temp_ptr, box_node_size);wait=true;
+ }
+ }
+best_ins_ptr(r)=null;
+n=qo(subtype(r));
+temp_ptr=list_ptr(box(n));
+free_node(box(n), box_node_size);
+box(n)=vpack(temp_ptr, natural);
+}
+
+@ @<Either append the insertion node |p|...@>=
+link(prev_p)=link(p);link(p)=null;
+if (wait)
+ {@+link(q)=p;q=p;incr(insert_penalties);
+ }
+else{@+delete_glue_ref(split_top_ptr(p));
+ free_node(p, ins_node_size);
+ }
+p=prev_p
+
+@ The list of heldover insertions, running from |link(page_head)| to
+|page_tail|, must be moved to the contribution list when the user has
+specified no output routine.
+
+@<Perform the default output routine@>=
+{@+if (link(page_head)!=null)
+ {@+if (link(contrib_head)==null)
+ if (nest_ptr==0) tail=page_tail;@+else contrib_tail=page_tail;
+ else link(page_tail)=link(contrib_head);
+ link(contrib_head)=link(page_head);
+ link(page_head)=null;page_tail=page_head;
+ }
+flush_node_list(page_disc);page_disc=null;
+ship_out(box(255));box(255)=null;
+}
+
+@ @<Explain that too many dead cycles have occurred in a row@>=
+{@+print_err("Output loop---");print_int(dead_cycles);
+@.Output loop...@>
+print(" consecutive dead cycles");
+help3("I've concluded that your \\output is awry; it never does a",@/
+"\\shipout, so I'm shipping \\box255 out myself. Next time",@/
+"increase \\maxdeadcycles if you want me to be more patient!");error();
+}
+
+@ @<Fire up the user's output routine and |return|@>=
+{@+output_active=true;
+incr(dead_cycles);
+push_nest();mode=-vmode;prev_depth=ignore_depth;mode_line=-line;
+begin_token_list(output_routine, output_text);
+new_save_level(output_group);normal_paragraph();
+scan_left_brace();
+return;
+}
+
+@ When the user's output routine finishes, it has constructed a vlist
+in internal vertical mode, and \TeX\ will do the following:
+
+@<Resume the page builder after an output routine has come to an end@>=
+{@+if ((loc!=null)||
+ ((token_type!=output_text)&&(token_type!=backed_up)))
+ @<Recover from an unbalanced output routine@>;
+end_token_list(); /*conserve stack space in case more outputs are triggered*/
+end_graf();unsave();output_active=false;insert_penalties=0;@/
+@<Ensure that box 255 is empty after output@>;
+if (tail!=head) /*current list goes after heldover insertions*/
+ {@+link(page_tail)=link(head);
+ page_tail=tail;
+ }
+if (link(page_head)!=null) /*and both go before heldover contributions*/
+ {@+if (link(contrib_head)==null) contrib_tail=page_tail;
+ link(page_tail)=link(contrib_head);
+ link(contrib_head)=link(page_head);
+ link(page_head)=null;page_tail=page_head;
+ }
+flush_node_list(page_disc);page_disc=null;
+pop_nest();build_page();
+}
+
+@ @<Recover from an unbalanced output routine@>=
+{@+print_err("Unbalanced output routine");
+@.Unbalanced output routine@>
+help2("Your sneaky output routine has problematic {'s and/or }'s.",@/
+"I can't handle that very well; good luck.");error();
+@/do@+{get_token();
+}@+ while (!(loc==null));
+} /*loops forever if reading from a file, since |null==min_halfword <= 0|*/
+
+@ @<Ensure that box 255 is empty after output@>=
+if (box(255)!=null)
+ {@+print_err("Output routine didn't use all of ");
+ print_esc("box");print_int(255);
+@.Output routine didn't use...@>
+ help3("Your \\output commands should empty \\box255,",@/
+ "e.g., by saying `\\shipout\\box255'.",@/
+ "Proceed; I'll discard its present contents.");
+ box_error(255);
+ }
+
+@* The chief executive.
+We come now to the |main_control| routine, which contains the master
+switch that causes all the various pieces of \TeX\ to do their things,
+in the right order.
+
+In a sense, this is the grand climax of the program: It applies all the
+tools that we have worked so hard to construct. In another sense, this is
+the messiest part of the program: It necessarily refers to other pieces
+of code all over the place, so that a person can't fully understand what is
+going on without paging back and forth to be reminded of conventions that
+are defined elsewhere. We are now at the hub of the web, the central nervous
+system that touches most of the other parts and ties them together.
+@^brain@>
+
+The structure of |main_control| itself is quite simple. There's a label
+called |big_switch|, at which point the next token of input is fetched
+using |get_x_token|. Then the program branches at high speed into one of
+about 100 possible directions, based on the value of the current
+mode and the newly fetched command code; the sum |abs(mode)+cur_cmd|
+indicates what to do next. For example, the case `|vmode+letter|' arises
+when a letter occurs in vertical mode (or internal vertical mode); this
+case leads to instructions that initialize a new paragraph and enter
+horizontal mode.
+
+The big |case| statement that contains this multiway switch has been labeled
+|reswitch|, so that the program can |goto reswitch| when the next token
+has already been fetched. Most of the cases are quite short; they call
+an ``action procedure'' that does the work for that case, and then they
+either |goto reswitch| or they ``fall through'' to the end of the |case|
+statement, which returns control back to |big_switch|. Thus, |main_control|
+is not an extremely large procedure, in spite of the multiplicity of things
+it must do; it is small enough to be handled by \PASCAL\ compilers that put
+severe restrictions on procedure size.
+@!@^action procedure@>
+
+One case is singled out for special treatment, because it accounts for most
+of \TeX's activities in typical applications. The process of reading simple
+text and converting it into |char_node| records, while looking for ligatures
+and kerns, is part of \TeX's ``inner loop''; the whole program runs
+efficiently when its inner loop is fast, so this part has been written
+with particular care.
+
+@ We shall concentrate first on the inner loop of |main_control|, deferring
+consideration of the other cases until later.
+@^inner loop@>
+
+@p @t\4@>@<Declare action procedures for use by |main_control|@>@;
+@t\4@>@<Declare the procedure called |handle_right_brace|@>@;
+static void main_control(void) /*governs \TeX's activities*/
+{@+
+int @!t; /*general-purpose temporary variable*/
+@<Initialize profiling@>@;
+if (every_job!=null) begin_token_list(every_job, every_job_text);
+big_switch:
+ @<record timing information@>@;
+ get_x_token();@/
+big_reswitch:
+ @<set current file, line, and command for the current time slot@>@;
+@<Give diagnostic information, if requested@>;
+switch (abs(mode)+cur_cmd) {
+case hmode+letter: case hmode+other_char:
+ case hmode+char_given: goto main_loop;
+case hmode+char_num: {@+scan_char_num();cur_chr=cur_val;goto main_loop;@+}
+case hmode+no_boundary: {@+get_x_token();
+ if ((cur_cmd==letter)||(cur_cmd==other_char)||(cur_cmd==char_given)||
+ (cur_cmd==char_num)) cancel_boundary=true;
+ goto big_reswitch;
+ }
+case hmode+spacer: if (space_factor==1000) goto append_normal_space;
+ else app_space();@+break;
+case hmode+ex_space: case mmode+ex_space: goto append_normal_space;
+@t\4@>@<Cases of |main_control| that are not part of the inner loop@>@;
+} /*of the big |case| statement*/
+goto big_switch;
+main_loop:
+@<record timing information@>@;
+@<set current file, line, and command for the current time slot@>@;
+@<Append character |cur_chr| and the following characters (if~any)
+to the current hlist in the current font; |goto big_reswitch| when a non-character
+has been fetched@>;
+append_normal_space: @<Append a normal inter-word space to the current list,
+then |goto big_switch|@>;
+}
+
+@ When a new token has just been fetched at |big_switch|, we have an
+ideal place to monitor \TeX's activity.
+@^debugging@>
+
+@<Give diagnostic information, if requested@>=
+if (interrupt!=0) if (OK_to_interrupt)
+ {@+back_input();check_interrupt;goto big_switch;
+ }
+#ifdef @!DEBUG
+if (panicking) check_mem(false);@;
+#endif
+if (tracing_commands > 0) show_cur_cmd_chr()
+
+@ The following part of the program was first written in a structured
+manner, according to the philosophy that ``premature optimization is
+the root of all evil.'' Then it was rearranged into pieces of
+spaghetti so that the most common actions could proceed with little or
+no redundancy.
+
+The original unoptimized form of this algorithm resembles the
+|reconstitute| procedure, which was described earlier in connection with
+hyphenation. Again we have an implied ``cursor'' between characters
+|cur_l| and |cur_r|. The main difference is that the |lig_stack| can now
+contain a charnode as well as pseudo-ligatures; that stack is now
+usually nonempty, because the next character of input (if any) has been
+appended to it. In |main_control| we have
+$$|cur_r|=\cases{|character(lig_stack)|,&if |lig_stack > null|;\cr
+ |font_bchar[cur_font]|,&otherwise;\cr}$$
+except when |character(lig_stack)==font_false_bchar[cur_font]|.
+Several additional global variables are needed.
+
+@<Glob...@>=
+static internal_font_number @!main_f; /*the current font*/
+static four_quarters @!main_i; /*character information bytes for |cur_l|*/
+static four_quarters @!main_j; /*ligature/kern command*/
+static font_index @!main_k; /*index into |font_info|*/
+static pointer @!main_p; /*temporary register for list manipulation*/
+static int @!main_s; /*space factor value*/
+static halfword @!bchar; /*boundary character of current font, or |non_char|*/
+static halfword @!false_bchar; /*nonexistent character matching |bchar|, or |non_char|*/
+static bool @!cancel_boundary; /*should the left boundary be ignored?*/
+static bool @!ins_disc; /*should we insert a discretionary node?*/
+
+@ The boolean variables of the main loop are normally false, and always reset
+to false before the loop is left. That saves us the extra work of initializing
+each time.
+
+@<Set init...@>=
+ligature_present=false;cancel_boundary=false;lft_hit=false;rt_hit=false;
+ins_disc=false;
+
+@ We leave the |space_factor| unchanged if |sf_code(cur_chr)==0|; otherwise we
+set it equal to |sf_code(cur_chr)|, except that it should never change
+from a value less than 1000 to a value exceeding 1000. The most common
+case is |sf_code(cur_chr)==1000|, so we want that case to be fast.
+
+The overall structure of the main loop is presented here. Some program labels
+are inside the individual sections.
+@^inner loop@>
+
+@d adjust_space_factor @t@>@;@/
+ main_s=sf_code(cur_chr);
+ if (main_s==1000) space_factor=1000;
+ else if (main_s < 1000)
+ {@+if (main_s > 0) space_factor=main_s;
+ }
+ else if (space_factor < 1000) space_factor=1000;
+ else space_factor=main_s
+
+@<Append character |cur_chr|...@>=
+adjust_space_factor;@/
+main_f=cur_font;
+bchar=font_bchar[main_f];false_bchar=font_false_bchar[main_f];
+if (mode > 0) if (language!=clang) fix_language();
+fast_get_avail(lig_stack);font(lig_stack)=main_f;cur_l=qi(cur_chr);
+character(lig_stack)=cur_l;@/
+cur_q=tail;
+if (cancel_boundary)
+ {@+cancel_boundary=false;main_k=non_address;
+ }
+else main_k=bchar_label[main_f];
+if (main_k==non_address) goto main_loop_move2; /*no left boundary processing*/
+cur_r=cur_l;cur_l=non_char;
+goto main_lig_loop1; /*begin with cursor after left boundary*/
+@#
+main_loop_wrapup: @<Make a ligature node, if |ligature_present|; insert a
+null discretionary, if appropriate@>;
+main_loop_move: @<If the cursor is immediately followed by the right boundary,
+|goto big_reswitch|; if it's followed by an invalid character, |goto big_switch|;
+otherwise move the cursor one step to the right and |goto main_lig_loop|@>;
+main_loop_lookahead: @<Look ahead for another character, or leave |lig_stack|
+empty if there's none there@>;
+main_lig_loop: @<If there's a ligature/kern command relevant to |cur_l| and
+|cur_r|, adjust the text appropriately; exit to |main_loop_wrapup|@>;
+main_loop_move_lig: @<Move the cursor past a pseudo-ligature, then |goto main_loop_lookahead|
+or |main_lig_loop|@>@;
+
+@ If |link(cur_q)| is nonnull when |wrapup| is invoked, |cur_q| points to
+the list of characters that were consumed while building the ligature
+character~|cur_l|.
+
+A discretionary break is not inserted for an explicit hyphen when we are in
+restricted horizontal mode. In particular, this avoids putting discretionary
+nodes inside of other discretionaries.
+@^inner loop@>
+
+@d pack_lig(X) /*the parameter is either |rt_hit| or |false|*/
+ {@+main_p=new_ligature(main_f, cur_l, link(cur_q));
+ if (lft_hit)
+ {@+subtype(main_p)=2;lft_hit=false;
+ }
+ if (X) if (lig_stack==null)
+ {@+incr(subtype(main_p));rt_hit=false;
+ }
+ link(cur_q)=main_p;tail=main_p;ligature_present=false;
+ }
+
+@d wrapup(A) if (cur_l < non_char)
+ {@+if (link(cur_q) > null)
+ if (character(tail)==qi(hyphen_char[main_f])) ins_disc=true;
+ if (ligature_present) pack_lig(A);
+ if (ins_disc)
+ {@+ins_disc=false;
+ if (mode > 0) tail_append(new_disc());
+ }
+ }
+
+@<Make a ligature node, if |ligature_present|;...@>=
+wrapup(rt_hit)
+
+@ @<If the cursor is immediately followed by the right boundary...@>=
+@^inner loop@>
+if (lig_stack==null) goto big_reswitch;
+cur_q=tail;cur_l=character(lig_stack);
+main_loop_move1: if (!is_char_node(lig_stack)) goto main_loop_move_lig;
+main_loop_move2: if ((cur_chr < font_bc[main_f])||(cur_chr > font_ec[main_f]))
+ {@+char_warning(main_f, cur_chr);free_avail(lig_stack);goto big_switch;
+ }
+main_i=char_info(main_f, cur_l);
+if (!char_exists(main_i))
+ {@+char_warning(main_f, cur_chr);free_avail(lig_stack);goto big_switch;
+ }
+link(tail)=lig_stack;tail=lig_stack /*|main_loop_lookahead| is next*/
+
+@ Here we are at |main_loop_move_lig|.
+When we begin this code we have |cur_q==tail| and |cur_l==character(lig_stack)|.
+
+@<Move the cursor past a pseudo-ligature...@>=
+main_p=lig_ptr(lig_stack);
+if (main_p > null) tail_append(main_p); /*append a single character*/
+temp_ptr=lig_stack;lig_stack=link(temp_ptr);
+free_node(temp_ptr, small_node_size);
+main_i=char_info(main_f, cur_l);ligature_present=true;
+if (lig_stack==null)
+ if (main_p > null) goto main_loop_lookahead;
+ else cur_r=bchar;
+else cur_r=character(lig_stack);
+goto main_lig_loop
+
+@ The result of \.{\\char} can participate in a ligature or kern, so we must
+look ahead for it.
+
+@<Look ahead for another character...@>=
+get_next(); /*set only |cur_cmd| and |cur_chr|, for speed*/
+if (cur_cmd==letter) goto main_loop_lookahead1;
+if (cur_cmd==other_char) goto main_loop_lookahead1;
+if (cur_cmd==char_given) goto main_loop_lookahead1;
+x_token(); /*now expand and set |cur_cmd|, |cur_chr|, |cur_tok|*/
+if (cur_cmd==letter) goto main_loop_lookahead1;
+if (cur_cmd==other_char) goto main_loop_lookahead1;
+if (cur_cmd==char_given) goto main_loop_lookahead1;
+if (cur_cmd==char_num)
+ {@+scan_char_num();cur_chr=cur_val;goto main_loop_lookahead1;
+ }
+if (cur_cmd==no_boundary) bchar=non_char;
+cur_r=bchar;lig_stack=null;goto main_lig_loop;
+main_loop_lookahead1: adjust_space_factor;
+fast_get_avail(lig_stack);font(lig_stack)=main_f;
+cur_r=qi(cur_chr);character(lig_stack)=cur_r;
+if (cur_r==false_bchar) cur_r=non_char /*this prevents spurious ligatures*/
+
+@ Even though comparatively few characters have a lig/kern program, several
+of the instructions here count as part of \TeX's inner loop, since a
+@^inner loop@>
+potentially long sequential search must be performed. For example, tests with
+Computer Modern Roman showed that about 40 per cent of all characters
+actually encountered in practice had a lig/kern program, and that about four
+lig/kern commands were investigated for every such character.
+
+At the beginning of this code we have |main_i==char_info(main_f, cur_l)|.
+
+@<If there's a ligature/kern command...@>=
+if (char_tag(main_i)!=lig_tag) goto main_loop_wrapup;
+if (cur_r==non_char) goto main_loop_wrapup;
+main_k=lig_kern_start(main_f, main_i);main_j=font_info[main_k].qqqq;
+if (skip_byte(main_j) <= stop_flag) goto main_lig_loop2;
+main_k=lig_kern_restart(main_f, main_j);
+main_lig_loop1: main_j=font_info[main_k].qqqq;
+main_lig_loop2: if (next_char(main_j)==cur_r)
+ if (skip_byte(main_j) <= stop_flag)
+ @<Do ligature or kern command, returning to |main_lig_loop| or |main_loop_wrapup|
+or |main_loop_move|@>;
+if (skip_byte(main_j)==qi(0)) incr(main_k);
+else{@+if (skip_byte(main_j) >= stop_flag) goto main_loop_wrapup;
+ main_k=main_k+qo(skip_byte(main_j))+1;
+ }
+goto main_lig_loop1
+
+@ When a ligature or kern instruction matches a character, we know from
+|read_font_info| that the character exists in the font, even though we
+haven't verified its existence in the normal way.
+
+This section could be made into a subroutine, if the code inside
+|main_control| needs to be shortened.
+
+\chardef\@@='174 % vertical line to indicate character retention
+
+@<Do ligature or kern command...@>=
+{@+if (op_byte(main_j) >= kern_flag)
+ {@+wrapup(rt_hit);
+ tail_append(new_kern(char_kern(main_f, main_j)));goto main_loop_move;
+ }
+if (cur_l==non_char) lft_hit=true;
+else if (lig_stack==null) rt_hit=true;
+check_interrupt; /*allow a way out in case there's an infinite ligature loop*/
+switch (op_byte(main_j)) {
+case qi(1): case qi(5): {@+cur_l=rem_byte(main_j); /*\.{=:\@@}, \.{=:\@@>}*/
+ main_i=char_info(main_f, cur_l);ligature_present=true;
+ } @+break;
+case qi(2): case qi(6): {@+cur_r=rem_byte(main_j); /*\.{\@@=:}, \.{\@@=:>}*/
+ if (lig_stack==null) /*right boundary character is being consumed*/
+ {@+lig_stack=new_lig_item(cur_r);bchar=non_char;
+ }
+ else if (is_char_node(lig_stack)) /*|link(lig_stack)==null|*/
+ {@+main_p=lig_stack;lig_stack=new_lig_item(cur_r);
+ lig_ptr(lig_stack)=main_p;
+ }
+ else character(lig_stack)=cur_r;
+ } @+break;
+case qi(3): {@+cur_r=rem_byte(main_j); /*\.{\@@=:\@@}*/
+ main_p=lig_stack;lig_stack=new_lig_item(cur_r);
+ link(lig_stack)=main_p;
+ } @+break;
+case qi(7): case qi(11): {@+wrapup(false); /*\.{\@@=:\@@>}, \.{\@@=:\@@>>}*/
+ cur_q=tail;cur_l=rem_byte(main_j);
+ main_i=char_info(main_f, cur_l);ligature_present=true;
+ } @+break;
+default:{@+cur_l=rem_byte(main_j);ligature_present=true; /*\.{=:}*/
+ if (lig_stack==null) goto main_loop_wrapup;
+ else goto main_loop_move1;
+ }
+}
+if (op_byte(main_j) > qi(4))
+ if (op_byte(main_j)!=qi(7)) goto main_loop_wrapup;
+if (cur_l < non_char) goto main_lig_loop;
+main_k=bchar_label[main_f];goto main_lig_loop1;
+}
+
+@ The occurrence of blank spaces is almost part of \TeX's inner loop,
+@^inner loop@>
+since we usually encounter about one space for every five non-blank characters.
+Therefore |main_control| gives second-highest priority to ordinary spaces.
+
+When a glue parameter like \.{\\spaceskip} is set to `\.{0pt}', we will
+see to it later that the corresponding glue specification is precisely
+|zero_glue|, not merely a pointer to some specification that happens
+to be full of zeroes. Therefore it is simple to test whether a glue parameter
+is zero or~not.
+
+@<Append a normal inter-word space...@>=
+if (space_skip==zero_glue)
+ {@+@<Find the glue specification, |main_p|, for text spaces in the current
+font@>;
+ temp_ptr=new_glue(main_p);
+ }
+else temp_ptr=new_param_glue(space_skip_code);
+link(tail)=temp_ptr;tail=temp_ptr;
+goto big_switch
+
+@ Having |font_glue| allocated for each text font saves both time and memory.
+If any of the three spacing parameters are subsequently changed by the
+use of \.{\\fontdimen}, the |find_font_dimen| procedure deallocates the
+|font_glue| specification allocated here.
+
+@<Find the glue specification...@>=
+{@+main_p=font_glue[cur_font];
+if (main_p==null)
+ {@+main_p=new_spec(zero_glue);main_k=param_base[cur_font]+space_code;
+ width(main_p)=font_info[main_k].sc; /*that's |space(cur_font)|*/
+ stretch(main_p)=font_info[main_k+1].sc; /*and |space_stretch(cur_font)|*/
+ shrink(main_p)=font_info[main_k+2].sc; /*and |space_shrink(cur_font)|*/
+ font_glue[cur_font]=main_p;
+ }
+}
+
+@ @<Declare act...@>=
+static void app_space(void) /*handle spaces when |space_factor!=1000|*/
+{@+pointer @!q; /*glue node*/
+if ((space_factor >= 2000)&&(xspace_skip!=zero_glue))
+ q=new_param_glue(xspace_skip_code);
+else{@+if (space_skip!=zero_glue) main_p=space_skip;
+ else@<Find the glue specification...@>;
+ main_p=new_spec(main_p);
+ @<Modify the glue specification in |main_p| according to the space factor@>;
+ q=new_glue(main_p);glue_ref_count(main_p)=null;
+ }
+link(tail)=q;tail=q;
+}
+
+@ @<Modify the glue specification in |main_p| according to the space factor@>=
+if (space_factor >= 2000) width(main_p)=width(main_p)+extra_space(cur_font);
+stretch(main_p)=xn_over_d(stretch(main_p), space_factor, 1000);
+shrink(main_p)=xn_over_d(shrink(main_p), 1000, space_factor)
+
+@ Whew---that covers the main loop. We can now proceed at a leisurely
+pace through the other combinations of possibilities.
+
+@d any_mode(A) case vmode+A: case hmode+A:
+ case mmode+A /*for mode-independent commands*/
+
+@<Cases of |main_control| that are not part of the inner loop@>=
+any_mode(relax): case vmode+spacer: case mmode+spacer:
+ case mmode+no_boundary: do_nothing;@+break;
+any_mode(ignore_spaces): {@+@<Get the next non-blank non-call...@>;
+ goto big_reswitch;
+ }
+case vmode+stop:
+ if (its_all_over())
+ { @<record the end of \TeX@>@;
+ return; /*this is the only way out*/
+ }
+ @+break;
+@t\4@>@<Forbidden cases detected in |main_control|@>@+@, any_mode(mac_param):
+ report_illegal_case();@+break;
+@<Math-only cases in non-math modes, or vice versa@>: insert_dollar_sign();@+break;
+@t\4@>@<Cases of |main_control| that build boxes and lists@>@;
+@t\4@>@<Cases of |main_control| that don't depend on |mode|@>@;
+@t\4@>@<Cases of |main_control| that are for extensions to \TeX@>@;
+
+@ Here is a list of cases where the user has probably gotten into or out of math
+mode by mistake. \TeX\ will insert a dollar sign and rescan the current token.
+
+@d non_math(A) case vmode+A: case hmode+A
+
+@<Math-only cases in non-math modes...@>=
+non_math(sup_mark): non_math(sub_mark): non_math(math_char_num):
+non_math(math_given): non_math(math_comp): non_math(delim_num):
+non_math(left_right): non_math(above): non_math(radical):
+non_math(math_style): non_math(math_choice): non_math(vcenter):
+non_math(non_script): non_math(mkern): non_math(limit_switch):
+non_math(mskip): non_math(math_accent):
+case mmode+endv: case mmode+par_end: case mmode+stop:
+ case mmode+vskip: case mmode+un_vbox:
+case mmode+valign: case mmode+hrule
+
+@ @<Declare action...@>=
+static void insert_dollar_sign(void)
+{@+back_input();cur_tok=math_shift_token+'$';
+print_err("Missing $ inserted");
+@.Missing \$ inserted@>
+help2("I've inserted a begin-math/end-math symbol since I think",@/
+"you left one out. Proceed, with fingers crossed.");ins_error();
+}
+
+@ When erroneous situations arise, \TeX\ usually issues an error message
+specific to the particular error. For example, `\.{\\noalign}' should
+not appear in any mode, since it is recognized by the |align_peek| routine
+in all of its legitimate appearances; a special error message is given
+when `\.{\\noalign}' occurs elsewhere. But sometimes the most appropriate
+error message is simply that the user is not allowed to do what he or she
+has attempted. For example, `\.{\\moveleft}' is allowed only in vertical mode,
+and `\.{\\lower}' only in non-vertical modes. Such cases are enumerated
+here and in the other sections referred to under `See also \dots.'
+
+@<Forbidden cases...@>=
+case vmode+vmove: case hmode+hmove: case mmode+hmove: any_mode(last_item):
+
+@ The `|you_cant|' procedure prints a line saying that the current command
+is illegal in the current mode; it identifies these things symbolically.
+
+@<Declare action...@>=
+static void you_cant(void)
+{@+print_err("You can't use `");
+@.You can't use x in y mode@>
+print_cmd_chr(cur_cmd, cur_chr);
+print("' in ");print_mode(mode);
+}
+
+@ @<Declare act...@>=
+static void report_illegal_case(void)
+{@+you_cant();
+help4("Sorry, but I'm not programmed to handle this case;",@/
+"I'll just pretend that you didn't ask for it.",@/
+"If you're in the wrong mode, you might be able to",@/
+"return to the right one by typing `I}' or `I$' or `I\\par'.");@/
+error();
+}
+
+@ Some operations are allowed only in privileged modes, i.e., in cases
+that |mode > 0|. The |privileged| function is used to detect violations
+of this rule; it issues an error message and returns |false| if the
+current |mode| is negative.
+
+@<Declare act...@>=
+static bool privileged(void)
+{@+if (mode > 0) return true;
+else{@+report_illegal_case();return false;
+ }
+}
+
+@ Either \.{\\dump} or \.{\\end} will cause |main_control| to enter the
+endgame, since both of them have `|stop|' as their command code.
+
+@<Put each...@>=
+primitive("end", stop, 0);@/
+@!@:end\_}{\.{\\end} primitive@>
+primitive("dump", stop, 1);@/
+@!@:dump\_}{\.{\\dump} primitive@>
+
+@ @<Cases of |print_cmd_chr|...@>=
+case stop: if (chr_code==1) print_esc("dump");@+else print_esc("end");@+break;
+
+@ We don't want to leave |main_control| immediately when a |stop| command
+is sensed, because it may be necessary to invoke an \.{\\output} routine
+several times before things really grind to a halt. (The output routine
+might even say `\.{\\gdef\\end\{...\}}', to prolong the life of the job.)
+Therefore |its_all_over| is |true| only when the current page
+and contribution list are empty, and when the last output was not a
+``dead cycle.''
+
+@<Declare act...@>=
+static bool its_all_over(void) /*do this when \.{\\end} or \.{\\dump} occurs*/
+{@+
+if (privileged())
+ {@+if ((page_head==page_tail)&&(head==tail)&&(dead_cycles==0))
+ {@+return true;
+ }
+ back_input(); /*we will try to end again after ejecting residual material*/
+ tail_append(new_null_box());
+ width(tail)=hsize;
+ tail_append(new_glue(fill_glue));
+ tail_append(new_penalty(-010000000000));@/
+ build_page(); /*append \.{\\hbox to \\hsize\{\}\\vfill\\penalty-'10000000000}*/
+ }
+return false;
+}
+
+@* Building boxes and lists.
+The most important parts of |main_control| are concerned with \TeX's
+chief mission of box-making. We need to control the activities that put
+entries on vlists and hlists, as well as the activities that convert
+those lists into boxes. All of the necessary machinery has already been
+developed; it remains for us to ``push the buttons'' at the right times.
+
+@ As an introduction to these routines, let's consider one of the simplest
+cases: What happens when `\.{\\hrule}' occurs in vertical mode, or
+`\.{\\vrule}' in horizontal mode or math mode? The code in |main_control|
+is short, since the |scan_rule_spec| routine already does most of what is
+required; thus, there is no need for a special action procedure.
+
+Note that baselineskip calculations are disabled after a rule in vertical
+mode, by setting |prev_depth=ignore_depth|.
+
+@<Cases of |main_control| that build...@>=
+case vmode+hrule: case hmode+vrule: case mmode+vrule: {@+tail_append(scan_rule_spec());
+ if (abs(mode)==vmode) prev_depth=ignore_depth;
+ else if (abs(mode)==hmode) space_factor=1000;
+ } @+break;
+
+@ The processing of things like \.{\\hskip} and \.{\\vskip} is slightly
+more complicated. But the code in |main_control| is very short, since
+it simply calls on the action routine |append_glue|. Similarly, \.{\\kern}
+activates |append_kern|.
+
+@<Cases of |main_control| that build...@>=
+case vmode+vskip: case hmode+hskip: case mmode+hskip:
+ case mmode+mskip: append_glue();@+break;
+any_mode(kern): case mmode+mkern: append_kern();@+break;
+
+@ The |hskip| and |vskip| command codes are used for control sequences
+like \.{\\hss} and \.{\\vfil} as well as for \.{\\hskip} and \.{\\vskip}.
+The difference is in the value of |cur_chr|.
+
+@d fil_code 0 /*identifies \.{\\hfil} and \.{\\vfil}*/
+@d fill_code 1 /*identifies \.{\\hfill} and \.{\\vfill}*/
+@d ss_code 2 /*identifies \.{\\hss} and \.{\\vss}*/
+@d fil_neg_code 3 /*identifies \.{\\hfilneg} and \.{\\vfilneg}*/
+@d skip_code 4 /*identifies \.{\\hskip} and \.{\\vskip}*/
+@d mskip_code 5 /*identifies \.{\\mskip}*/
+
+@<Put each...@>=
+primitive("hskip", hskip, skip_code);@/
+@!@:hskip\_}{\.{\\hskip} primitive@>
+primitive("hfil", hskip, fil_code);
+@!@:hfil\_}{\.{\\hfil} primitive@>
+primitive("hfill", hskip, fill_code);@/
+@!@:hfill\_}{\.{\\hfill} primitive@>
+primitive("hss", hskip, ss_code);
+@!@:hss\_}{\.{\\hss} primitive@>
+primitive("hfilneg", hskip, fil_neg_code);@/
+@!@:hfil\_neg\_}{\.{\\hfilneg} primitive@>
+primitive("vskip", vskip, skip_code);@/
+@!@:vskip\_}{\.{\\vskip} primitive@>
+primitive("vfil", vskip, fil_code);
+@!@:vfil\_}{\.{\\vfil} primitive@>
+primitive("vfill", vskip, fill_code);@/
+@!@:vfill\_}{\.{\\vfill} primitive@>
+primitive("vss", vskip, ss_code);
+@!@:vss\_}{\.{\\vss} primitive@>
+primitive("vfilneg", vskip, fil_neg_code);@/
+@!@:vfil\_neg\_}{\.{\\vfilneg} primitive@>
+primitive("mskip", mskip, mskip_code);@/
+@!@:mskip\_}{\.{\\mskip} primitive@>
+primitive("kern", kern, explicit);
+@!@:kern\_}{\.{\\kern} primitive@>
+primitive("mkern", mkern, mu_glue);@/
+@!@:mkern\_}{\.{\\mkern} primitive@>
+
+@ @<Cases of |print_cmd_chr|...@>=
+case hskip: switch (chr_code) {
+ case skip_code: print_esc("hskip");@+break;
+ case fil_code: print_esc("hfil");@+break;
+ case fill_code: print_esc("hfill");@+break;
+ case ss_code: print_esc("hss");@+break;
+ default:print_esc("hfilneg");
+ } @+break;
+case vskip: switch (chr_code) {
+ case skip_code: print_esc("vskip");@+break;
+ case fil_code: print_esc("vfil");@+break;
+ case fill_code: print_esc("vfill");@+break;
+ case ss_code: print_esc("vss");@+break;
+ default:print_esc("vfilneg");
+ } @+break;
+case mskip: print_esc("mskip");@+break;
+case kern: print_esc("kern");@+break;
+case mkern: print_esc("mkern");@+break;
+
+@ All the work relating to glue creation has been relegated to the
+following subroutine. It does not call |build_page|, because it is
+used in at least one place where that would be a mistake.
+
+@<Declare action...@>=
+static void append_glue(void)
+{@+small_number s; /*modifier of skip command*/
+s=cur_chr;
+switch (s) {
+case fil_code: cur_val=fil_glue;@+break;
+case fill_code: cur_val=fill_glue;@+break;
+case ss_code: cur_val=ss_glue;@+break;
+case fil_neg_code: cur_val=fil_neg_glue;@+break;
+case skip_code: scan_glue(glue_val);@+break;
+case mskip_code: scan_glue(mu_val);
+} /*now |cur_val| points to the glue specification*/
+tail_append(new_glue(cur_val));
+if (s >= skip_code)
+ {@+decr(glue_ref_count(cur_val));
+ if (s > skip_code) subtype(tail)=mu_glue;
+ }
+}
+
+@ @<Declare act...@>=
+static void append_kern(void)
+{@+quarterword s; /*|subtype| of the kern node*/
+s=cur_chr;scan_dimen(s==mu_glue, false, false);
+tail_append(new_kern(cur_val));subtype(tail)=s;
+}
+
+@ Many of the actions related to box-making are triggered by the appearance
+of braces in the input. For example, when the user says `\.{\\hbox}
+\.{to} \.{100pt\{$\langle\,\hbox{\rm hlist}\,\rangle$\}}' in vertical mode,
+the information about the box size (100pt, |exactly|) is put onto |save_stack|
+with a level boundary word just above it, and |cur_group=adjusted_hbox_group|;
+\TeX\ enters restricted horizontal mode to process the hlist. The right
+brace eventually causes |save_stack| to be restored to its former state,
+at which time the information about the box size (100pt, |exactly|) is
+available once again; a box is packaged and we leave restricted horizontal
+mode, appending the new box to the current list of the enclosing mode
+(in this case to the current list of vertical mode), followed by any
+vertical adjustments that were removed from the box by |hpack|.
+
+The next few sections of the program are therefore concerned with the
+treatment of left and right curly braces.
+
+@ If a left brace occurs in the middle of a page or paragraph, it simply
+introduces a new level of grouping, and the matching right brace will not have
+such a drastic effect. Such grouping affects neither the mode nor the
+current list.
+
+@<Cases of |main_control| that build...@>=
+non_math(left_brace): new_save_level(simple_group);@+break;
+any_mode(begin_group): new_save_level(semi_simple_group);@+break;
+any_mode(end_group): if (cur_group==semi_simple_group) unsave();
+ else off_save();@+break;
+
+@ We have to deal with errors in which braces and such things are not
+properly nested. Sometimes the user makes an error of commission by
+inserting an extra symbol, but sometimes the user makes an error of omission.
+\TeX\ can't always tell one from the other, so it makes a guess and tries
+to avoid getting into a loop.
+
+The |off_save| routine is called when the current group code is wrong. It tries
+to insert something into the user's input that will help clean off
+the top level.
+
+@<Declare act...@>=
+static void off_save(void)
+{@+pointer p; /*inserted token*/
+if (cur_group==bottom_level)
+ @<Drop current token and complain that it was unmatched@>@;
+else{@+back_input();p=get_avail();link(temp_head)=p;
+ print_err("Missing ");
+ @<Prepare to insert a token that matches |cur_group|, and print what it
+is@>;
+ print(" inserted");ins_list(link(temp_head));
+ help5("I've inserted something that you may have forgotten.",@/
+ "(See the <inserted text> above.)",@/
+ "With luck, this will get me unwedged. But if you",@/
+ "really didn't forget anything, try typing `2' now; then",@/
+ "my insertion and my current dilemma will both disappear.");
+ error();
+ }
+}
+
+@ At this point, |link(temp_head)==p|, a pointer to an empty one-word node.
+
+@<Prepare to insert a token that matches |cur_group|...@>=
+switch (cur_group) {
+case semi_simple_group: {@+info(p)=cs_token_flag+frozen_end_group;
+ print_esc("endgroup");
+@.Missing \\endgroup inserted@>
+ } @+break;
+case math_shift_group: {@+info(p)=math_shift_token+'$';print_char('$');
+@.Missing \$ inserted@>
+ } @+break;
+case math_left_group: {@+info(p)=cs_token_flag+frozen_right;link(p)=get_avail();
+ p=link(p);info(p)=other_token+'.';print_esc("right.");
+@.Missing \\right\hbox{.} inserted@>
+@^null delimiter@>
+ } @+break;
+default:{@+info(p)=right_brace_token+'}';print_char('}');
+@.Missing \} inserted@>
+ }
+}
+
+@ @<Drop current token and complain that it was unmatched@>=
+{@+print_err("Extra ");print_cmd_chr(cur_cmd, cur_chr);
+@.Extra x@>
+help1("Things are pretty mixed up, but I think the worst is over.");@/
+error();
+}
+
+@ The routine for a |right_brace| character branches into many subcases,
+since a variety of things may happen, depending on |cur_group|. Some
+types of groups are not supposed to be ended by a right brace; error
+messages are given in hopes of pinpointing the problem. Most branches
+of this routine will be filled in later, when we are ready to understand
+them; meanwhile, we must prepare ourselves to deal with such errors.
+
+@<Cases of |main_control| that build...@>=
+any_mode(right_brace): handle_right_brace();@+break;
+
+@ @<Declare the procedure called |handle_right_brace|@>=
+static void handle_right_brace(void)
+{@+pointer p, @!q; /*for short-term use*/
+scaled @!d; /*holds |split_max_depth| in |insert_group|*/
+int @!f; /*holds |floating_penalty| in |insert_group|*/
+switch (cur_group) {
+case simple_group: unsave();@+break;
+case bottom_level: {@+print_err("Too many }'s");
+@.Too many \}'s@>
+ help2("You've closed more groups than you opened.",@/
+ "Such booboos are generally harmless, so keep going.");error();
+ } @+break;
+case semi_simple_group: case math_shift_group:
+ case math_left_group: extra_right_brace();@+break;
+@t\4@>@<Cases of |handle_right_brace| where a |right_brace| triggers a delayed
+action@>@;
+default:confusion("rightbrace");
+@:this can't happen rightbrace}{\quad rightbrace@>
+}
+}
+
+@ @<Declare act...@>=
+static void extra_right_brace(void)
+{@+print_err("Extra }, or forgotten ");
+@.Extra \}, or forgotten x@>
+switch (cur_group) {
+case semi_simple_group: print_esc("endgroup");@+break;
+case math_shift_group: print_char('$');@+break;
+case math_left_group: print_esc("right");
+} @/
+help5("I've deleted a group-closing symbol because it seems to be",@/
+"spurious, as in `$x}$'. But perhaps the } is legitimate and",@/
+"you forgot something else, as in `\\hbox{$x}'. In such cases",@/
+"the way to recover is to insert both the forgotten and the",@/
+"deleted material, e.g., by typing `I$}'.");error();
+incr(align_state);
+}
+
+@ Here is where we clear the parameters that are supposed to revert to their
+default values after every paragraph and when internal vertical mode is entered.
+
+@<Declare act...@>=
+static void normal_paragraph(void)
+{@+if (looseness!=0) eq_word_define(int_base+looseness_code, 0);
+if (hang_indent!=0) eq_word_define(dimen_base+hang_indent_code, 0);
+if (hang_after!=1) eq_word_define(int_base+hang_after_code, 1);
+if (par_shape_ptr!=null) eq_define(par_shape_loc, shape_ref, null);
+if (inter_line_penalties_ptr!=null)
+ eq_define(inter_line_penalties_loc, shape_ref, null);
+}
+
+@ Now let's turn to the question of how \.{\\hbox} is treated. We actually
+need to consider also a slightly larger context, since constructions like
+`\.{\\setbox3=}\penalty0\.{\\hbox...}' and
+`\.{\\leaders}\penalty0\.{\\hbox...}' and
+`\.{\\lower3.8pt\\hbox...}'
+are supposed to invoke quite
+different actions after the box has been packaged. Conversely,
+constructions like `\.{\\setbox3=}' can be followed by a variety of
+different kinds of boxes, and we would like to encode such things in an
+efficient way.
+
+In other words, there are two problems: to represent the context of a box,
+and to represent its type.
+
+The first problem is solved by putting a ``context code'' on the |save_stack|,
+just below the two entries that give the dimensions produced by |scan_spec|.
+The context code is either a (signed) shift amount, or it is a large
+integer | >= box_flag|, where |box_flag==@t$2^{30}$@>|. Codes |box_flag| through
+|global_box_flag-1| represent `\.{\\setbox0}' through `\.{\\setbox32767}';
+codes |global_box_flag| through |ship_out_flag-1| represent
+`\.{\\global\\setbox0}' through `\.{\\global\\setbox32767}';
+code |ship_out_flag| represents `\.{\\shipout}'; and codes |leader_flag|
+through |leader_flag+2| represent `\.{\\leaders}', `\.{\\cleaders}',
+and `\.{\\xleaders}'.
+
+The second problem is solved by giving the command code |make_box| to all
+control sequences that produce a box, and by using the following |chr_code|
+values to distinguish between them: |box_code|, |copy_code|, |last_box_code|,
+|vsplit_code|, |vtop_code|, |vtop_code+vmode|, and |vtop_code+hmode|, where
+the latter two are used to denote \.{\\vbox} and \.{\\hbox}, respectively.
+
+@d box_flag 010000000000 /*context code for `\.{\\setbox0}'*/
+@d global_box_flag 010000100000 /*context code for `\.{\\global\\setbox0}'*/
+@d ship_out_flag 010000200000 /*context code for `\.{\\shipout}'*/
+@d leader_flag 010000200001 /*context code for `\.{\\leaders}'*/
+@d box_code 0 /*|chr_code| for `\.{\\box}'*/
+@d copy_code 1 /*|chr_code| for `\.{\\copy}'*/
+@d last_box_code 2 /*|chr_code| for `\.{\\lastbox}'*/
+@d vsplit_code 3 /*|chr_code| for `\.{\\vsplit}'*/
+@d vtop_code 4 /*|chr_code| for `\.{\\vtop}'*/
+
+@<Put each...@>=
+primitive("moveleft", hmove, 1);
+@!@:move\_left\_}{\.{\\moveleft} primitive@>
+primitive("moveright", hmove, 0);@/
+@!@:move\_right\_}{\.{\\moveright} primitive@>
+primitive("raise", vmove, 1);
+@!@:raise\_}{\.{\\raise} primitive@>
+primitive("lower", vmove, 0);
+@!@:lower\_}{\.{\\lower} primitive@>
+@#
+primitive("box", make_box, box_code);
+@!@:box\_}{\.{\\box} primitive@>
+primitive("copy", make_box, copy_code);
+@!@:copy\_}{\.{\\copy} primitive@>
+primitive("lastbox", make_box, last_box_code);
+@!@:last\_box\_}{\.{\\lastbox} primitive@>
+primitive("vsplit", make_box, vsplit_code);
+@!@:vsplit\_}{\.{\\vsplit} primitive@>
+primitive("vtop", make_box, vtop_code);@/
+@!@:vtop\_}{\.{\\vtop} primitive@>
+primitive("vbox", make_box, vtop_code+vmode);
+@!@:vbox\_}{\.{\\vbox} primitive@>
+primitive("hbox", make_box, vtop_code+hmode);@/
+@!@:hbox\_}{\.{\\hbox} primitive@>
+primitive("shipout", leader_ship, a_leaders-1); /*|ship_out_flag==leader_flag-1|*/
+@!@:ship\_out\_}{\.{\\shipout} primitive@>
+primitive("leaders", leader_ship, a_leaders);
+@!@:leaders\_}{\.{\\leaders} primitive@>
+primitive("cleaders", leader_ship, c_leaders);
+@!@:c\_leaders\_}{\.{\\cleaders} primitive@>
+primitive("xleaders", leader_ship, x_leaders);
+@!@:x\_leaders\_}{\.{\\xleaders} primitive@>
+
+@ @<Cases of |print_cmd_chr|...@>=
+case hmove: if (chr_code==1) print_esc("moveleft");@+else print_esc("moveright");@+break;
+case vmove: if (chr_code==1) print_esc("raise");@+else print_esc("lower");@+break;
+case make_box: switch (chr_code) {
+ case box_code: print_esc("box");@+break;
+ case copy_code: print_esc("copy");@+break;
+ case last_box_code: print_esc("lastbox");@+break;
+ case vsplit_code: print_esc("vsplit");@+break;
+ case vtop_code: print_esc("vtop");@+break;
+ case vtop_code+vmode: print_esc("vbox");@+break;
+ default:print_esc("hbox");
+ } @+break;
+case leader_ship: if (chr_code==a_leaders) print_esc("leaders");
+ else if (chr_code==c_leaders) print_esc("cleaders");
+ else if (chr_code==x_leaders) print_esc("xleaders");
+ else print_esc("shipout");@+break;
+
+@ Constructions that require a box are started by calling |scan_box| with
+a specified context code. The |scan_box| routine verifies
+that a |make_box| command comes next and then it calls |begin_box|.
+
+@<Cases of |main_control| that build...@>=
+case vmode+hmove: case hmode+vmove: case mmode+vmove: {@+t=cur_chr;
+ scan_normal_dimen;
+ if (t==0) scan_box(cur_val);@+else scan_box(-cur_val);
+ } @+break;
+any_mode(leader_ship): scan_box(leader_flag-a_leaders+cur_chr);@+break;
+any_mode(make_box): begin_box(0);@+break;
+
+@ The global variable |cur_box| will point to a newly made box. If the box
+is void, we will have |cur_box==null|. Otherwise we will have
+|type(cur_box)==hlist_node| or |vlist_node| or |rule_node|; the |rule_node|
+case can occur only with leaders.
+
+@<Glob...@>=
+static pointer @!cur_box; /*box to be placed into its context*/
+
+@ The |box_end| procedure does the right thing with |cur_box|, if
+|box_context| represents the context as explained above.
+
+@<Declare act...@>=
+static void box_end(int @!box_context)
+{@+pointer p; /*|ord_noad| for new box in math mode*/
+small_number @!a; /*global prefix*/
+if (box_context < box_flag) @<Append box |cur_box| to the current list, shifted
+by |box_context|@>@;
+else if (box_context < ship_out_flag) @<Store \(c)|cur_box| in a box register@>@;
+else if (cur_box!=null)
+ if (box_context > ship_out_flag) @<Append a new leader node that uses |cur_box|@>@;
+ else ship_out(cur_box);
+}
+
+@ The global variable |adjust_tail| will be non-null if and only if the
+current box might include adjustments that should be appended to the
+current vertical list.
+
+@<Append box |cur_box| to the current...@>=
+{@+if (cur_box!=null)
+ {@+shift_amount(cur_box)=box_context;
+ if (abs(mode)==vmode)
+ {@+append_to_vlist(cur_box);
+ if (adjust_tail!=null)
+ {@+if (adjust_head!=adjust_tail)
+ {@+link(tail)=link(adjust_head);tail=adjust_tail;
+ }
+ adjust_tail=null;
+ }
+ if (mode > 0) build_page();
+ }
+ else{@+if (abs(mode)==hmode) space_factor=1000;
+ else{@+p=new_noad();
+ math_type(nucleus(p))=sub_box;
+ info(nucleus(p))=cur_box;cur_box=p;
+ }
+ link(tail)=cur_box;tail=cur_box;
+ }
+ }
+}
+
+@ @<Store \(c)|cur_box| in a box register@>=
+{@+if (box_context < global_box_flag)
+ {@+cur_val=box_context-box_flag;a=0;
+ }
+else{@+cur_val=box_context-global_box_flag;a=4;
+ }
+if (cur_val < 256) define(box_base+cur_val, box_ref, cur_box);
+else sa_def_box;
+}
+
+@ @<Append a new leader node...@>=
+{@+@<Get the next non-blank non-relax...@>;
+if (((cur_cmd==hskip)&&(abs(mode)!=vmode))||@|
+ ((cur_cmd==vskip)&&(abs(mode)==vmode)))
+ {@+append_glue();subtype(tail)=box_context-(leader_flag-a_leaders);
+ leader_ptr(tail)=cur_box;
+ }
+else{@+print_err("Leaders not followed by proper glue");
+@.Leaders not followed by...@>
+ help3("You should say `\\leaders <box or rule><hskip or vskip>'.",@/
+ "I found the <box or rule>, but there's no suitable",@/
+ "<hskip or vskip>, so I'm ignoring these leaders.");back_error();
+ flush_node_list(cur_box);
+ }
+}
+
+@ Now that we can see what eventually happens to boxes, we can consider
+the first steps in their creation. The |begin_box| routine is called when
+|box_context| is a context specification, |cur_chr| specifies the type of
+box desired, and |cur_cmd==make_box|.
+
+@<Declare act...@>=
+static void begin_box(int @!box_context)
+{@+
+pointer @!p, @!q; /*run through the current list*/
+int @!m; /*the length of a replacement list*/
+halfword @!k; /*0 or |vmode| or |hmode|*/
+halfword @!n; /*a box number*/
+switch (cur_chr) {
+case box_code: {@+scan_register_num();fetch_box(cur_box);
+ change_box(null); /*the box becomes void, at the same level*/
+ } @+break;
+case copy_code: {@+scan_register_num();fetch_box(q);cur_box=copy_node_list(q);
+ } @+break;
+case last_box_code: @<If the current list ends with a box node, delete it
+from the list and make |cur_box| point to it; otherwise set |cur_box:=null|@>@;@+break;
+case vsplit_code: @<Split off part of a vertical box, make |cur_box| point
+to it@>@;@+break;
+default:@<Initiate the construction of an hbox or vbox, then |return|@>@;
+} @/
+box_end(box_context); /*in simple cases, we use the box immediately*/
+}
+
+@ Note that the condition |!is_char_node(tail)| implies that |head!=tail|,
+since |head| is a one-word node.
+
+@<If the current list ends with a box node, delete it...@>=
+{@+cur_box=null;
+if (abs(mode)==mmode)
+ {@+you_cant();help1("Sorry; this \\lastbox will be void.");error();
+ }
+else if ((mode==vmode)&&(head==tail))
+ {@+you_cant();
+ help2("Sorry...I usually can't take things from the current page.",@/
+ "This \\lastbox will therefore be void.");error();
+ }
+else{@+if (!is_char_node(tail))
+ if ((type(tail)==hlist_node)||(type(tail)==vlist_node))
+ @<Remove the last box, unless it's part of a discretionary@>;
+ }
+}
+
+@ @<Remove the last box...@>=
+{@+q=head;
+@/do@+{p=q;
+if (!is_char_node(q)) if (type(q)==disc_node)
+ {@+for (m=1; m<=replace_count(q); m++) p=link(p);
+ if (p==tail) goto done;
+ }
+q=link(p);
+}@+ while (!(q==tail));
+cur_box=tail;shift_amount(cur_box)=0;
+tail=p;link(p)=null;
+done: ;}
+
+@ Here we deal with things like `\.{\\vsplit 13 to 100pt}'.
+
+@<Split off part of a vertical box, make |cur_box| point to it@>=
+{@+scan_register_num();n=cur_val;
+if (!scan_keyword("to"))
+@.to@>
+ {@+print_err("Missing `to' inserted");
+@.Missing `to' inserted@>
+ help2("I'm working on `\\vsplit<box number> to <dimen>';",@/
+ "will look for the <dimen> next.");error();
+ }
+scan_normal_dimen;
+cur_box=vsplit(n, cur_val);
+}
+
+@ Here is where we enter restricted horizontal mode or internal vertical
+mode, in order to make a box.
+
+@<Initiate the construction of an hbox or vbox, then |return|@>=
+{@+k=cur_chr-vtop_code;saved(0)=box_context;
+if (k==hmode)
+ if ((box_context < box_flag)&&(abs(mode)==vmode))
+ scan_spec(adjusted_hbox_group, true);
+ else scan_spec(hbox_group, true);
+else{@+if (k==vmode) scan_spec(vbox_group, true);
+ else{@+scan_spec(vtop_group, true);k=vmode;
+ }
+ normal_paragraph();
+ }
+push_nest();mode=-k;
+if (k==vmode)
+ {@+prev_depth=ignore_depth;
+ if (every_vbox!=null) begin_token_list(every_vbox, every_vbox_text);
+ }
+else{@+space_factor=1000;
+ if (every_hbox!=null) begin_token_list(every_hbox, every_hbox_text);
+ }
+return;
+}
+
+@ @<Declare act...@>=
+static void scan_box(int @!box_context)
+ /*the next input should specify a box or perhaps a rule*/
+{@+@<Get the next non-blank non-relax...@>;
+if (cur_cmd==make_box) begin_box(box_context);
+else if ((box_context >= leader_flag)&&((cur_cmd==hrule)||(cur_cmd==vrule)))
+ {@+cur_box=scan_rule_spec();box_end(box_context);
+ }
+else{@+@t@>@;@/
+ print_err("A <box> was supposed to be here");@/
+@.A <box> was supposed to...@>
+ help3("I was expecting to see \\hbox or \\vbox or \\copy or \\box or",@/
+ "something like that. So you might find something missing in",@/
+ "your output. But keep trying; you can fix this later.");back_error();
+ }
+}
+
+@ When the right brace occurs at the end of an \.{\\hbox} or \.{\\vbox} or
+\.{\\vtop} construction, the |package| routine comes into action. We might
+also have to finish a paragraph that hasn't ended.
+
+@<Cases of |handle...@>=
+case hbox_group: package(0);@+break;
+case adjusted_hbox_group: {@+adjust_tail=adjust_head;package(0);
+ } @+break;
+case vbox_group: {@+end_graf();package(0);
+ } @+break;
+case vtop_group: {@+end_graf();package(vtop_code);
+ } @+break;
+
+@ @<Declare action...@>=
+static void package(small_number @!c)
+{@+scaled h; /*height of box*/
+pointer @!p; /*first node in a box*/
+scaled @!d; /*max depth*/
+d=box_max_depth;unsave();save_ptr=save_ptr-3;
+if (mode==-hmode) cur_box=hpack(link(head), saved(2), saved(1));
+else{@+cur_box=vpackage(link(head), saved(2), saved(1), d);
+ if (c==vtop_code) @<Readjust the height and depth of |cur_box|, for \.{\\vtop}@>;
+ }
+pop_nest();box_end(saved(0));
+}
+
+@ The height of a `\.{\\vtop}' box is inherited from the first item on its list,
+if that item is an |hlist_node|, |vlist_node|, or |rule_node|; otherwise
+the \.{\\vtop} height is zero.
+
+
+@<Readjust the height...@>=
+{@+h=0;p=list_ptr(cur_box);
+if (p!=null) if (type(p) <= rule_node) h=height(p);
+depth(cur_box)=depth(cur_box)-h+height(cur_box);height(cur_box)=h;
+}
+
+@ A paragraph begins when horizontal-mode material occurs in vertical mode,
+or when the paragraph is explicitly started by `\.{\\indent}' or
+`\.{\\noindent}'.
+
+@<Put each...@>=
+primitive("indent", start_par, 1);
+@!@:indent\_}{\.{\\indent} primitive@>
+primitive("noindent", start_par, 0);
+@!@:no\_indent\_}{\.{\\noindent} primitive@>
+
+@ @<Cases of |print_cmd_chr|...@>=
+case start_par: if (chr_code==0) print_esc("noindent");@+else print_esc("indent");@+break;
+
+@ @<Cases of |main_control| that build...@>=
+case vmode+start_par: new_graf(cur_chr > 0);@+break;
+case vmode+letter: case vmode+other_char:
+ case vmode+char_num: case vmode+char_given:
+ case vmode+math_shift: case vmode+un_hbox:
+ case vmode+vrule:
+ case vmode+accent: case vmode+discretionary:
+ case vmode+hskip: case vmode+valign:
+ case vmode+ex_space: case vmode+no_boundary: @t@>@;@/
+ {@+back_input();new_graf(true);
+ } @+break;
+
+@ @<Declare act...@>=
+static small_number norm_min(int @!h)
+{@+if (h <= 0) return 1;@+else if (h >= 63) return 63;@+
+else return h;
+}
+@#
+static void new_graf(bool @!indented)
+{@+prev_graf=0;
+if ((mode==vmode)||(head!=tail))
+ tail_append(new_param_glue(par_skip_code));
+push_nest();mode=hmode;space_factor=1000;set_cur_lang;clang=cur_lang;
+prev_graf=(norm_min(left_hyphen_min)*0100+norm_min(right_hyphen_min))
+ *0200000+cur_lang;
+if (indented)
+ {@+tail=new_null_box();link(head)=tail;width(tail)=par_indent;@+
+ }
+if (every_par!=null) begin_token_list(every_par, every_par_text);
+if (nest_ptr==1) build_page(); /*put |par_skip| glue on current page*/
+}
+
+@ @<Cases of |main_control| that build...@>=
+case hmode+start_par: case mmode+start_par: indent_in_hmode();@+break;
+
+@ @<Declare act...@>=
+static void indent_in_hmode(void)
+{@+pointer p, @!q;
+if (cur_chr > 0) /*\.{\\indent}*/
+ {@+p=new_null_box();width(p)=par_indent;
+ if (abs(mode)==hmode) space_factor=1000;
+ else{@+q=new_noad();math_type(nucleus(q))=sub_box;
+ info(nucleus(q))=p;p=q;
+ }
+ tail_append(p);
+ }
+}
+
+@ A paragraph ends when a |par_end| command is sensed, or when we are in
+horizontal mode when reaching the right brace of vertical-mode routines
+like \.{\\vbox}, \.{\\insert}, or \.{\\output}.
+
+@<Cases of |main_control| that build...@>=
+case vmode+par_end: {@+normal_paragraph();
+ if (mode > 0) build_page();
+ } @+break;
+case hmode+par_end: {@+if (align_state < 0) off_save(); /*this tries to
+ recover from an alignment that didn't end properly*/
+ end_graf(); /*this takes us to the enclosing mode, if |mode > 0|*/
+ if (mode==vmode) build_page();
+ } @+break;
+case hmode+stop: case hmode+vskip: case hmode+hrule:
+ case hmode+un_vbox: case hmode+halign: head_for_vmode();@+break;
+
+@ @<Declare act...@>=
+static void head_for_vmode(void)
+{@+if (mode < 0)
+ if (cur_cmd!=hrule) off_save();
+ else{@+print_err("You can't use `");
+ print_esc("hrule");print("' here except with leaders");
+@.You can't use \\hrule...@>
+ help2("To put a horizontal rule in an hbox or an alignment,",@/
+ "you should use \\leaders or \\hrulefill (see The TeXbook).");
+ error();
+ }
+else{@+back_input();cur_tok=par_token;back_input();token_type=inserted;
+ }
+}
+
+@ @<Declare act...@>=
+static void end_graf(void)
+{@+if (mode==hmode)
+ {@+if (head==tail) pop_nest(); /*null paragraphs are ignored*/
+ else line_break(widow_penalty);
+ normal_paragraph();
+ error_count=0;
+ }
+}
+
+@ Insertion and adjustment and mark nodes are constructed by the following
+pieces of the program.
+
+@<Cases of |main_control| that build...@>=
+any_mode(insert): case hmode+vadjust:
+ case mmode+vadjust: begin_insert_or_adjust();@+break;
+any_mode(mark): make_mark();@+break;
+
+@ @<Forbidden...@>=
+case vmode+vadjust:
+
+@ @<Declare act...@>=
+static void begin_insert_or_adjust(void)
+{@+if (cur_cmd==vadjust) cur_val=255;
+else{@+scan_eight_bit_int();
+ if (cur_val==255)
+ {@+print_err("You can't ");print_esc("insert");print_int(255);
+@.You can't \\insert255@>
+ help1("I'm changing to \\insert0; box 255 is special.");
+ error();cur_val=0;
+ }
+ }
+saved(0)=cur_val;incr(save_ptr);
+new_save_level(insert_group);scan_left_brace();normal_paragraph();
+push_nest();mode=-vmode;prev_depth=ignore_depth;
+}
+
+@ @<Cases of |handle...@>=
+case insert_group: {@+end_graf();q=split_top_skip;add_glue_ref(q);
+ d=split_max_depth;f=floating_penalty;unsave();decr(save_ptr);
+ /*now |saved(0)| is the insertion number, or 255 for |vadjust|*/
+ p=vpack(link(head), natural);pop_nest();
+ if (saved(0) < 255)
+ {@+tail_append(get_node(ins_node_size));
+ type(tail)=ins_node;subtype(tail)=qi(saved(0));
+ height(tail)=height(p)+depth(p);ins_ptr(tail)=list_ptr(p);
+ split_top_ptr(tail)=q;depth(tail)=d;float_cost(tail)=f;
+ }
+ else{@+tail_append(get_node(small_node_size));
+ type(tail)=adjust_node;@/
+ subtype(tail)=0; /*the |subtype| is not used*/
+ adjust_ptr(tail)=list_ptr(p);delete_glue_ref(q);
+ }
+ free_node(p, box_node_size);
+ if (nest_ptr==0) build_page();
+ } @+break;
+case output_group: @<Resume the page builder...@>@;@+break;
+
+@ @<Declare act...@>=
+static void make_mark(void)
+{@+pointer p; /*new node*/
+halfword @!c; /*the mark class*/
+if (cur_chr==0) c=0;
+else{@+scan_register_num();c=cur_val;
+ }
+p=scan_toks(false, true);p=get_node(small_node_size);
+mark_class(p)=c;
+type(p)=mark_node;subtype(p)=0; /*the |subtype| is not used*/
+mark_ptr(p)=def_ref;link(tail)=p;tail=p;
+}
+
+@ Penalty nodes get into a list via the |break_penalty| command.
+@^penalties@>
+
+@<Cases of |main_control| that build...@>=
+any_mode(break_penalty): append_penalty();@+break;
+
+@ @<Declare action...@>=
+static void append_penalty(void)
+{@+scan_int();tail_append(new_penalty(cur_val));
+if (mode==vmode) build_page();
+}
+
+@ The |remove_item| command removes a penalty, kern, or glue node if it
+appears at the tail of the current list, using a brute-force linear scan.
+Like \.{\\lastbox}, this command is not allowed in vertical mode (except
+internal vertical mode), since the current list in vertical mode is sent
+to the page builder. But if we happen to be able to implement it in
+vertical mode, we do.
+
+@<Cases of |main_control| that build...@>=
+any_mode(remove_item): delete_last();@+break;
+
+@ When |delete_last| is called, |cur_chr| is the |type| of node that
+will be deleted, if present.
+
+@<Declare action...@>=
+static void delete_last(void)
+{@+
+pointer @!p, @!q; /*run through the current list*/
+int @!m; /*the length of a replacement list*/
+if ((mode==vmode)&&(tail==head))
+ @<Apologize for inability to do the operation now, unless \.{\\unskip} follows
+non-glue@>@;
+else{@+if (!is_char_node(tail)) if (type(tail)==cur_chr)
+ {@+q=head;
+ @/do@+{p=q;
+ if (!is_char_node(q)) if (type(q)==disc_node)
+ {@+for (m=1; m<=replace_count(q); m++) p=link(p);
+ if (p==tail) return;
+ }
+ q=link(p);
+ }@+ while (!(q==tail));
+ link(p)=null;flush_node_list(tail);tail=p;
+ }
+ }
+}
+
+@ @<Apologize for inability to do the operation...@>=
+{@+if ((cur_chr!=glue_node)||(last_glue!=max_halfword))
+ {@+you_cant();
+ help2("Sorry...I usually can't take things from the current page.",@/
+ "Try `I\\vskip-\\lastskip' instead.");
+ if (cur_chr==kern_node) help_line[0]=
+ ("Try `I\\kern-\\lastkern' instead.");
+ else if (cur_chr!=glue_node) help_line[0]=@|
+ ("Perhaps you can make the output routine do it.");
+ error();
+ }
+}
+
+@ @<Put each...@>=
+primitive("unpenalty", remove_item, penalty_node);@/
+@!@:un\_penalty\_}{\.{\\unpenalty} primitive@>
+primitive("unkern", remove_item, kern_node);@/
+@!@:un\_kern\_}{\.{\\unkern} primitive@>
+primitive("unskip", remove_item, glue_node);@/
+@!@:un\_skip\_}{\.{\\unskip} primitive@>
+primitive("unhbox", un_hbox, box_code);@/
+@!@:un\_hbox\_}{\.{\\unhbox} primitive@>
+primitive("unhcopy", un_hbox, copy_code);@/
+@!@:un\_hcopy\_}{\.{\\unhcopy} primitive@>
+primitive("unvbox", un_vbox, box_code);@/
+@!@:un\_vbox\_}{\.{\\unvbox} primitive@>
+primitive("unvcopy", un_vbox, copy_code);@/
+@!@:un\_vcopy\_}{\.{\\unvcopy} primitive@>
+
+@ @<Cases of |print_cmd_chr|...@>=
+case remove_item: if (chr_code==glue_node) print_esc("unskip");
+ else if (chr_code==kern_node) print_esc("unkern");
+ else print_esc("unpenalty");@+break;
+case un_hbox: if (chr_code==copy_code) print_esc("unhcopy");
+ else print_esc("unhbox");@+break;
+case un_vbox: if (chr_code==copy_code) print_esc("unvcopy")
+ @<Cases of |un_vbox| for |print_cmd_chr|@>;@/
+ else print_esc("unvbox");@+break;
+
+@ The |un_hbox| and |un_vbox| commands unwrap one of the 256 current boxes.
+
+@<Cases of |main_control| that build...@>=
+case vmode+un_vbox: case hmode+un_hbox:
+ case mmode+un_hbox: unpackage();@+break;
+
+@ @<Declare act...@>=
+static void unpackage(void)
+{@+
+pointer p; /*the box*/
+int @!c; /*should we copy?*/
+if (cur_chr > copy_code) @<Handle saved items and |goto done|@>;
+c=cur_chr;scan_register_num();fetch_box(p);
+if (p==null) return;
+if ((abs(mode)==mmode)||((abs(mode)==vmode)&&(type(p)!=vlist_node))||@|
+ ((abs(mode)==hmode)&&(type(p)!=hlist_node)))
+ {@+print_err("Incompatible list can't be unboxed");
+@.Incompatible list...@>
+ help3("Sorry, Pandora. (You sneaky devil.)",@/
+ "I refuse to unbox an \\hbox in vertical mode or vice versa.",@/
+ "And I can't open any boxes in math mode.");@/
+ error();return;
+ }
+if (c==copy_code) link(tail)=copy_node_list(list_ptr(p));
+else{@+link(tail)=list_ptr(p);change_box(null);
+ free_node(p, box_node_size);
+ }
+done:
+while (link(tail)!=null) tail=link(tail);
+}
+
+@ @<Forbidden...@>=case vmode+ital_corr:
+
+@ Italic corrections are converted to kern nodes when the |ital_corr| command
+follows a character. In math mode the same effect is achieved by appending
+a kern of zero here, since italic corrections are supplied later.
+
+@<Cases of |main_control| that build...@>=
+case hmode+ital_corr: append_italic_correction();@+break;
+case mmode+ital_corr: tail_append(new_kern(0))@;@+break;
+
+@ @<Declare act...@>=
+static void append_italic_correction(void)
+{@+
+pointer p; /*|char_node| at the tail of the current list*/
+internal_font_number @!f; /*the font in the |char_node|*/
+if (tail!=head)
+ {@+if (is_char_node(tail)) p=tail;
+ else if (type(tail)==ligature_node) p=lig_char(tail);
+ else return;
+ f=font(p);
+ tail_append(new_kern(char_italic(f, char_info(f, character(p)))));
+ subtype(tail)=explicit;
+ }
+}
+
+@ Discretionary nodes are easy in the common case `\.{\\-}', but in the
+general case we must process three braces full of items.
+
+@<Put each...@>=
+primitive("-", discretionary, 1);
+@!@:Single-character primitives -}{\quad\.{\\-}@>
+primitive("discretionary", discretionary, 0);
+@!@:discretionary\_}{\.{\\discretionary} primitive@>
+
+@ @<Cases of |print_cmd_chr|...@>=
+case discretionary: if (chr_code==1)
+ print_esc("-");@+else print_esc("discretionary");@+break;
+
+@ @<Cases of |main_control| that build...@>=
+case hmode+discretionary: case mmode+discretionary: append_discretionary();@+break;
+
+@ The space factor does not change when we append a discretionary node,
+but it starts out as 1000 in the subsidiary lists.
+
+@<Declare act...@>=
+static void append_discretionary(void)
+{@+int c; /*hyphen character*/
+tail_append(new_disc());
+if (cur_chr==1)
+ {@+c=hyphen_char[cur_font];
+ if (c >= 0) if (c < 256) pre_break(tail)=new_character(cur_font, c);
+ }
+else{@+incr(save_ptr);saved(-1)=0;new_save_level(disc_group);
+ scan_left_brace();push_nest();mode=-hmode;space_factor=1000;
+ }
+}
+
+@ The three discretionary lists are constructed somewhat as if they were
+hboxes. A~subroutine called |build_discretionary| handles the transitions.
+(This is sort of fun.)
+
+@<Cases of |handle...@>=
+case disc_group: build_discretionary();@+break;
+
+@ @<Declare act...@>=
+static void build_discretionary(void)
+{@+
+pointer p, @!q; /*for link manipulation*/
+int @!n; /*length of discretionary list*/
+unsave();
+@<Prune the current list, if necessary, until it contains only |char_node|,
+|kern_node|, |hlist_node|, |vlist_node|, |rule_node|, and |ligature_node|
+items; set |n| to the length of the list, and set |q| to the list's tail@>;
+p=link(head);pop_nest();
+switch (saved(-1)) {
+case 0: pre_break(tail)=p;@+break;
+case 1: post_break(tail)=p;@+break;
+case 2: @<Attach list |p| to the current list, and record its length; then
+finish up and |return|@>;
+} /*there are no other cases*/
+incr(saved(-1));new_save_level(disc_group);scan_left_brace();
+push_nest();mode=-hmode;space_factor=1000;
+}
+
+@ @<Attach list |p| to the current...@>=
+{@+if ((n > 0)&&(abs(mode)==mmode))
+ {@+print_err("Illegal math ");print_esc("discretionary");
+@.Illegal math \\disc...@>
+ help2("Sorry: The third part of a discretionary break must be",@/
+ "empty, in math formulas. I had to delete your third part.");
+ flush_node_list(p);n=0;error();
+ }
+else link(tail)=p;
+if (n <= max_quarterword) replace_count(tail)=n;
+else{@+print_err("Discretionary list is too long");
+@.Discretionary list is too long@>
+ help2("Wow---I never thought anybody would tweak me here.",@/
+ "You can't seriously need such a huge discretionary list?");
+ error();
+ }
+if (n > 0) tail=q;
+decr(save_ptr);return;
+}
+
+@ During this loop, |p==link(q)| and there are |n| items preceding |p|.
+
+@<Prune the current list, if necessary...@>=
+q=head;p=link(q);n=0;
+while (p!=null)
+ {@+if (!is_char_node(p)) if (type(p) > rule_node)
+ if (type(p)!=kern_node) if (type(p)!=ligature_node)
+ {@+print_err("Improper discretionary list");
+@.Improper discretionary list@>
+ help1("Discretionary lists must contain only boxes and kerns.");@/
+ error();
+ begin_diagnostic();
+ print_nl("The following discretionary sublist has been deleted:");
+@.The following...deleted@>
+ show_box(p);
+ end_diagnostic(true);
+ flush_node_list(p);link(q)=null;goto done;
+ }
+ q=p;p=link(q);incr(n);
+ }
+done:
+
+@ We need only one more thing to complete the horizontal mode routines, namely
+the \.{\\accent} primitive.
+
+@<Cases of |main_control| that build...@>=
+case hmode+accent: make_accent();@+break;
+
+@ The positioning of accents is straightforward but tedious. Given an accent
+of width |a|, designed for characters of height |x| and slant |s|;
+and given a character of width |w|, height |h|, and slant |t|: We will shift
+the accent down by |x-h|, and we will insert kern nodes that have the effect of
+centering the accent over the character and shifting the accent to the
+right by $\delta={1\over2}(w-a)+h\cdot t-x\cdot s$. If either character is
+absent from the font, we will simply use the other, without shifting.
+
+@<Declare act...@>=
+static void make_accent(void)
+{@+double s, @!t; /*amount of slant*/
+pointer @!p, @!q, @!r; /*character, box, and kern nodes*/
+internal_font_number @!f; /*relevant font*/
+scaled @!a, @!h, @!x, @!w, @!delta; /*heights and widths, as explained above*/
+four_quarters @!i; /*character information*/
+scan_char_num();f=cur_font;p=new_character(f, cur_val);
+if (p!=null)
+ {@+x=x_height(f);s=slant(f)/float_constant(65536);
+@^real division@>
+ a=char_width(f, char_info(f, character(p)));@/
+ do_assignments();@/
+ @<Create a character node |q| for the next character, but set |q:=null|
+if problems arise@>;
+ if (q!=null) @<Append the accent with appropriate kerns, then set |p:=q|@>;
+ link(tail)=p;tail=p;space_factor=1000;
+ }
+}
+
+@ @<Create a character node |q| for the next...@>=
+q=null;f=cur_font;
+if ((cur_cmd==letter)||(cur_cmd==other_char)||(cur_cmd==char_given))
+ q=new_character(f, cur_chr);
+else if (cur_cmd==char_num)
+ {@+scan_char_num();q=new_character(f, cur_val);
+ }
+else back_input()
+
+@ The kern nodes appended here must be distinguished from other kerns, lest
+they be wiped away by the hyphenation algorithm or by a previous line break.
+
+The two kerns are computed with (machine-dependent) |double| arithmetic, but
+their sum is machine-independent; the net effect is machine-independent,
+because the user cannot remove these nodes nor access them via \.{\\lastkern}.
+
+@<Append the accent with appropriate kerns...@>=
+{@+t=slant(f)/float_constant(65536);
+@^real division@>
+i=char_info(f, character(q));
+w=char_width(f, i);h=char_height(f, height_depth(i));
+if (h!=x) /*the accent must be shifted up or down*/
+ {@+p=hpack(p, natural);shift_amount(p)=x-h;
+ }
+delta=round((w-a)/float_constant(2)+h*t-x*s);
+@^real multiplication@>
+@^real addition@>
+r=new_kern(delta);subtype(r)=acc_kern;link(tail)=r;link(r)=p;
+tail=new_kern(-a-delta);subtype(tail)=acc_kern;link(p)=tail;p=q;
+}
+
+@ When `\.{\\cr}' or `\.{\\span}' or a tab mark comes through the scanner
+into |main_control|, it might be that the user has foolishly inserted
+one of them into something that has nothing to do with alignment. But it is
+far more likely that a left brace or right brace has been omitted, since
+|get_next| takes actions appropriate to alignment only when `\.{\\cr}'
+or `\.{\\span}' or tab marks occur with |align_state==0|. The following
+program attempts to make an appropriate recovery.
+
+@<Cases of |main_control| that build...@>=
+any_mode(car_ret): any_mode(tab_mark): align_error();@+break;
+any_mode(no_align): no_align_error();@+break;
+any_mode(omit): omit_error();@+break;
+
+@ @<Declare act...@>=
+static void align_error(void)
+{@+if (abs(align_state) > 2)
+ @<Express consternation over the fact that no alignment is in progress@>@;
+else{@+back_input();
+ if (align_state < 0)
+ {@+print_err("Missing { inserted");
+@.Missing \{ inserted@>
+ incr(align_state);cur_tok=left_brace_token+'{';
+ }
+ else{@+print_err("Missing } inserted");
+@.Missing \} inserted@>
+ decr(align_state);cur_tok=right_brace_token+'}';
+ }
+ help3("I've put in what seems to be necessary to fix",@/
+ "the current column of the current alignment.",@/
+ "Try to go on, since this might almost work.");ins_error();
+ }
+}
+
+@ @<Express consternation...@>=
+{@+print_err("Misplaced ");print_cmd_chr(cur_cmd, cur_chr);
+@.Misplaced \&@>
+@.Misplaced \\span@>
+@.Misplaced \\cr@>
+if (cur_tok==tab_token+'&')
+ {@+help6("I can't figure out why you would want to use a tab mark",@/
+ "here. If you just want an ampersand, the remedy is",@/
+ "simple: Just type `I\\&' now. But if some right brace",@/
+ "up above has ended a previous alignment prematurely,",@/
+ "you're probably due for more error messages, and you",@/
+ "might try typing `S' now just to see what is salvageable.");
+ }
+else{@+help5("I can't figure out why you would want to use a tab mark",@/
+ "or \\cr or \\span just now. If something like a right brace",@/
+ "up above has ended a previous alignment prematurely,",@/
+ "you're probably due for more error messages, and you",@/
+ "might try typing `S' now just to see what is salvageable.");
+ }
+error();
+}
+
+@ The help messages here contain a little white lie, since \.{\\noalign}
+and \.{\\omit} are allowed also after `\.{\\noalign\{...\}}'.
+
+@<Declare act...@>=
+static void no_align_error(void)
+{@+print_err("Misplaced ");print_esc("noalign");
+@.Misplaced \\noalign@>
+help2("I expect to see \\noalign only after the \\cr of",@/
+ "an alignment. Proceed, and I'll ignore this case.");error();
+}
+static void omit_error(void)
+{@+print_err("Misplaced ");print_esc("omit");
+@.Misplaced \\omit@>
+help2("I expect to see \\omit only after tab marks or the \\cr of",@/
+ "an alignment. Proceed, and I'll ignore this case.");error();
+}
+
+@ We've now covered most of the abuses of \.{\\halign} and \.{\\valign}.
+Let's take a look at what happens when they are used correctly.
+
+@<Cases of |main_control| that build...@>=
+case vmode+halign: case hmode+valign: init_align();@+break;
+case mmode+halign: if (privileged())
+ if (cur_group==math_shift_group) init_align();
+ else off_save();@+break;
+case vmode+endv: case hmode+endv: do_endv();@+break;
+
+@ An |align_group| code is supposed to remain on the |save_stack|
+during an entire alignment, until |fin_align| removes it.
+
+A devious user might force an |endv| command to occur just about anywhere;
+we must defeat such hacks.
+
+@<Declare act...@>=
+static void do_endv(void)
+{@+base_ptr=input_ptr;input_stack[base_ptr]=cur_input;
+while ((input_stack[base_ptr].index_field!=v_template)&&
+ (input_stack[base_ptr].loc_field==null)&&
+ (input_stack[base_ptr].state_field==token_list)) decr(base_ptr);
+if ((input_stack[base_ptr].index_field!=v_template)||
+ (input_stack[base_ptr].loc_field!=null)||
+ (input_stack[base_ptr].state_field!=token_list))
+ fatal_error("(interwoven alignment preambles are not allowed)");
+@.interwoven alignment preambles...@>
+ if (cur_group==align_group)
+ {@+end_graf();
+ if (fin_col()) fin_row();
+ }
+else off_save();
+}
+
+@ @<Cases of |handle_right_brace|...@>=
+case align_group: {@+back_input();cur_tok=cs_token_flag+frozen_cr;
+ print_err("Missing ");print_esc("cr");print(" inserted");
+@.Missing \\cr inserted@>
+ help1("I'm guessing that you meant to end an alignment here.");
+ ins_error();
+ } @+break;
+
+@ @<Cases of |handle_right_brace|...@>=
+case no_align_group: {@+end_graf();unsave();align_peek();
+ } @+break;
+
+@ Finally, \.{\\endcsname} is not supposed to get through to |main_control|.
+
+@<Cases of |main_control| that build...@>=
+any_mode(end_cs_name): cs_error();@+break;
+
+@ @<Declare act...@>=
+static void cs_error(void)
+{@+print_err("Extra ");print_esc("endcsname");
+@.Extra \\endcsname@>
+help1("I'm ignoring this, since I wasn't doing a \\csname.");
+error();
+}
+
+@* Building math lists.
+The routines that \TeX\ uses to create mlists are similar to those we have
+just seen for the generation of hlists and vlists. But it is necessary to
+make ``noads'' as well as nodes, so the reader should review the
+discussion of math mode data structures before trying to make sense out of
+the following program.
+
+Here is a little routine that needs to be done whenever a subformula
+is about to be processed. The parameter is a code like |math_group|.
+
+@<Declare act...@>=
+static void push_math(group_code @!c)
+{@+push_nest();mode=-mmode;incompleat_noad=null;new_save_level(c);
+}
+
+@ We get into math mode from horizontal mode when a `\.\$' (i.e., a
+|math_shift| character) is scanned. We must check to see whether this
+`\.\$' is immediately followed by another, in case display math mode is
+called for.
+
+@<Cases of |main_control| that build...@>=
+case hmode+math_shift: init_math();@+break;
+
+@ @<Declare act...@>=
+static void init_math(void)
+{@+
+scaled w; /*new or partial |pre_display_size|*/
+scaled @!l; /*new |display_width|*/
+scaled @!s; /*new |display_indent|*/
+pointer @!p; /*current node when calculating |pre_display_size|*/
+pointer @!q; /*glue specification when calculating |pre_display_size|*/
+internal_font_number @!f; /*font in current |char_node|*/
+int @!n; /*scope of paragraph shape specification*/
+scaled @!v; /*|w| plus possible glue amount*/
+scaled @!d; /*increment to |v|*/
+get_token(); /*|get_x_token| would fail on \.{\\ifmmode}\thinspace!*/
+if ((cur_cmd==math_shift)&&(mode > 0)) @<Go into display math mode@>@;
+else{@+back_input();@<Go into ordinary math mode@>;
+ }
+}
+
+@ @<Go into ordinary math mode@>=
+{@+push_math(math_shift_group);eq_word_define(int_base+cur_fam_code,-1);
+if (every_math!=null) begin_token_list(every_math, every_math_text);
+}
+
+@ We get into ordinary math mode from display math mode when `\.{\\eqno}' or
+`\.{\\leqno}' appears. In such cases |cur_chr| will be 0 or~1, respectively;
+the value of |cur_chr| is placed onto |save_stack| for safe keeping.
+
+@<Cases of |main_control| that build...@>=
+case mmode+eq_no: if (privileged())
+ if (cur_group==math_shift_group) start_eq_no();
+ else off_save();@+break;
+
+@ @<Put each...@>=
+primitive("eqno", eq_no, 0);
+@!@:eq\_no\_}{\.{\\eqno} primitive@>
+primitive("leqno", eq_no, 1);
+@!@:leq\_no\_}{\.{\\leqno} primitive@>
+
+@ When \TeX\ is in display math mode, |cur_group==math_shift_group|,
+so it is not necessary for the |start_eq_no| procedure to test for
+this condition.
+
+@<Declare act...@>=
+static void start_eq_no(void)
+{@+saved(0)=cur_chr;incr(save_ptr);
+@<Go into ordinary math mode@>;
+}
+
+@ @<Cases of |print_cmd_chr|...@>=
+case eq_no: if (chr_code==1) print_esc("leqno");@+else print_esc("eqno");@+break;
+
+@ @<Forbidden...@>=non_math(eq_no):
+
+@ When we enter display math mode, we need to call |line_break| to
+process the partial paragraph that has just been interrupted by the
+display. Then we can set the proper values of |display_width| and
+|display_indent| and |pre_display_size|.
+
+@<Go into display math mode@>=
+{@+if (head==tail) /*`\.{\\noindent\$\$}' or `\.{\$\${ }\$\$}'*/
+ {@+pop_nest();w=-max_dimen;
+ }
+else{@+line_break(display_widow_penalty);@/
+ @<Calculate the natural width, |w|, by which the characters of the final
+line extend to the right of the reference point, plus two ems; or set |w:=max_dimen|
+if the non-blank information on that line is affected by stretching or shrinking@>;
+ }
+ /*now we are in vertical mode, working on the list that will contain the display*/
+@<Calculate the length, |l|, and the shift amount, |s|, of the display lines@>;
+push_math(math_shift_group);mode=mmode;
+eq_word_define(int_base+cur_fam_code,-1);@/
+eq_word_define(dimen_base+pre_display_size_code, w);
+eq_word_define(dimen_base+display_width_code, l);
+eq_word_define(dimen_base+display_indent_code, s);
+if (every_display!=null) begin_token_list(every_display, every_display_text);
+if (nest_ptr==1) build_page();
+}
+
+@ @<Calculate the natural width, |w|, by which...@>=
+v=shift_amount(just_box)+2*quad(cur_font);w=-max_dimen;
+p=list_ptr(just_box);
+while (p!=null)
+ {@+@<Let |d| be the natural width of node |p|; if the node is ``visible,''
+|goto found|; if the node is glue that stretches or shrinks, set |v:=max_dimen|@>;
+ if (v < max_dimen) v=v+d;
+ goto not_found;
+ found: if (v < max_dimen)
+ {@+v=v+d;w=v;
+ }
+ else{@+w=max_dimen;goto done;
+ }
+ not_found: p=link(p);
+ }
+done:
+
+@ @<Let |d| be the natural width of node |p|...@>=
+reswitch: if (is_char_node(p))
+ {@+f=font(p);d=char_width(f, char_info(f, character(p)));
+ goto found;
+ }
+switch (type(p)) {
+case hlist_node: case vlist_node: case rule_node: {@+d=width(p);goto found;
+ }
+case ligature_node: @<Make node |p| look like a |char_node|...@>@;
+case kern_node: case math_node: d=width(p);@+break;
+case glue_node: @<Let |d| be the natural width of this glue; if stretching
+or shrinking, set |v:=max_dimen|; |goto found| in the case of leaders@>@;@+break;
+case whatsit_node: @<Let |d| be the width of the whatsit |p|@>;@+break;
+default:d=0;
+}
+
+@ We need to be careful that |w|, |v|, and |d| do not depend on any |glue_set|
+values, since such values are subject to system-dependent rounding.
+System-dependent numbers are not allowed to infiltrate parameters like
+|pre_display_size|, since \TeX82 is supposed to make the same decisions on all
+machines.
+
+@<Let |d| be the natural width of this glue...@>=
+{@+q=glue_ptr(p);d=width(q);
+if (glue_sign(just_box)==stretching)
+ {@+if ((glue_order(just_box)==stretch_order(q))&&@|
+ (stretch(q)!=0))
+ v=max_dimen;
+ }
+else if (glue_sign(just_box)==shrinking)
+ {@+if ((glue_order(just_box)==shrink_order(q))&&@|
+ (shrink(q)!=0))
+ v=max_dimen;
+ }
+if (subtype(p) >= a_leaders) goto found;
+}
+
+@ A displayed equation is considered to be three lines long, so we
+calculate the length and offset of line number |prev_graf+2|.
+
+@<Calculate the length, |l|,...@>=
+if (par_shape_ptr==null)
+ if ((hang_indent!=0)&&@|
+ (((hang_after >= 0)&&(prev_graf+2 > hang_after))||@|
+ (prev_graf+1 < -hang_after)))
+ {@+l=hsize-abs(hang_indent);
+ if (hang_indent > 0) s=hang_indent;@+else s=0;
+ }
+ else{@+l=hsize;s=0;
+ }
+else{@+n=info(par_shape_ptr);
+ if (prev_graf+2 >= n) p=par_shape_ptr+2*n;
+ else p=par_shape_ptr+2*(prev_graf+2);
+ s=mem[p-1].sc;l=mem[p].sc;
+ }
+
+@ Subformulas of math formulas cause a new level of math mode to be entered,
+on the semantic nest as well as the save stack. These subformulas arise in
+several ways: (1)~A left brace by itself indicates the beginning of a
+subformula that will be put into a box, thereby freezing its glue and
+preventing line breaks. (2)~A subscript or superscript is treated as a
+subformula if it is not a single character; the same applies to
+the nucleus of things like \.{\\underline}. (3)~The \.{\\left} primitive
+initiates a subformula that will be terminated by a matching \.{\\right}.
+The group codes placed on |save_stack| in these three cases are
+|math_group|, |math_group|, and |math_left_group|, respectively.
+
+Here is the code that handles case (1); the other cases are not quite as
+trivial, so we shall consider them later.
+
+@<Cases of |main_control| that build...@>=
+case mmode+left_brace: {@+tail_append(new_noad());
+ back_input();scan_math(nucleus(tail));
+ } @+break;
+
+@ Recall that the |nucleus|, |subscr|, and |supscr| fields in a noad are
+broken down into subfields called |math_type| and either |info| or
+|(fam, character)|. The job of |scan_math| is to figure out what to place
+in one of these principal fields; it looks at the subformula that
+comes next in the input, and places an encoding of that subformula
+into a given word of |mem|.
+
+@d fam_in_range ((cur_fam >= 0)&&(cur_fam < 16))
+
+@<Declare act...@>=
+static void scan_math(pointer @!p)
+{@+
+int c; /*math character code*/
+restart: @<Get the next non-blank non-relax...@>;
+reswitch: switch (cur_cmd) {
+case letter: case other_char: case char_given: {@+c=ho(math_code(cur_chr));
+ if (c==0100000)
+ {@+@<Treat |cur_chr| as an active character@>;
+ goto restart;
+ }
+ } @+break;
+case char_num: {@+scan_char_num();cur_chr=cur_val;cur_cmd=char_given;
+ goto reswitch;
+ }
+case math_char_num: {@+scan_fifteen_bit_int();c=cur_val;
+ } @+break;
+case math_given: c=cur_chr;@+break;
+case delim_num: {@+scan_twenty_seven_bit_int();c=cur_val/010000;
+ } @+break;
+default:@<Scan a subformula enclosed in braces and |return|@>@;
+} @/
+math_type(p)=math_char;character(p)=qi(c%256);
+if ((c >= var_code)&&fam_in_range) fam(p)=cur_fam;
+else fam(p)=(c/256)%16;
+}
+
+@ An active character that is an |outer_call| is allowed here.
+
+@<Treat |cur_chr|...@>=
+{@+cur_cs=cur_chr+active_base;
+cur_cmd=eq_type(cur_cs);cur_chr=equiv(cur_cs);
+x_token();back_input();
+}
+
+@ The pointer |p| is placed on |save_stack| while a complex subformula
+is being scanned.
+
+@<Scan a subformula...@>=
+{@+back_input();scan_left_brace();@/
+saved(0)=p;incr(save_ptr);push_math(math_group);return;
+}
+
+@ The simplest math formula is, of course, `\.{\${ }\$}', when no noads are
+generated. The next simplest cases involve a single character, e.g.,
+`\.{\$x\$}'. Even though such cases may not seem to be very interesting,
+the reader can perhaps understand how happy the author was when `\.{\$x\$}'
+was first properly typeset by \TeX. The code in this section was used.
+@^Knuth, Donald Ervin@>
+
+@<Cases of |main_control| that build...@>=
+case mmode+letter: case mmode+other_char:
+ case mmode+char_given:
+ set_math_char(ho(math_code(cur_chr)));@+break;
+case mmode+char_num: {@+scan_char_num();cur_chr=cur_val;
+ set_math_char(ho(math_code(cur_chr)));
+ } @+break;
+case mmode+math_char_num: {@+scan_fifteen_bit_int();set_math_char(cur_val);
+ } @+break;
+case mmode+math_given: set_math_char(cur_chr);@+break;
+case mmode+delim_num: {@+scan_twenty_seven_bit_int();
+ set_math_char(cur_val/010000);
+ } @+break;
+
+@ The |set_math_char| procedure creates a new noad appropriate to a given
+math code, and appends it to the current mlist. However, if the math code
+is sufficiently large, the |cur_chr| is treated as an active character and
+nothing is appended.
+
+@<Declare act...@>=
+static void set_math_char(int @!c)
+{@+pointer p; /*the new noad*/
+if (c >= 0100000)
+ @<Treat |cur_chr|...@>@;
+else{@+p=new_noad();math_type(nucleus(p))=math_char;
+ character(nucleus(p))=qi(c%256);
+ fam(nucleus(p))=(c/256)%16;
+ if (c >= var_code)
+ {@+if (fam_in_range) fam(nucleus(p))=cur_fam;
+ type(p)=ord_noad;
+ }
+ else type(p)=ord_noad+(c/010000);
+ link(tail)=p;tail=p;
+ }
+}
+
+@ Primitive math operators like \.{\\mathop} and \.{\\underline} are given
+the command code |math_comp|, supplemented by the noad type that they
+generate.
+
+@<Put each...@>=
+primitive("mathord", math_comp, ord_noad);
+@!@:math\_ord\_}{\.{\\mathord} primitive@>
+primitive("mathop", math_comp, op_noad);
+@!@:math\_op\_}{\.{\\mathop} primitive@>
+primitive("mathbin", math_comp, bin_noad);
+@!@:math\_bin\_}{\.{\\mathbin} primitive@>
+primitive("mathrel", math_comp, rel_noad);
+@!@:math\_rel\_}{\.{\\mathrel} primitive@>
+primitive("mathopen", math_comp, open_noad);
+@!@:math\_open\_}{\.{\\mathopen} primitive@>
+primitive("mathclose", math_comp, close_noad);
+@!@:math\_close\_}{\.{\\mathclose} primitive@>
+primitive("mathpunct", math_comp, punct_noad);
+@!@:math\_punct\_}{\.{\\mathpunct} primitive@>
+primitive("mathinner", math_comp, inner_noad);
+@!@:math\_inner\_}{\.{\\mathinner} primitive@>
+primitive("underline", math_comp, under_noad);
+@!@:underline\_}{\.{\\underline} primitive@>
+primitive("overline", math_comp, over_noad);@/
+@!@:overline\_}{\.{\\overline} primitive@>
+primitive("displaylimits", limit_switch, normal);
+@!@:display\_limits\_}{\.{\\displaylimits} primitive@>
+primitive("limits", limit_switch, limits);
+@!@:limits\_}{\.{\\limits} primitive@>
+primitive("nolimits", limit_switch, no_limits);
+@!@:no\_limits\_}{\.{\\nolimits} primitive@>
+
+@ @<Cases of |print_cmd_chr|...@>=
+case math_comp: switch (chr_code) {
+ case ord_noad: print_esc("mathord");@+break;
+ case op_noad: print_esc("mathop");@+break;
+ case bin_noad: print_esc("mathbin");@+break;
+ case rel_noad: print_esc("mathrel");@+break;
+ case open_noad: print_esc("mathopen");@+break;
+ case close_noad: print_esc("mathclose");@+break;
+ case punct_noad: print_esc("mathpunct");@+break;
+ case inner_noad: print_esc("mathinner");@+break;
+ case under_noad: print_esc("underline");@+break;
+ default:print_esc("overline");
+ } @+break;
+case limit_switch: if (chr_code==limits) print_esc("limits");
+ else if (chr_code==no_limits) print_esc("nolimits");
+ else print_esc("displaylimits");@+break;
+
+@ @<Cases of |main_control| that build...@>=
+case mmode+math_comp: {@+tail_append(new_noad());
+ type(tail)=cur_chr;scan_math(nucleus(tail));
+ } @+break;
+case mmode+limit_switch: math_limit_switch();@+break;
+
+@ @<Declare act...@>=
+static void math_limit_switch(void)
+{@+
+if (head!=tail) if (type(tail)==op_noad)
+ {@+subtype(tail)=cur_chr;return;
+ }
+print_err("Limit controls must follow a math operator");
+@.Limit controls must follow...@>
+help1("I'm ignoring this misplaced \\limits or \\nolimits command.");error();
+}
+
+@ Delimiter fields of noads are filled in by the |scan_delimiter| routine.
+The first parameter of this procedure is the |mem| address where the
+delimiter is to be placed; the second tells if this delimiter follows
+\.{\\radical} or not.
+
+@<Declare act...@>=
+static void scan_delimiter(pointer @!p, bool @!r)
+{@+if (r) scan_twenty_seven_bit_int();
+else{@+@<Get the next non-blank non-relax...@>;
+ switch (cur_cmd) {
+ case letter: case other_char: cur_val=del_code(cur_chr);@+break;
+ case delim_num: scan_twenty_seven_bit_int();@+break;
+ default:cur_val=-1;
+ }
+ }
+if (cur_val < 0) @<Report that an invalid delimiter code is being changed
+to null; set~|cur_val:=0|@>;
+small_fam(p)=(cur_val/04000000)%16;
+small_char(p)=qi((cur_val/010000)%256);
+large_fam(p)=(cur_val/256)%16;
+large_char(p)=qi(cur_val%256);
+}
+
+@ @<Report that an invalid delimiter...@>=
+{@+print_err("Missing delimiter (. inserted)");
+@.Missing delimiter...@>
+help6("I was expecting to see something like `(' or `\\{' or",@/
+ "`\\}' here. If you typed, e.g., `{' instead of `\\{', you",@/
+ "should probably delete the `{' by typing `1' now, so that",@/
+ "braces don't get unbalanced. Otherwise just proceed.",@/
+ "Acceptable delimiters are characters whose \\delcode is",@/
+ "nonnegative, or you can use `\\delimiter <delimiter code>'.");
+back_error();cur_val=0;
+}
+
+@ @<Cases of |main_control| that build...@>=
+case mmode+radical: math_radical();@+break;
+
+@ @<Declare act...@>=
+static void math_radical(void)
+{@+tail_append(get_node(radical_noad_size));
+type(tail)=radical_noad;subtype(tail)=normal;
+mem[nucleus(tail)].hh=empty_field;
+mem[subscr(tail)].hh=empty_field;
+mem[supscr(tail)].hh=empty_field;
+scan_delimiter(left_delimiter(tail), true);scan_math(nucleus(tail));
+}
+
+@ @<Cases of |main_control| that build...@>=
+case mmode+accent: case mmode+math_accent: math_ac();@+break;
+
+@ @<Declare act...@>=
+static void math_ac(void)
+{@+if (cur_cmd==accent)
+ @<Complain that the user should have said \.{\\mathaccent}@>;
+tail_append(get_node(accent_noad_size));
+type(tail)=accent_noad;subtype(tail)=normal;
+mem[nucleus(tail)].hh=empty_field;
+mem[subscr(tail)].hh=empty_field;
+mem[supscr(tail)].hh=empty_field;
+math_type(accent_chr(tail))=math_char;
+scan_fifteen_bit_int();
+character(accent_chr(tail))=qi(cur_val%256);
+if ((cur_val >= var_code)&&fam_in_range) fam(accent_chr(tail))=cur_fam;
+else fam(accent_chr(tail))=(cur_val/256)%16;
+scan_math(nucleus(tail));
+}
+
+@ @<Complain that the user should have said \.{\\mathaccent}@>=
+{@+print_err("Please use ");print_esc("mathaccent");
+print(" for accents in math mode");
+@.Please use \\mathaccent...@>
+help2("I'm changing \\accent to \\mathaccent here; wish me luck.",@/
+ "(Accents are not the same in formulas as they are in text.)");
+error();
+}
+
+@ @<Cases of |main_control| that build...@>=
+case mmode+vcenter: {@+scan_spec(vcenter_group, false);normal_paragraph();
+ push_nest();mode=-vmode;prev_depth=ignore_depth;
+ if (every_vbox!=null) begin_token_list(every_vbox, every_vbox_text);
+ } @+break;
+
+@ @<Cases of |handle...@>=
+case vcenter_group: {@+end_graf();unsave();save_ptr=save_ptr-2;
+ p=vpack(link(head), saved(1), saved(0));pop_nest();
+ tail_append(new_noad());type(tail)=vcenter_noad;
+ math_type(nucleus(tail))=sub_box;info(nucleus(tail))=p;
+ } @+break;
+
+@ The routine that inserts a |style_node| holds no surprises.
+
+@<Put each...@>=
+primitive("displaystyle", math_style, display_style);
+@!@:display\_style\_}{\.{\\displaystyle} primitive@>
+primitive("textstyle", math_style, text_style);
+@!@:text\_style\_}{\.{\\textstyle} primitive@>
+primitive("scriptstyle", math_style, script_style);
+@!@:script\_style\_}{\.{\\scriptstyle} primitive@>
+primitive("scriptscriptstyle", math_style, script_script_style);
+@!@:script\_script\_style\_}{\.{\\scriptscriptstyle} primitive@>
+
+@ @<Cases of |print_cmd_chr|...@>=
+case math_style: print_style(chr_code);@+break;
+
+@ @<Cases of |main_control| that build...@>=
+case mmode+math_style: tail_append(new_style(cur_chr))@;@+break;
+case mmode+non_script: {@+tail_append(new_glue(zero_glue));
+ subtype(tail)=cond_math_glue;
+ } @+break;
+case mmode+math_choice: append_choices();@+break;
+
+@ The routine that scans the four mlists of a \.{\\mathchoice} is very
+much like the routine that builds discretionary nodes.
+
+@<Declare act...@>=
+static void append_choices(void)
+{@+tail_append(new_choice());incr(save_ptr);saved(-1)=0;
+push_math(math_choice_group);scan_left_brace();
+}
+
+@ @<Cases of |handle_right_brace|...@>=
+case math_choice_group: build_choices();@+break;
+
+@ @<Declare act...@>=
+@t\4@>@<Declare the function called |fin_mlist|@>@t@>@;@/
+static void build_choices(void)
+{@+
+pointer p; /*the current mlist*/
+unsave();p=fin_mlist(null);
+switch (saved(-1)) {
+case 0: display_mlist(tail)=p;@+break;
+case 1: text_mlist(tail)=p;@+break;
+case 2: script_mlist(tail)=p;@+break;
+case 3: {@+script_script_mlist(tail)=p;decr(save_ptr);return;
+ }
+} /*there are no other cases*/
+incr(saved(-1));push_math(math_choice_group);scan_left_brace();
+}
+
+@ Subscripts and superscripts are attached to the previous nucleus by the
+@^superscripts@>@^subscripts@>
+action procedure called |sub_sup|. We use the facts that |sub_mark==sup_mark+1|
+and |subscr(p)==supscr(p)+1|.
+
+@<Cases of |main_control| that build...@>=
+case mmode+sub_mark: case mmode+sup_mark: sub_sup();@+break;
+
+@ @<Declare act...@>=
+static void sub_sup(void)
+{@+small_number t; /*type of previous sub/superscript*/
+pointer @!p; /*field to be filled by |scan_math|*/
+t=empty;p=null;
+if (tail!=head) if (scripts_allowed(tail))
+ {@+p=supscr(tail)+cur_cmd-sup_mark; /*|supscr| or |subscr|*/
+ t=math_type(p);
+ }
+if ((p==null)||(t!=empty)) @<Insert a dummy noad to be sub/superscripted@>;
+scan_math(p);
+}
+
+@ @<Insert a dummy...@>=
+{@+tail_append(new_noad());
+p=supscr(tail)+cur_cmd-sup_mark; /*|supscr| or |subscr|*/
+if (t!=empty)
+ {@+if (cur_cmd==sup_mark)
+ {@+print_err("Double superscript");
+@.Double superscript@>
+ help1("I treat `x^1^2' essentially like `x^1{}^2'.");
+ }
+ else{@+print_err("Double subscript");
+@.Double subscript@>
+ help1("I treat `x_1_2' essentially like `x_1{}_2'.");
+ }
+ error();
+ }
+}
+
+@ An operation like `\.{\\over}' causes the current mlist to go into a
+state of suspended animation: |incompleat_noad| points to a |fraction_noad|
+that contains the mlist-so-far as its numerator, while the denominator
+is yet to come. Finally when the mlist is finished, the denominator will
+go into the incompleat fraction noad, and that noad will become the
+whole formula, unless it is surrounded by `\.{\\left}' and `\.{\\right}'
+delimiters.
+
+@d above_code 0 /* `\.{\\above}' */
+@d over_code 1 /* `\.{\\over}' */
+@d atop_code 2 /* `\.{\\atop}' */
+@d delimited_code 3 /* `\.{\\abovewithdelims}', etc.*/
+
+@<Put each...@>=
+primitive("above", above, above_code);@/
+@!@:above\_}{\.{\\above} primitive@>
+primitive("over", above, over_code);@/
+@!@:over\_}{\.{\\over} primitive@>
+primitive("atop", above, atop_code);@/
+@!@:atop\_}{\.{\\atop} primitive@>
+primitive("abovewithdelims", above, delimited_code+above_code);@/
+@!@:above\_with\_delims\_}{\.{\\abovewithdelims} primitive@>
+primitive("overwithdelims", above, delimited_code+over_code);@/
+@!@:over\_with\_delims\_}{\.{\\overwithdelims} primitive@>
+primitive("atopwithdelims", above, delimited_code+atop_code);
+@!@:atop\_with\_delims\_}{\.{\\atopwithdelims} primitive@>
+
+@ @<Cases of |print_cmd_chr|...@>=
+case above: switch (chr_code) {
+ case over_code: print_esc("over");@+break;
+ case atop_code: print_esc("atop");@+break;
+ case delimited_code+above_code: print_esc("abovewithdelims");@+break;
+ case delimited_code+over_code: print_esc("overwithdelims");@+break;
+ case delimited_code+atop_code: print_esc("atopwithdelims");@+break;
+ default:print_esc("above");
+ } @+break;
+
+@ @<Cases of |main_control| that build...@>=
+case mmode+above: math_fraction();@+break;
+
+@ @<Declare act...@>=
+static void math_fraction(void)
+{@+small_number c; /*the type of generalized fraction we are scanning*/
+c=cur_chr;
+if (incompleat_noad!=null)
+ @<Ignore the fraction operation and complain about this ambiguous case@>@;
+else{@+incompleat_noad=get_node(fraction_noad_size);
+ type(incompleat_noad)=fraction_noad;
+ subtype(incompleat_noad)=normal;
+ math_type(numerator(incompleat_noad))=sub_mlist;
+ info(numerator(incompleat_noad))=link(head);
+ mem[denominator(incompleat_noad)].hh=empty_field;
+ mem[left_delimiter(incompleat_noad)].qqqq=null_delimiter;
+ mem[right_delimiter(incompleat_noad)].qqqq=null_delimiter;@/
+ link(head)=null;tail=head;
+ @<Use code |c| to distinguish between generalized fractions@>;
+ }
+}
+
+@ @<Use code |c|...@>=
+if (c >= delimited_code)
+ {@+scan_delimiter(left_delimiter(incompleat_noad), false);
+ scan_delimiter(right_delimiter(incompleat_noad), false);
+ }
+switch (c%delimited_code) {
+case above_code: {@+scan_normal_dimen;
+ thickness(incompleat_noad)=cur_val;
+ } @+break;
+case over_code: thickness(incompleat_noad)=default_code;@+break;
+case atop_code: thickness(incompleat_noad)=0;
+} /*there are no other cases*/
+
+@ @<Ignore the fraction...@>=
+{@+if (c >= delimited_code)
+ {@+scan_delimiter(garbage, false);scan_delimiter(garbage, false);
+ }
+if (c%delimited_code==above_code) scan_normal_dimen;
+print_err("Ambiguous; you need another { and }");
+@.Ambiguous...@>
+help3("I'm ignoring this fraction specification, since I don't",@/
+ "know whether a construction like `x \\over y \\over z'",@/
+ "means `{x \\over y} \\over z' or `x \\over {y \\over z}'.");
+error();
+}
+
+@ At the end of a math formula or subformula, the |fin_mlist| routine is
+called upon to return a pointer to the newly completed mlist, and to
+pop the nest back to the enclosing semantic level. The parameter to
+|fin_mlist|, if not null, points to a |right_noad| that ends the
+current mlist; this |right_noad| has not yet been appended.
+
+@<Declare the function called |fin_mlist|@>=
+static pointer fin_mlist(pointer @!p)
+{@+pointer q; /*the mlist to return*/
+if (incompleat_noad!=null) @<Compleat the incompleat noad@>@;
+else{@+link(tail)=p;q=link(head);
+ }
+pop_nest();return q;
+}
+
+@ @<Compleat...@>=
+{@+math_type(denominator(incompleat_noad))=sub_mlist;
+info(denominator(incompleat_noad))=link(head);
+if (p==null) q=incompleat_noad;
+else{@+q=info(numerator(incompleat_noad));
+ if ((type(q)!=left_noad)||(delim_ptr==null)) confusion("right");
+@:this can't happen right}{\quad right@>
+ info(numerator(incompleat_noad))=link(delim_ptr);
+ link(delim_ptr)=incompleat_noad;link(incompleat_noad)=p;
+ }
+}
+
+@ Now at last we're ready to see what happens when a right brace occurs
+in a math formula. Two special cases are simplified here: Braces are effectively
+removed when they surround a single Ord without sub/superscripts, or when they
+surround an accent that is the nucleus of an Ord atom.
+
+@<Cases of |handle...@>=
+case math_group: {@+unsave();decr(save_ptr);@/
+ math_type(saved(0))=sub_mlist;p=fin_mlist(null);info(saved(0))=p;
+ if (p!=null) if (link(p)==null)
+ if (type(p)==ord_noad)
+ {@+if (math_type(subscr(p))==empty)
+ if (math_type(supscr(p))==empty)
+ {@+mem[saved(0)].hh=mem[nucleus(p)].hh;
+ free_node(p, noad_size);
+ }
+ }
+ else if (type(p)==accent_noad) if (saved(0)==nucleus(tail))
+ if (type(tail)==ord_noad) @<Replace the tail of the list by |p|@>;
+ } @+break;
+
+@ @<Replace the tail...@>=
+{@+q=head;while (link(q)!=tail) q=link(q);
+link(q)=p;free_node(tail, noad_size);tail=p;
+}
+
+@ We have dealt with all constructions of math mode except `\.{\\left}' and
+`\.{\\right}', so the picture is completed by the following sections of
+the program.
+
+@<Put each...@>=
+primitive("left", left_right, left_noad);
+@!@:left\_}{\.{\\left} primitive@>
+primitive("right", left_right, right_noad);
+@!@:right\_}{\.{\\right} primitive@>
+text(frozen_right)=text(cur_val);eqtb[frozen_right]=eqtb[cur_val];
+
+@ @<Cases of |print_cmd_chr|...@>=
+case left_right: if (chr_code==left_noad) print_esc("left")
+@/@<Cases of |left_right| for |print_cmd_chr|@>;@/
+else print_esc("right");@+break;
+
+@ @<Cases of |main_control| that build...@>=
+case mmode+left_right: math_left_right();@+break;
+
+@ @<Declare act...@>=
+static void math_left_right(void)
+{@+small_number t; /*|left_noad| or |right_noad|*/
+pointer @!p; /*new noad*/
+pointer @!q; /*resulting mlist*/
+t=cur_chr;
+if ((t!=left_noad)&&(cur_group!=math_left_group))
+ @<Try to recover from mismatched \.{\\right}@>@;
+else{@+p=new_noad();type(p)=t;
+ scan_delimiter(delimiter(p), false);
+ if (t==middle_noad)
+ {@+type(p)=right_noad;subtype(p)=middle_noad;
+ }
+ if (t==left_noad) q=p;
+ else{@+q=fin_mlist(p);unsave(); /*end of |math_left_group|*/
+ }
+ if (t!=right_noad)
+ {@+push_math(math_left_group);link(head)=q;tail=p;
+ delim_ptr=p;
+ }
+ else{@+
+ tail_append(new_noad());type(tail)=inner_noad;
+ math_type(nucleus(tail))=sub_mlist;
+ info(nucleus(tail))=q;
+ }
+ }
+}
+
+@ @<Try to recover from mismatch...@>=
+{@+if (cur_group==math_shift_group)
+ {@+scan_delimiter(garbage, false);
+ print_err("Extra ");
+ if (t==middle_noad)
+ {@+print_esc("middle");
+@.Extra \\middle.@>
+ help1("I'm ignoring a \\middle that had no matching \\left.");
+ }
+ else{@+print_esc("right");
+@.Extra \\right.@>
+ help1("I'm ignoring a \\right that had no matching \\left.");
+ }
+ error();
+ }
+else off_save();
+}
+
+@ Here is the only way out of math mode.
+
+@<Cases of |main_control| that build...@>=
+case mmode+math_shift: if (cur_group==math_shift_group) after_math();
+ else off_save();@+break;
+
+@ @<Declare act...@>=
+static void after_math(void)
+{@+bool l; /*`\.{\\leqno}' instead of `\.{\\eqno}'*/
+bool @!danger; /*not enough symbol fonts are present*/
+int @!m; /*|mmode| or |-mmode|*/
+pointer @!p; /*the formula*/
+pointer @!a; /*box containing equation number*/
+@<Local variables for finishing a displayed formula@>@;
+danger=false;
+@<Check that the necessary fonts for math symbols are present; if not, flush
+the current math lists and set |danger:=true|@>;
+m=mode;l=false;p=fin_mlist(null); /*this pops the nest*/
+if (mode==-m) /*end of equation number*/
+ {@+@<Check that another \.\$ follows@>;
+ cur_mlist=p;cur_style=text_style;mlist_penalties=false;
+ mlist_to_hlist();a=hpack(link(temp_head), natural);
+ unsave();decr(save_ptr); /*now |cur_group==math_shift_group|*/
+ if (saved(0)==1) l=true;
+ danger=false;
+ @<Check that the necessary fonts for math symbols are present; if not, flush
+the current math lists and set |danger:=true|@>;
+ m=mode;p=fin_mlist(null);
+ }
+else a=null;
+if (m < 0) @<Finish math in text@>@;
+else{@+if (a==null) @<Check that another \.\$ follows@>;
+ @<Finish displayed math@>;
+ }
+}
+
+@ @<Check that the necessary fonts...@>=
+if ((font_params[fam_fnt(2+text_size)] < total_mathsy_params)||@|
+ (font_params[fam_fnt(2+script_size)] < total_mathsy_params)||@|
+ (font_params[fam_fnt(2+script_script_size)] < total_mathsy_params))
+ {@+print_err("Math formula deleted: Insufficient symbol fonts");@/
+@.Math formula deleted...@>
+ help3("Sorry, but I can't typeset math unless \\textfont 2",@/
+ "and \\scriptfont 2 and \\scriptscriptfont 2 have all",@/
+ "the \\fontdimen values needed in math symbol fonts.");
+ error();flush_math();danger=true;
+ }
+else if ((font_params[fam_fnt(3+text_size)] < total_mathex_params)||@|
+ (font_params[fam_fnt(3+script_size)] < total_mathex_params)||@|
+ (font_params[fam_fnt(3+script_script_size)] < total_mathex_params))
+ {@+print_err("Math formula deleted: Insufficient extension fonts");@/
+ help3("Sorry, but I can't typeset math unless \\textfont 3",@/
+ "and \\scriptfont 3 and \\scriptscriptfont 3 have all",@/
+ "the \\fontdimen values needed in math extension fonts.");
+ error();flush_math();danger=true;
+ }
+
+@ The |unsave| is done after everything else here; hence an appearance of
+`\.{\\mathsurround}' inside of `\.{\$...\$}' affects the spacing at these
+particular \.\$'s. This is consistent with the conventions of
+`\.{\$\$...\$\$}', since `\.{\\abovedisplayskip}' inside a display affects the
+space above that display.
+
+@<Finish math in text@>=
+{@+tail_append(new_math(math_surround, before));
+cur_mlist=p;cur_style=text_style;mlist_penalties=(mode > 0);mlist_to_hlist();
+link(tail)=link(temp_head);
+while (link(tail)!=null) tail=link(tail);
+tail_append(new_math(math_surround, after));
+space_factor=1000;unsave();
+}
+
+@ \TeX\ gets to the following part of the program when the first `\.\$' ending
+a display has been scanned.
+
+@<Check that another \.\$ follows@>=
+{@+get_x_token();
+if (cur_cmd!=math_shift)
+ {@+print_err("Display math should end with $$");
+@.Display math...with \$\$@>
+ help2("The `$' that I just saw supposedly matches a previous `$$'.",@/
+ "So I shall assume that you typed `$$' both times.");
+ back_error();
+ }
+}
+
+@ We have saved the worst for last: The fussiest part of math mode processing
+occurs when a displayed formula is being centered and placed with an optional
+equation number.
+
+@<Local variables for finishing...@>=
+pointer @!b; /*box containing the equation*/
+scaled @!w; /*width of the equation*/
+scaled @!z; /*width of the line*/
+scaled @!e; /*width of equation number*/
+scaled @!q; /*width of equation number plus space to separate from equation*/
+scaled @!d; /*displacement of equation in the line*/
+scaled @!s; /*move the line right this much*/
+small_number @!g1, @!g2; /*glue parameter codes for before and after*/
+pointer @!r; /*kern node used to position the display*/
+pointer @!t; /*tail of adjustment list*/
+
+@ At this time |p| points to the mlist for the formula; |a| is either
+|null| or it points to a box containing the equation number; and we are in
+vertical mode (or internal vertical mode).
+
+@<Finish displayed math@>=
+cur_mlist=p;cur_style=display_style;mlist_penalties=false;
+mlist_to_hlist();p=link(temp_head);@/
+adjust_tail=adjust_head;b=hpack(p, natural);p=list_ptr(b);
+t=adjust_tail;adjust_tail=null;@/
+w=width(b);z=display_width;s=display_indent;
+if ((a==null)||danger)
+ {@+e=0;q=0;
+ }
+else{@+e=width(a);q=e+math_quad(text_size);
+ }
+if (w+q > z)
+ @<Squeeze the equation as much as possible; if there is an equation number
+that should go on a separate line by itself, set~|e:=0|@>;
+@<Determine the displacement, |d|, of the left edge of the equation, with
+respect to the line size |z|, assuming that |l=false|@>;
+@<Append the glue or equation number preceding the display@>;
+@<Append the display and perhaps also the equation number@>;
+@<Append the glue or equation number following the display@>;
+resume_after_display()
+
+@ @<Declare act...@>=
+static void resume_after_display(void)
+{@+if (cur_group!=math_shift_group) confusion("display");
+@:this can't happen display}{\quad display@>
+unsave();prev_graf=prev_graf+3;
+push_nest();mode=hmode;space_factor=1000;set_cur_lang;clang=cur_lang;
+prev_graf=(norm_min(left_hyphen_min)*0100+norm_min(right_hyphen_min))
+ *0200000+cur_lang;
+@<Scan an optional space@>;
+if (nest_ptr==1) build_page();
+}
+
+@ The user can force the equation number to go on a separate line
+by causing its width to be zero.
+
+@<Squeeze the equation as much as possible...@>=
+{@+if ((e!=0)&&((w-total_shrink[normal]+q <= z)||@|
+ (total_shrink[fil]!=0)||(total_shrink[fill]!=0)||
+ (total_shrink[filll]!=0)))
+ {@+free_node(b, box_node_size);
+ b=hpack(p, z-q, exactly);
+ }
+else{@+e=0;
+ if (w > z)
+ {@+free_node(b, box_node_size);
+ b=hpack(p, z, exactly);
+ }
+ }
+w=width(b);
+}
+
+@ We try first to center the display without regard to the existence of
+the equation number. If that would make it too close (where ``too close''
+means that the space between display and equation number is less than the
+width of the equation number), we either center it in the remaining space
+or move it as far from the equation number as possible. The latter alternative
+is taken only if the display begins with glue, since we assume that the
+user put glue there to control the spacing precisely.
+
+@<Determine the displacement, |d|, of the left edge of the equation...@>=
+d=half(z-w);
+if ((e > 0)&&(d < 2*e)) /*too close*/
+ {@+d=half(z-w-e);
+ if (p!=null) if (!is_char_node(p)) if (type(p)==glue_node) d=0;
+ }
+
+@ If the equation number is set on a line by itself, either before or
+after the formula, we append an infinite penalty so that no page break will
+separate the display from its number; and we use the same size and
+displacement for all three potential lines of the display, even though
+`\.{\\parshape}' may specify them differently.
+
+@<Append the glue or equation number preceding the display@>=
+tail_append(new_penalty(pre_display_penalty));@/
+if ((d+s <= pre_display_size)||l) /*not enough clearance*/
+ {@+g1=above_display_skip_code;g2=below_display_skip_code;
+ }
+else{@+g1=above_display_short_skip_code;
+ g2=below_display_short_skip_code;
+ }
+if (l&&(e==0)) /*it follows that |type(a)==hlist_node|*/
+ {@+shift_amount(a)=s;append_to_vlist(a);
+ tail_append(new_penalty(inf_penalty));
+ }
+else tail_append(new_param_glue(g1))
+
+@ @<Append the display and perhaps also the equation number@>=
+if (e!=0)
+ {@+r=new_kern(z-w-e-d);
+ if (l)
+ {@+link(a)=r;link(r)=b;b=a;d=0;
+ }
+ else{@+link(b)=r;link(r)=a;
+ }
+ b=hpack(b, natural);
+ }
+shift_amount(b)=s+d;append_to_vlist(b)
+
+@ @<Append the glue or equation number following the display@>=
+if ((a!=null)&&(e==0)&&!l)
+ {@+tail_append(new_penalty(inf_penalty));
+ shift_amount(a)=s+z-width(a);
+ append_to_vlist(a);
+ g2=0;
+ }
+if (t!=adjust_head) /*migrating material comes after equation number*/
+ {@+link(tail)=link(adjust_head);tail=t;
+ }
+tail_append(new_penalty(post_display_penalty));
+if (g2 > 0) tail_append(new_param_glue(g2))
+
+@ When \.{\\halign} appears in a display, the alignment routines operate
+essentially as they do in vertical mode. Then the following program is
+activated, with |p| and |q| pointing to the beginning and end of the
+resulting list, and with |aux_save| holding the |prev_depth| value.
+
+@<Finish an alignment in a display@>=
+{@+do_assignments();
+if (cur_cmd!=math_shift) @<Pontificate about improper alignment in display@>@;
+else@<Check that another \.\$ follows@>;
+pop_nest();
+tail_append(new_penalty(pre_display_penalty));
+tail_append(new_param_glue(above_display_skip_code));
+link(tail)=p;
+if (p!=null) tail=q;
+tail_append(new_penalty(post_display_penalty));
+tail_append(new_param_glue(below_display_skip_code));
+prev_depth=aux_save.sc;resume_after_display();
+}
+
+@ @<Pontificate...@>=
+{@+print_err("Missing $$ inserted");
+@.Missing {\$\$} inserted@>
+help2("Displays can use special alignments (like \\eqalignno)",@/
+ "only if nothing but the alignment itself is between $$'s.");
+back_error();
+}
+
+@* Mode-independent processing.
+The long |main_control| procedure has now been fully specified, except for
+certain activities that are independent of the current mode. These activities
+do not change the current vlist or hlist or mlist; if they change anything,
+it is the value of a parameter or the meaning of a control sequence.
+
+Assignments to values in |eqtb| can be global or local. Furthermore, a
+control sequence can be defined to be `\.{\\long}', `\.{\\protected}',
+or `\.{\\outer}', and it might or might not be expanded. The prefixes
+`\.{\\global}', `\.{\\long}', `\.{\\protected}',
+and `\.{\\outer}' can occur in any order. Therefore we assign binary numeric
+codes, making it possible to accumulate the union of all specified prefixes
+by adding the corresponding codes. (\PASCAL's |set| operations could also
+have been used.)
+
+@<Put each...@>=
+primitive("long", prefix, 1);
+@!@:long\_}{\.{\\long} primitive@>
+primitive("outer", prefix, 2);
+@!@:outer\_}{\.{\\outer} primitive@>
+primitive("global", prefix, 4);
+@!@:global\_}{\.{\\global} primitive@>
+primitive("def", def, 0);
+@!@:def\_}{\.{\\def} primitive@>
+primitive("gdef", def, 1);
+@!@:gdef\_}{\.{\\gdef} primitive@>
+primitive("edef", def, 2);
+@!@:edef\_}{\.{\\edef} primitive@>
+primitive("xdef", def, 3);
+@!@:xdef\_}{\.{\\xdef} primitive@>
+
+@ @<Cases of |print_cmd_chr|...@>=
+case prefix: if (chr_code==1) print_esc("long");
+ else if (chr_code==2) print_esc("outer")
+ @/@<Cases of |prefix| for |print_cmd_chr|@>;@/
+ else print_esc("global");@+break;
+case def: if (chr_code==0) print_esc("def");
+ else if (chr_code==1) print_esc("gdef");
+ else if (chr_code==2) print_esc("edef");
+ else print_esc("xdef");@+break;
+
+@ Every prefix, and every command code that might or might not be prefixed,
+calls the action procedure |prefixed_command|. This routine accumulates
+a sequence of prefixes until coming to a non-prefix, then it carries out
+the command.
+
+@<Cases of |main_control| that don't...@>=
+any_mode(toks_register):
+any_mode(assign_toks):
+any_mode(assign_int):
+any_mode(assign_dimen):
+any_mode(assign_glue):
+any_mode(assign_mu_glue):
+any_mode(assign_font_dimen):
+any_mode(assign_font_int):
+any_mode(set_aux):
+any_mode(set_prev_graf):
+any_mode(set_page_dimen):
+any_mode(set_page_int):
+any_mode(set_box_dimen):
+any_mode(set_shape):
+any_mode(def_code):
+any_mode(def_family):
+any_mode(set_font):
+any_mode(def_font):
+any_mode(internal_register):
+any_mode(advance):
+any_mode(multiply):
+any_mode(divide):
+any_mode(prefix):
+any_mode(let):
+any_mode(shorthand_def):
+any_mode(read_to_cs):
+any_mode(def):
+any_mode(set_box):
+any_mode(hyph_data):
+any_mode(set_interaction): prefixed_command();@+break;
+
+@ If the user says, e.g., `\.{\\global\\global}', the redundancy is
+silently accepted.
+
+@<Declare act...@>=
+@t\4@>@<Declare subprocedures for |prefixed_command|@>@t@>@;@/
+static void prefixed_command(void)
+{@+
+small_number a; /*accumulated prefix codes so far*/
+internal_font_number @!f; /*identifies a font*/
+int @!j; /*index into a \.{\\parshape} specification*/
+font_index @!k; /*index into |font_info|*/
+pointer @!p, @!q; /*for temporary short-term use*/
+int @!n; /*ditto*/
+bool @!e; /*should a definition be expanded? or was \.{\\let} not done?*/
+a=0;
+while (cur_cmd==prefix)
+ {@+if (!odd(a/cur_chr)) a=a+cur_chr;
+ @<Get the next non-blank non-relax...@>;
+ if (cur_cmd <= max_non_prefixed_command)
+ @<Discard erroneous prefixes and |return|@>;
+ if (tracing_commands > 2) if (eTeX_ex) show_cur_cmd_chr();
+ }
+@<Discard the prefixes \.{\\long} and \.{\\outer} if they are irrelevant@>;
+@<Adjust \(f)for the setting of \.{\\globaldefs}@>;
+switch (cur_cmd) {
+@t\4@>@<Assignments@>@;
+default:confusion("prefix");
+@:this can't happen prefix}{\quad prefix@>
+}
+done: @<Insert a token saved by \.{\\afterassignment}, if any@>;
+}
+
+@ @<Discard erroneous...@>=
+{@+print_err("You can't use a prefix with `");
+@.You can't use a prefix with x@>
+print_cmd_chr(cur_cmd, cur_chr);print_char('\'');
+help1("I'll pretend you didn't say \\long or \\outer or \\global.");
+if (eTeX_ex) help_line[0]=@|
+ "I'll pretend you didn't say \\long or \\outer or \\global or \\protected.";
+back_error();return;
+}
+
+@ @<Discard the prefixes...@>=
+if (a >= 8)
+ {@+j=protected_token;a=a-8;
+ }
+else j=0;
+if ((cur_cmd!=def)&&((a%4!=0)||(j!=0)))
+ {@+print_err("You can't use `");print_esc("long");print("' or `");
+ print_esc("outer");
+ help1("I'll pretend you didn't say \\long or \\outer here.");
+ if (eTeX_ex)
+ {@+help_line[0]=@|
+ "I'll pretend you didn't say \\long or \\outer or \\protected here.";
+ print("' or `");print_esc("protected");
+ }
+ print("' with `");
+@.You can't use \\long...@>
+ print_cmd_chr(cur_cmd, cur_chr);print_char('\'');
+ error();
+ }
+
+@ The previous routine does not have to adjust |a| so that |a%4==0|,
+since the following routines test for the \.{\\global} prefix as follows.
+
+@d global (a >= 4)
+@d define(A, B, C) if (global) geq_define(A, B, C);@+else eq_define(A, B, C)
+@d word_define(A, B) if (global) geq_word_define(A, B);@+else eq_word_define(A, B)
+
+@<Adjust \(f)for the setting of \.{\\globaldefs}@>=
+if (global_defs!=0)
+ if (global_defs < 0)
+ {@+if (global) a=a-4;
+ }
+ else{@+if (!global) a=a+4;
+ }
+
+@ When a control sequence is to be defined, by \.{\\def} or \.{\\let} or
+something similar, the |get_r_token| routine will substitute a special
+control sequence for a token that is not redefinable.
+
+@<Declare subprocedures for |prefixed_command|@>=
+static void get_r_token(void)
+{@+
+restart: @/do@+{get_token();
+}@+ while (!(cur_tok!=space_token));
+if ((cur_cs==0)||(cur_cs > frozen_control_sequence))
+ {@+print_err("Missing control sequence inserted");
+@.Missing control...@>
+ help5("Please don't say `\\def cs{...}', say `\\def\\cs{...}'.",@/
+ "I've inserted an inaccessible control sequence so that your",@/
+ "definition will be completed without mixing me up too badly.",@/
+ "You can recover graciously from this error, if you're",@/
+ "careful; see exercise 27.2 in The TeXbook.");
+@:TeXbook}{\sl The \TeX book@>
+ if (cur_cs==0) back_input();
+ cur_tok=cs_token_flag+frozen_protection;ins_error();goto restart;
+ }
+}
+
+@ @<Initialize table entries...@>=
+text(frozen_protection)=s_no("inaccessible");
+@.inaccessible@>
+
+@ Here's an example of the way many of the following routines operate.
+(Unfortunately, they aren't all as simple as this.)
+
+@<Assignments@>=
+case set_font: define(cur_font_loc, data, cur_chr);@+break;
+
+@ When a |def| command has been scanned,
+|cur_chr| is odd if the definition is supposed to be global, and
+|cur_chr >= 2| if the definition is supposed to be expanded.
+
+@<Assignments@>=
+case def: {@+uint32_t def_fl;
+ if (odd(cur_chr)&&!global&&(global_defs >= 0)) a=a+4;
+ e=(cur_chr >= 2);get_r_token();p=cur_cs; def_fl=cur_file_line;
+ q=scan_toks(true, e);
+ if (j!=0)
+ {@+q=get_avail();info(q)=j;link(q)=link(def_ref);
+ link(def_ref)=q;
+ }
+ define(p, call+(a%4), def_ref);
+ fl_mem[def_ref]=def_fl;
+ } @+break;
+
+@ Both \.{\\let} and \.{\\futurelet} share the command code |let|.
+
+@<Put each...@>=
+primitive("let", let, normal);@/
+@!@:let\_}{\.{\\let} primitive@>
+primitive("futurelet", let, normal+1);@/
+@!@:future\_let\_}{\.{\\futurelet} primitive@>
+
+@ @<Cases of |print_cmd_chr|...@>=
+case let: if (chr_code!=normal) print_esc("futurelet");@+else print_esc("let");@+break;
+
+@ @<Assignments@>=
+case let: {@+n=cur_chr;
+ get_r_token();p=cur_cs;
+ if (n==normal)
+ {@+@/do@+{get_token();
+ }@+ while (!(cur_cmd!=spacer));
+ if (cur_tok==other_token+'=')
+ {@+get_token();
+ if (cur_cmd==spacer) get_token();
+ }
+ }
+ else{@+get_token();q=cur_tok;get_token();back_input();
+ cur_tok=q;back_input(); /*look ahead, then back up*/
+ } /*note that |back_input| doesn't affect |cur_cmd|, |cur_chr|*/
+ if (cur_cmd >= call) add_token_ref(cur_chr);
+ else if ((cur_cmd==internal_register)||(cur_cmd==toks_register))
+ if ((cur_chr < mem_bot)||(cur_chr > lo_mem_stat_max))
+ add_sa_ref(cur_chr);
+ define(p, cur_cmd, cur_chr);
+ } @+break;
+
+@ A \.{\\chardef} creates a control sequence whose |cmd| is |char_given|;
+a \.{\\mathchardef} creates a control sequence whose |cmd| is |math_given|;
+and the corresponding |chr| is the character code or math code. A \.{\\countdef}
+or \.{\\dimendef} or \.{\\skipdef} or \.{\\muskipdef} creates a control
+sequence whose |cmd| is |assign_int| or \dots\ or |assign_mu_glue|, and the
+corresponding |chr| is the |eqtb| location of the internal register in question.
+
+@d char_def_code 0 /*|shorthand_def| for \.{\\chardef}*/
+@d math_char_def_code 1 /*|shorthand_def| for \.{\\mathchardef}*/
+@d count_def_code 2 /*|shorthand_def| for \.{\\countdef}*/
+@d dimen_def_code 3 /*|shorthand_def| for \.{\\dimendef}*/
+@d skip_def_code 4 /*|shorthand_def| for \.{\\skipdef}*/
+@d mu_skip_def_code 5 /*|shorthand_def| for \.{\\muskipdef}*/
+@d toks_def_code 6 /*|shorthand_def| for \.{\\toksdef}*/
+
+@<Put each...@>=
+primitive("chardef", shorthand_def, char_def_code);@/
+@!@:char\_def\_}{\.{\\chardef} primitive@>
+primitive("mathchardef", shorthand_def, math_char_def_code);@/
+@!@:math\_char\_def\_}{\.{\\mathchardef} primitive@>
+primitive("countdef", shorthand_def, count_def_code);@/
+@!@:count\_def\_}{\.{\\countdef} primitive@>
+primitive("dimendef", shorthand_def, dimen_def_code);@/
+@!@:dimen\_def\_}{\.{\\dimendef} primitive@>
+primitive("skipdef", shorthand_def, skip_def_code);@/
+@!@:skip\_def\_}{\.{\\skipdef} primitive@>
+primitive("muskipdef", shorthand_def, mu_skip_def_code);@/
+@!@:mu\_skip\_def\_}{\.{\\muskipdef} primitive@>
+primitive("toksdef", shorthand_def, toks_def_code);@/
+@!@:toks\_def\_}{\.{\\toksdef} primitive@>
+
+@ @<Cases of |print_cmd_chr|...@>=
+case shorthand_def: switch (chr_code) {
+ case char_def_code: print_esc("chardef");@+break;
+ case math_char_def_code: print_esc("mathchardef");@+break;
+ case count_def_code: print_esc("countdef");@+break;
+ case dimen_def_code: print_esc("dimendef");@+break;
+ case skip_def_code: print_esc("skipdef");@+break;
+ case mu_skip_def_code: print_esc("muskipdef");@+break;
+ default:print_esc("toksdef");
+ } @+break;
+case char_given: {@+print_esc("char");print_hex(chr_code);
+ } @+break;
+case math_given: {@+print_esc("mathchar");print_hex(chr_code);
+ } @+break;
+
+@ We temporarily define |p| to be |relax|, so that an occurrence of |p|
+while scanning the definition will simply stop the scanning instead of
+producing an ``undefined control sequence'' error or expanding the
+previous meaning. This allows, for instance, `\.{\\chardef\\foo=123\\foo}'.
+
+@<Assignments@>=
+case shorthand_def: {@+n=cur_chr;get_r_token();p=cur_cs;define(p, relax, 256);
+ scan_optional_equals();
+ switch (n) {
+ case char_def_code: {@+scan_char_num();define(p, char_given, cur_val);
+ } @+break;
+ case math_char_def_code: {@+scan_fifteen_bit_int();define(p, math_given, cur_val);
+ } @+break;
+ default:{@+scan_register_num();
+ if (cur_val > 255)
+ {@+j=n-count_def_code; /*|int_val dotdot box_val|*/
+ if (j > mu_val) j=tok_val; /*|int_val dotdot mu_val| or |tok_val|*/
+ find_sa_element(j, cur_val, true);add_sa_ref(cur_ptr);
+ if (j==tok_val) j=toks_register;@+else j=internal_register;
+ define(p, j, cur_ptr);
+ }
+ else
+ switch (n) {
+ case count_def_code: define(p, assign_int, count_base+cur_val);@+break;
+ case dimen_def_code: define(p, assign_dimen, scaled_base+cur_val);@+break;
+ case skip_def_code: define(p, assign_glue, skip_base+cur_val);@+break;
+ case mu_skip_def_code: define(p, assign_mu_glue, mu_skip_base+cur_val);@+break;
+ case toks_def_code: define(p, assign_toks, toks_base+cur_val);
+ } /*there are no other cases*/
+ }
+ }
+ } @+break;
+
+@ @<Assignments@>=
+case read_to_cs: {@+j=cur_chr;scan_int();n=cur_val;
+ if (!scan_keyword("to"))
+@.to@>
+ {@+print_err("Missing `to' inserted");
+@.Missing `to'...@>
+ help2("You should have said `\\read<number> to \\cs'.",@/
+ "I'm going to look for the \\cs now.");error();
+ }
+ get_r_token();
+ p=cur_cs;read_toks(n, p, j);define(p, call, cur_val);
+ } @+break;
+
+@ The token-list parameters, \.{\\output} and \.{\\everypar}, etc., receive
+their values in the following way. (For safety's sake, we place an
+enclosing pair of braces around an \.{\\output} list.)
+
+@<Assignments@>=
+case toks_register: case assign_toks: {@+uint32_t def_fl=cur_file_line; q=cur_cs;
+ e=false; /*just in case, will be set |true| for sparse array elements*/
+ if (cur_cmd==toks_register)
+ if (cur_chr==mem_bot)
+ {@+scan_register_num();
+ if (cur_val > 255)
+ {@+find_sa_element(tok_val, cur_val, true);
+ cur_chr=cur_ptr;e=true;
+ }
+ else cur_chr=toks_base+cur_val;
+ }
+ else e=true;
+ p=cur_chr; /*|p==every_par_loc| or |output_routine_loc| or \dots*/
+ scan_optional_equals();
+ @<Get the next non-blank non-relax non-call token@>;
+ if (cur_cmd!=left_brace) @<If the right-hand side is a token parameter or
+token register, finish the assignment and |goto done|@>;
+ back_input();cur_cs=q;q=scan_toks(false, false);
+ if (link(def_ref)==null) /*empty list: revert to the default*/
+ {@+sa_define(p, null, p, undefined_cs, null);free_avail(def_ref);
+ }
+ else{@+if ((p==output_routine_loc)&&!e) /*enclose in curlies*/
+ {@+link(q)=get_avail();q=link(q);
+ info(q)=right_brace_token+'}';
+ fl_mem[q]=FILE_LINE(system_file, system_insert);
+ q=get_avail();info(q)=left_brace_token+'{';
+ fl_mem[q]=FILE_LINE(system_file, system_insert);
+ link(q)=link(def_ref);link(def_ref)=q;
+ }
+ sa_define(p, def_ref, p, call, def_ref);
+ fl_mem[def_ref]=def_fl;
+ }
+ } @+break;
+
+@ @<If the right-hand side is a token parameter...@>=
+if ((cur_cmd==toks_register)||(cur_cmd==assign_toks))
+ {@+if (cur_cmd==toks_register)
+ if (cur_chr==mem_bot)
+ {@+scan_register_num();
+ if (cur_val < 256) q=equiv(toks_base+cur_val);
+ else{@+find_sa_element(tok_val, cur_val, false);
+ if (cur_ptr==null) q=null;
+ else q=sa_ptr(cur_ptr);
+ }
+ }
+ else q=sa_ptr(cur_chr);
+ else q=equiv(cur_chr);
+ if (q==null) sa_define(p, null, p, undefined_cs, null);
+ else{@+add_token_ref(q);sa_define(p, q, p, call, q);
+ }
+ goto done;
+ }
+
+@ Similar routines are used to assign values to the numeric parameters.
+
+@<Assignments@>=
+case assign_int: {@+p=cur_chr;scan_optional_equals();scan_int();
+ word_define(p, cur_val);
+ } @+break;
+case assign_dimen: {@+p=cur_chr;scan_optional_equals();
+ scan_normal_dimen;word_define(p, cur_val);
+ } @+break;
+case assign_glue: case assign_mu_glue: {@+p=cur_chr;n=cur_cmd;scan_optional_equals();
+ if (n==assign_mu_glue) scan_glue(mu_val);@+else scan_glue(glue_val);
+ trap_zero_glue();
+ define(p, glue_ref, cur_val);
+ } @+break;
+
+@ When a glue register or parameter becomes zero, it will always point to
+|zero_glue| because of the following procedure. (Exception: The tabskip
+glue isn't trapped while preambles are being scanned.)
+
+@<Declare subprocedures for |prefixed_command|@>=
+static void trap_zero_glue(void)
+{@+if ((width(cur_val)==0)&&(stretch(cur_val)==0)&&(shrink(cur_val)==0))
+ {@+add_glue_ref(zero_glue);
+ delete_glue_ref(cur_val);cur_val=zero_glue;
+ }
+}
+
+@ The various character code tables are changed by the |def_code| commands,
+and the font families are declared by |def_family|.
+
+@<Put each...@>=
+primitive("catcode", def_code, cat_code_base);
+@!@:cat\_code\_}{\.{\\catcode} primitive@>
+primitive("mathcode", def_code, math_code_base);
+@!@:math\_code\_}{\.{\\mathcode} primitive@>
+primitive("lccode", def_code, lc_code_base);
+@!@:lc\_code\_}{\.{\\lccode} primitive@>
+primitive("uccode", def_code, uc_code_base);
+@!@:uc\_code\_}{\.{\\uccode} primitive@>
+primitive("sfcode", def_code, sf_code_base);
+@!@:sf\_code\_}{\.{\\sfcode} primitive@>
+primitive("delcode", def_code, del_code_base);
+@!@:del\_code\_}{\.{\\delcode} primitive@>
+primitive("textfont", def_family, math_font_base);
+@!@:text\_font\_}{\.{\\textfont} primitive@>
+primitive("scriptfont", def_family, math_font_base+script_size);
+@!@:script\_font\_}{\.{\\scriptfont} primitive@>
+primitive("scriptscriptfont", def_family, math_font_base+script_script_size);
+@!@:script\_script\_font\_}{\.{\\scriptscriptfont} primitive@>
+
+@ @<Cases of |print_cmd_chr|...@>=
+case def_code: if (chr_code==cat_code_base) print_esc("catcode");
+ else if (chr_code==math_code_base) print_esc("mathcode");
+ else if (chr_code==lc_code_base) print_esc("lccode");
+ else if (chr_code==uc_code_base) print_esc("uccode");
+ else if (chr_code==sf_code_base) print_esc("sfcode");
+ else print_esc("delcode");@+break;
+case def_family: print_size(chr_code-math_font_base);@+break;
+
+@ The different types of code values have different legal ranges; the
+following program is careful to check each case properly.
+
+@<Assignments@>=
+case def_code: {@+@<Let |n| be the largest legal code value, based on |cur_chr|@>;
+ p=cur_chr;scan_char_num();p=p+cur_val;scan_optional_equals();
+ scan_int();
+ if (((cur_val < 0)&&(p < del_code_base))||(cur_val > n))
+ {@+print_err("Invalid code (");print_int(cur_val);
+@.Invalid code@>
+ if (p < del_code_base) print("), should be in the range 0..");
+ else print("), should be at most ");
+ print_int(n);
+ help1("I'm going to use 0 instead of that illegal code value.");@/
+ error();cur_val=0;
+ }
+ if (p < math_code_base) define(p, data, cur_val);
+ else if (p < del_code_base) define(p, data, hi(cur_val));
+ else word_define(p, cur_val);
+ } @+break;
+
+@ @<Let |n| be the largest...@>=
+if (cur_chr==cat_code_base) n=max_char_code;
+else if (cur_chr==math_code_base) n=0100000;
+else if (cur_chr==sf_code_base) n=077777;
+else if (cur_chr==del_code_base) n=077777777;
+else n=255
+
+@ @<Assignments@>=
+case def_family: {@+p=cur_chr;scan_four_bit_int();p=p+cur_val;
+ scan_optional_equals();scan_font_ident();define(p, data, cur_val);
+ } @+break;
+
+@ Next we consider changes to \TeX's numeric registers.
+
+@<Assignments@>=
+case internal_register: case advance:
+ case multiply: case divide: do_register_command(a);@+break;
+
+@ We use the fact that |internal_register < advance < multiply < divide|.
+
+@<Declare subprocedures for |prefixed_command|@>=
+static void do_register_command(small_number @!a)
+{@+
+pointer l, @!q, @!r, @!s; /*for list manipulation*/
+int @!p; /*type of register involved*/
+bool @!e; /*does |l| refer to a sparse array element?*/
+int @!w; /*integer or dimen value of |l|*/
+q=cur_cmd;
+e=false; /*just in case, will be set |true| for sparse array elements*/
+@<Compute the register location |l| and its type |p|; but |return| if invalid@>;
+if (q==internal_register) scan_optional_equals();
+else if (scan_keyword("by")) do_nothing; /*optional `\.{by}'*/
+@.by@>
+arith_error=false;
+if (q < multiply) @<Compute result of |register| or |advance|, put it in |cur_val|@>@;
+else@<Compute result of |multiply| or |divide|, put it in |cur_val|@>;
+if (arith_error)
+ {@+print_err("Arithmetic overflow");
+@.Arithmetic overflow@>
+ help2("I can't carry out that multiplication or division,",@/
+ "since the result is out of range.");
+ if (p >= glue_val) delete_glue_ref(cur_val);
+ error();return;
+ }
+if (p < glue_val) sa_word_define(l, cur_val);
+else{@+trap_zero_glue();sa_define(l, cur_val, l, glue_ref, cur_val);
+ }
+}
+
+@ Here we use the fact that the consecutive codes |int_val dotdot mu_val| and
+|assign_int dotdot assign_mu_glue| correspond to each other nicely.
+
+@<Compute the register location |l| and its type |p|...@>=
+{@+if (q!=internal_register)
+ {@+get_x_token();
+ if ((cur_cmd >= assign_int)&&(cur_cmd <= assign_mu_glue))
+ {@+l=cur_chr;p=cur_cmd-assign_int;goto found;
+ }
+ if (cur_cmd!=internal_register)
+ {@+print_err("You can't use `");print_cmd_chr(cur_cmd, cur_chr);
+@.You can't use x after ...@>
+ print("' after ");print_cmd_chr(q, 0);
+ help1("I'm forgetting what you said and not changing anything.");
+ error();return;
+ }
+ }
+if ((cur_chr < mem_bot)||(cur_chr > lo_mem_stat_max))
+ {@+l=cur_chr;p=sa_type(l);e=true;
+ }
+else{@+p=cur_chr-mem_bot;scan_register_num();
+ if (cur_val > 255)
+ {@+find_sa_element(p, cur_val, true);l=cur_ptr;e=true;
+ }
+ else
+switch (p) {
+case int_val: l=cur_val+count_base;@+break;
+case dimen_val: l=cur_val+scaled_base;@+break;
+case glue_val: l=cur_val+skip_base;@+break;
+case mu_val: l=cur_val+mu_skip_base;
+} /*there are no other cases*/
+ }
+}
+found: if (p < glue_val) @+if (e) w=sa_int(l);@+else w=eqtb[l].i;
+else if (e) s=sa_ptr(l);@+else s=equiv(l)
+
+@ @<Compute result of |register| or |advance|...@>=
+if (p < glue_val)
+ {@+if (p==int_val) scan_int();@+else scan_normal_dimen;
+ if (q==advance) cur_val=cur_val+w;
+ }
+else{@+scan_glue(p);
+ if (q==advance) @<Compute the sum of two glue specs@>;
+ }
+
+@ @<Compute the sum of two glue specs@>=
+{@+q=new_spec(cur_val);r=s;
+delete_glue_ref(cur_val);
+width(q)=width(q)+width(r);
+if (stretch(q)==0) stretch_order(q)=normal;
+if (stretch_order(q)==stretch_order(r)) stretch(q)=stretch(q)+stretch(r);
+else if ((stretch_order(q) < stretch_order(r))&&(stretch(r)!=0))
+ {@+stretch(q)=stretch(r);stretch_order(q)=stretch_order(r);
+ }
+if (shrink(q)==0) shrink_order(q)=normal;
+if (shrink_order(q)==shrink_order(r)) shrink(q)=shrink(q)+shrink(r);
+else if ((shrink_order(q) < shrink_order(r))&&(shrink(r)!=0))
+ {@+shrink(q)=shrink(r);shrink_order(q)=shrink_order(r);
+ }
+cur_val=q;
+}
+
+@ @<Compute result of |multiply| or |divide|...@>=
+{@+scan_int();
+if (p < glue_val)
+ if (q==multiply)
+ if (p==int_val) cur_val=mult_integers(w, cur_val);
+ else cur_val=nx_plus_y(w, cur_val, 0);
+ else cur_val=x_over_n(w, cur_val);
+else{@+r=new_spec(s);
+ if (q==multiply)
+ {@+width(r)=nx_plus_y(width(s), cur_val, 0);
+ stretch(r)=nx_plus_y(stretch(s), cur_val, 0);
+ shrink(r)=nx_plus_y(shrink(s), cur_val, 0);
+ }
+ else{@+width(r)=x_over_n(width(s), cur_val);
+ stretch(r)=x_over_n(stretch(s), cur_val);
+ shrink(r)=x_over_n(shrink(s), cur_val);
+ }
+ cur_val=r;
+ }
+}
+
+@ The processing of boxes is somewhat different, because we may need
+to scan and create an entire box before we actually change the value of the old
+one.
+
+@<Assignments@>=
+case set_box: {@+scan_register_num();
+ if (global) n=global_box_flag+cur_val;@+else n=box_flag+cur_val;
+ scan_optional_equals();
+ if (set_box_allowed) scan_box(n);
+ else{@+print_err("Improper ");print_esc("setbox");
+@.Improper \\setbox@>
+ help2("Sorry, \\setbox is not allowed after \\halign in a display,",@/
+ "or between \\accent and an accented character.");error();
+ }
+ } @+break;
+
+@ The |space_factor| or |prev_depth| settings are changed when a |set_aux|
+command is sensed. Similarly, |prev_graf| is changed in the presence of
+|set_prev_graf|, and |dead_cycles| or |insert_penalties| in the presence of
+|set_page_int|. These definitions are always global.
+
+When some dimension of a box register is changed, the change isn't exactly
+global; but \TeX\ does not look at the \.{\\global} switch.
+
+@<Assignments@>=
+case set_aux: alter_aux();@+break;
+case set_prev_graf: alter_prev_graf();@+break;
+case set_page_dimen: alter_page_so_far();@+break;
+case set_page_int: alter_integer();@+break;
+case set_box_dimen: alter_box_dimen();@+break;
+
+@ @<Declare subprocedures for |prefixed_command|@>=
+static void alter_aux(void)
+{@+halfword c; /*|hmode| or |vmode|*/
+if (cur_chr!=abs(mode)) report_illegal_case();
+else{@+c=cur_chr;scan_optional_equals();
+ if (c==vmode)
+ {@+scan_normal_dimen;prev_depth=cur_val;
+ }
+ else{@+scan_int();
+ if ((cur_val <= 0)||(cur_val > 32767))
+ {@+print_err("Bad space factor");
+@.Bad space factor@>
+ help1("I allow only values in the range 1..32767 here.");
+ int_error(cur_val);
+ }
+ else space_factor=cur_val;
+ }
+ }
+}
+
+@ @<Declare subprocedures for |prefixed_command|@>=
+static void alter_prev_graf(void)
+{@+int p; /*index into |nest|*/
+nest[nest_ptr]=cur_list;p=nest_ptr;
+while (abs(nest[p].mode_field)!=vmode) decr(p);
+scan_optional_equals();scan_int();
+if (cur_val < 0)
+ {@+print_err("Bad ");print_esc("prevgraf");
+@.Bad \\prevgraf@>
+ help1("I allow only nonnegative values here.");
+ int_error(cur_val);
+ }
+else{@+nest[p].pg_field=cur_val;cur_list=nest[nest_ptr];
+ }
+}
+
+@ @<Declare subprocedures for |prefixed_command|@>=
+static void alter_page_so_far(void)
+{@+int c; /*index into |page_so_far|*/
+c=cur_chr;scan_optional_equals();scan_normal_dimen;
+page_so_far[c]=cur_val;
+}
+
+@ @<Declare subprocedures for |prefixed_command|@>=
+static void alter_integer(void)
+{@+small_number c;
+ /*0 for \.{\\deadcycles}, 1 for \.{\\insertpenalties}, etc.*/
+c=cur_chr;scan_optional_equals();scan_int();
+if (c==0) dead_cycles=cur_val
+@/@<Cases for |alter_integer|@>@;@/
+else insert_penalties=cur_val;
+}
+
+@ @<Declare subprocedures for |prefixed_command|@>=
+static void alter_box_dimen(void)
+{@+small_number c; /*|width_offset| or |height_offset| or |depth_offset|*/
+pointer @!b; /*box register*/
+c=cur_chr;scan_register_num();fetch_box(b);scan_optional_equals();
+scan_normal_dimen;
+if (b!=null) mem[b+c].sc=cur_val;
+}
+
+@ Paragraph shapes are set up in the obvious way.
+
+@<Assignments@>=
+case set_shape: {@+q=cur_chr;scan_optional_equals();scan_int();n=cur_val;
+ if (n <= 0) p=null;
+ else if (q > par_shape_loc)
+ {@+n=(cur_val/2)+1;p=get_node(2*n+1);info(p)=n;
+ n=cur_val;mem[p+1].i=n; /*number of penalties*/
+ for (j=p+2; j<=p+n+1; j++)
+ {@+scan_int();mem[j].i=cur_val; /*penalty values*/
+ }
+ if (!odd(n)) mem[p+n+2].i=0; /*unused*/
+ }
+ else{@+p=get_node(2*n+1);info(p)=n;
+ for (j=1; j<=n; j++)
+ {@+scan_normal_dimen;
+ mem[p+2*j-1].sc=cur_val; /*indentation*/
+ scan_normal_dimen;
+ mem[p+2*j].sc=cur_val; /*width*/
+ }
+ }
+ define(q, shape_ref, p);
+ } @+break;
+
+@ Here's something that isn't quite so obvious. It guarantees that
+|info(par_shape_ptr)| can hold any positive~|n| for which |get_node(2*n+1)|
+doesn't overflow the memory capacity.
+
+@<Check the ``constant''...@>=
+if (2*max_halfword < mem_top-mem_min) bad=41;
+
+@ New hyphenation data is loaded by the |hyph_data| command.
+
+@<Put each...@>=
+primitive("hyphenation", hyph_data, 0);
+@!@:hyphenation\_}{\.{\\hyphenation} primitive@>
+primitive("patterns", hyph_data, 1);
+@!@:patterns\_}{\.{\\patterns} primitive@>
+
+@ @<Cases of |print_cmd_chr|...@>=
+case hyph_data: if (chr_code==1) print_esc("patterns");
+ else print_esc("hyphenation");@+break;
+
+@ @<Assignments@>=
+case hyph_data: if (cur_chr==1)
+ {
+#ifdef @!INIT
+new_patterns();goto done;@;
+#endif
+ print_err("Patterns can be loaded only by INITEX");
+@.Patterns can be...@>
+ help0;error();
+ @/do@+{get_token();}@+ while (!(cur_cmd==right_brace)); /*flush the patterns*/
+ return;
+ }
+ else{@+new_hyph_exceptions();goto done;
+ } @+break;
+
+@ All of \TeX's parameters are kept in |eqtb| except the font information,
+the interaction mode, and the hyphenation tables; these are strictly global.
+
+@<Assignments@>=
+case assign_font_dimen: {@+find_font_dimen(true);k=cur_val;
+ scan_optional_equals();scan_normal_dimen;font_info[k].sc=cur_val;
+ } @+break;
+case assign_font_int: {@+n=cur_chr;scan_font_ident();f=cur_val;
+ scan_optional_equals();scan_int();
+ if (n==0) hyphen_char[f]=cur_val;@+else skew_char[f]=cur_val;
+ } @+break;
+
+@ @<Put each...@>=
+primitive("hyphenchar", assign_font_int, 0);
+@!@:hyphen\_char\_}{\.{\\hyphenchar} primitive@>
+primitive("skewchar", assign_font_int, 1);
+@!@:skew\_char\_}{\.{\\skewchar} primitive@>
+
+@ @<Cases of |print_cmd_chr|...@>=
+case assign_font_int: if (chr_code==0) print_esc("hyphenchar");
+ else print_esc("skewchar");@+break;
+
+@ Here is where the information for a new font gets loaded.
+
+@<Assignments@>=
+case def_font: new_font(a);@+break;
+
+@ @<Declare subprocedures for |prefixed_command|@>=
+static void new_font(small_number @!a)
+{@+
+pointer u; /*user's font identifier*/
+scaled @!s; /*stated ``at'' size, or negative of scaled magnification*/
+int @!f; /*runs through existing fonts*/
+str_number @!t; /*name for the frozen font identifier*/
+int @!old_setting; /*holds |selector| setting*/
+str_number @!flushable_string; /*string not yet referenced*/
+if (job_name==0) open_log_file();
+ /*avoid confusing \.{texput} with the font name*/
+@.texput@>
+get_r_token();u=cur_cs;
+if (u >= hash_base) t=text(u);
+else if (u >= single_base)
+ if (u==null_cs) t=s_no("FONT");@+else t=u-single_base;
+else{@+old_setting=selector;selector=new_string;
+ print("FONT");printn(u-active_base);selector=old_setting;
+@.FONTx@>
+ str_room(1);t=make_string();
+ }
+define(u, set_font, null_font);scan_optional_equals();scan_file_name();
+@<Scan the font size specification@>;
+@<If this font has already been loaded, set |f| to the internal font number
+and |goto common_ending|@>;
+f=read_font_info(u, cur_name, cur_area, s);
+common_ending: define(u, set_font, f);eqtb[font_id_base+f]=eqtb[u];font_id_text(f)=t;
+}
+
+@ @<Scan the font size specification@>=
+name_in_progress=true; /*this keeps |cur_name| from being changed*/
+if (scan_keyword("at")) @<Put the \(p)(positive) `at' size into |s|@>@;
+@.at@>
+else if (scan_keyword("scaled"))
+@.scaled@>
+ {@+scan_int();s=-cur_val;
+ if ((cur_val <= 0)||(cur_val > 32768))
+ {@+print_err("Illegal magnification has been changed to 1000");@/
+@.Illegal magnification...@>
+ help1("The magnification ratio must be between 1 and 32768.");
+ int_error(cur_val);s=-1000;
+ }
+ }
+else s=-1000;
+name_in_progress=false
+
+@ @<Put the \(p)(positive) `at' size into |s|@>=
+{@+scan_normal_dimen;s=cur_val;
+if ((s <= 0)||(s >= 01000000000))
+ {@+print_err("Improper `at' size (");
+ print_scaled(s);print("pt), replaced by 10pt");
+@.Improper `at' size...@>
+ help2("I can only handle fonts at positive sizes that are",@/
+ "less than 2048pt, so I've changed what you said to 10pt.");
+ error();s=10*unity;
+ }
+}
+
+@ When the user gives a new identifier to a font that was previously loaded,
+the new name becomes the font identifier of record. Font names `\.{xyz}' and
+`\.{XYZ}' are considered to be different.
+
+@<If this font has already been loaded...@>=
+flushable_string=str_ptr-1;
+for (f=font_base+1; f<=font_ptr; f++)
+ if (str_eq_str(font_name[f], cur_name)&&str_eq_str(font_area[f], cur_area))
+ {@+if (cur_name==flushable_string)
+ {@+flush_string;cur_name=font_name[f];
+ }
+ if (s > 0)
+ {@+if (s==font_size[f]) goto common_ending;
+ }
+ else if (font_size[f]==xn_over_d(font_dsize[f],-s, 1000))
+ goto common_ending;
+ }
+
+@ @<Cases of |print_cmd_chr|...@>=
+case set_font: {@+print("select font ");slow_print(font_name[chr_code]);
+ if (font_size[chr_code]!=font_dsize[chr_code])
+ {@+print(" at ");print_scaled(font_size[chr_code]);
+ print("pt");
+ }
+ } @+break;
+
+@ @<Put each...@>=
+primitive("batchmode", set_interaction, batch_mode);
+@!@:batch\_mode\_}{\.{\\batchmode} primitive@>
+primitive("nonstopmode", set_interaction, nonstop_mode);
+@!@:nonstop\_mode\_}{\.{\\nonstopmode} primitive@>
+primitive("scrollmode", set_interaction, scroll_mode);
+@!@:scroll\_mode\_}{\.{\\scrollmode} primitive@>
+primitive("errorstopmode", set_interaction, error_stop_mode);
+@!@:error\_stop\_mode\_}{\.{\\errorstopmode} primitive@>
+
+@ @<Cases of |print_cmd_chr|...@>=
+case set_interaction: switch (chr_code) {
+ case batch_mode: print_esc("batchmode");@+break;
+ case nonstop_mode: print_esc("nonstopmode");@+break;
+ case scroll_mode: print_esc("scrollmode");@+break;
+ default:print_esc("errorstopmode");
+ } @+break;
+
+@ @<Assignments@>=
+case set_interaction: new_interaction();@+break;
+
+@ @<Declare subprocedures for |prefixed_command|@>=
+static void new_interaction(void)
+{@+print_ln();
+interaction=cur_chr;
+@<Initialize the print |selector| based on |interaction|@>;
+if (log_opened) selector=selector+2;
+}
+
+@ The \.{\\afterassignment} command puts a token into the global
+variable |after_token|. This global variable is examined just after
+every assignment has been performed.
+
+@<Glob...@>=
+static halfword @!after_token; /*zero, or a saved token*/
+
+@ @<Set init...@>=
+after_token=0;
+
+@ @<Cases of |main_control| that don't...@>=
+any_mode(after_assignment): {@+get_token();after_token=cur_tok;
+ } @+break;
+
+@ @<Insert a token saved by \.{\\afterassignment}, if any@>=
+if (after_token!=0)
+ {@+cur_tok=after_token;back_input();after_token=0;
+ }
+
+@ Here is a procedure that might be called `Get the next non-blank non-relax
+non-call non-assignment token'.
+
+@<Declare act...@>=
+static void do_assignments(void)
+{@+
+loop{@+@<Get the next non-blank non-relax...@>;
+ if (cur_cmd <= max_non_prefixed_command) return;
+ set_box_allowed=false;prefixed_command();set_box_allowed=true;
+ }
+}
+
+@ @<Cases of |main_control| that don't...@>=
+any_mode(after_group): {@+get_token();save_for_after(cur_tok);
+ } @+break;
+
+@ Files for \.{\\read} are opened and closed by the |in_stream| command.
+
+@<Put each...@>=
+primitive("openin", in_stream, 1);
+@!@:open\_in\_}{\.{\\openin} primitive@>
+primitive("closein", in_stream, 0);
+@!@:close\_in\_}{\.{\\closein} primitive@>
+
+@ @<Cases of |print_cmd_chr|...@>=
+case in_stream: if (chr_code==0) print_esc("closein");
+ else print_esc("openin");@+break;
+
+@ @<Cases of |main_control| that don't...@>=
+any_mode(in_stream): open_or_close_in();@+break;
+
+@ @<Declare act...@>=
+static void open_or_close_in(void)
+{@+int c; /*1 for \.{\\openin}, 0 for \.{\\closein}*/
+int @!n; /*stream number*/
+c=cur_chr;scan_four_bit_int();n=cur_val;
+if (read_open[n]!=closed)
+ {@+a_close(&read_file[n]);read_open[n]=closed;
+ }
+if (c!=0)
+ {@+scan_optional_equals();scan_file_name();
+ pack_cur_name(".tex");
+ if (a_open_in(&read_file[n]))
+ { read_open[n]=just_open;
+ @<Set new |read_file_num[n]|@>@;
+ }
+ }
+}
+
+@ The user can issue messages to the terminal, regardless of the
+current mode.
+
+@<Cases of |main_control| that don't...@>=
+any_mode(message): issue_message();@+break;
+
+@ @<Put each...@>=
+primitive("message", message, 0);
+@!@:message\_}{\.{\\message} primitive@>
+primitive("errmessage", message, 1);
+@!@:err\_message\_}{\.{\\errmessage} primitive@>
+
+@ @<Cases of |print_cmd_chr|...@>=
+case message: if (chr_code==0) print_esc("message");
+ else print_esc("errmessage");@+break;
+
+@ @<Declare act...@>=
+static void issue_message(void)
+{@+int old_setting; /*holds |selector| setting*/
+int @!c; /*identifies \.{\\message} and \.{\\errmessage}*/
+str_number @!s; /*the message*/
+c=cur_chr;link(garbage)=scan_toks(false, true);
+old_setting=selector;selector=new_string;
+token_show(def_ref);selector=old_setting;
+flush_list(def_ref);
+str_room(1);s=make_string();
+if (c==0) @<Print string |s| on the terminal@>@;
+else@<Print string |s| as an error message@>;
+flush_string;
+}
+
+@ @<Print string |s| on the terminal@>=
+{@+if (term_offset+length(s) > max_print_line-2) print_ln();
+else if ((term_offset > 0)||(file_offset > 0)) print_char(' ');
+slow_print(s);update_terminal;
+}
+
+@ If \.{\\errmessage} occurs often in |scroll_mode|, without user-defined
+\.{\\errhelp}, we don't want to give a long help message each time. So we
+give a verbose explanation only once.
+
+@<Glob...@>=
+static bool @!long_help_seen; /*has the long \.{\\errmessage} help been used?*/
+
+@ @<Set init...@>=long_help_seen=false;
+
+@ @<Print string |s| as an error message@>=
+{@+print_err("");slow_print(s);
+if (err_help!=null) use_err_help=true;
+else if (long_help_seen) help1("(That was another \\errmessage.)")@;
+else{@+if (interaction < error_stop_mode) long_help_seen=true;
+ help4("This error message was generated by an \\errmessage",@/
+ "command, so I can't give any explicit help.",@/
+ "Pretend that you're Hercule Poirot: Examine all clues,",@/
+@^Poirot, Hercule@>
+ "and deduce the truth by order and method.");
+ }
+error();use_err_help=false;
+}
+
+@ The |error| routine calls on |give_err_help| if help is requested from
+the |err_help| parameter.
+
+@p static void give_err_help(void)
+{@+token_show(err_help);
+}
+
+@ The \.{\\uppercase} and \.{\\lowercase} commands are implemented by
+building a token list and then changing the cases of the letters in it.
+
+@<Cases of |main_control| that don't...@>=
+any_mode(case_shift): shift_case();@+break;
+
+@ @<Put each...@>=
+primitive("lowercase", case_shift, lc_code_base);
+@!@:lowercase\_}{\.{\\lowercase} primitive@>
+primitive("uppercase", case_shift, uc_code_base);
+@!@:uppercase\_}{\.{\\uppercase} primitive@>
+
+@ @<Cases of |print_cmd_chr|...@>=
+case case_shift: if (chr_code==lc_code_base) print_esc("lowercase");
+ else print_esc("uppercase");@+break;
+
+@ @<Declare act...@>=
+static void shift_case(void)
+{@+pointer b; /*|lc_code_base| or |uc_code_base|*/
+pointer @!p; /*runs through the token list*/
+halfword @!t; /*token*/
+eight_bits @!c; /*character code*/
+b=cur_chr;p=scan_toks(false, false);p=link(def_ref);
+while (p!=null)
+ {@+@<Change the case of the token in |p|, if a change is appropriate@>;
+ p=link(p);
+ }
+back_list(link(def_ref));free_avail(def_ref); /*omit reference count*/
+}
+
+@ When the case of a |chr_code| changes, we don't change the |cmd|.
+We also change active characters, using the fact that
+|cs_token_flag+active_base| is a multiple of~256.
+@^data structure assumptions@>
+
+@<Change the case of the token in |p|, if a change is appropriate@>=
+t=info(p);
+if (t < cs_token_flag+single_base)
+ {@+c=t%256;
+ if (equiv(b+c)!=0) info(p)=t-c+equiv(b+c);
+ }
+
+@ We come finally to the last pieces missing from |main_control|, namely the
+`\.{\\show}' commands that are useful when debugging.
+
+@<Cases of |main_control| that don't...@>=
+any_mode(xray): show_whatever();@+break;
+
+@ @d show_code 0 /* \.{\\show} */
+@d show_box_code 1 /* \.{\\showbox} */
+@d show_the_code 2 /* \.{\\showthe} */
+@d show_lists_code 3 /* \.{\\showlists} */
+
+@<Put each...@>=
+primitive("show", xray, show_code);
+@!@:show\_}{\.{\\show} primitive@>
+primitive("showbox", xray, show_box_code);
+@!@:show\_box\_}{\.{\\showbox} primitive@>
+primitive("showthe", xray, show_the_code);
+@!@:show\_the\_}{\.{\\showthe} primitive@>
+primitive("showlists", xray, show_lists_code);
+@!@:show\_lists\_code\_}{\.{\\showlists} primitive@>
+
+@ @<Cases of |print_cmd_chr|...@>=
+case xray: switch (chr_code) {
+ case show_box_code: print_esc("showbox");@+break;
+ case show_the_code: print_esc("showthe");@+break;
+ case show_lists_code: print_esc("showlists");@+break;
+ @<Cases of |xray| for |print_cmd_chr|@>@;@/
+ default:print_esc("show");
+ } @+break;
+
+@ @<Declare act...@>=
+static void show_whatever(void)
+{@+
+pointer p; /*tail of a token list to show*/
+small_number @!t; /*type of conditional being shown*/
+int @!m; /*upper bound on |fi_or_else| codes*/
+int @!l; /*line where that conditional began*/
+int @!n; /*level of \.{\\if...\\fi} nesting*/
+switch (cur_chr) {
+case show_lists_code: {@+begin_diagnostic();show_activities();
+ } @+break;
+case show_box_code: @<Show the current contents of a box@>@;@+break;
+case show_code: @<Show the current meaning of a token, then |goto common_ending|@>@;
+@<Cases for |show_whatever|@>@;@/
+default:@<Show the current value of some parameter or register, then |goto
+common_ending|@>@;
+} @/
+@<Complete a potentially long \.{\\show} command@>;
+common_ending: if (interaction < error_stop_mode)
+ {@+help0;decr(error_count);
+ }
+else if (tracing_online > 0)
+ {@+@t@>@;@/
+ help3("This isn't an error message; I'm just \\showing something.",@/
+ "Type `I\\show...' to show more (e.g., \\show\\cs,",@/
+ "\\showthe\\count10, \\showbox255, \\showlists).");
+ }
+else{@+@t@>@;@/
+ help5("This isn't an error message; I'm just \\showing something.",@/
+ "Type `I\\show...' to show more (e.g., \\show\\cs,",@/
+ "\\showthe\\count10, \\showbox255, \\showlists).",@/
+ "And type `I\\tracingonline=1\\show...' to show boxes and",@/
+ "lists on your terminal as well as in the transcript file.");
+ }
+error();
+}
+
+@ @<Show the current meaning of a token...@>=
+{@+get_token();
+if (interaction==error_stop_mode) wake_up_terminal;
+print_nl("> ");
+if (cur_cs!=0)
+ {@+sprint_cs(cur_cs);print_char('=');
+ }
+print_meaning();goto common_ending;
+}
+
+@ @<Cases of |print_cmd_chr|...@>=
+case undefined_cs: print("undefined");@+break;
+case call: case long_call: case outer_call:
+ case long_outer_call: {@+n=cmd-call;
+ if (info(link(chr_code))==protected_token) n=n+4;
+ if (odd(n/4)) print_esc("protected");
+ if (odd(n)) print_esc("long");
+ if (odd(n/2)) print_esc("outer");
+ if (n > 0) print_char(' ');
+ print("macro");
+ } @+break;
+case end_template: print_esc("outer endtemplate");@+break;
+
+@ @<Show the current contents of a box@>=
+{@+scan_register_num();fetch_box(p);begin_diagnostic();
+print_nl("> \\box");print_int(cur_val);print_char('=');
+if (p==null) print("void");@+else show_box(p);
+}
+
+@ @<Show the current value of some parameter...@>=
+{@+the_toks();
+if (interaction==error_stop_mode) wake_up_terminal;
+print_nl("> ");token_show(temp_head);
+flush_list(link(temp_head));goto common_ending;
+}
+
+@ @<Complete a potentially long \.{\\show} command@>=
+end_diagnostic(true);print_err("OK");
+@.OK@>
+if (selector==term_and_log) if (tracing_online <= 0)
+ {@+selector=term_only;print(" (see the transcript file)");
+ selector=term_and_log;
+ }
+
+@* Dumping and undumping the tables.
+After \.{INITEX} has seen a collection of fonts and macros, it
+can write all the necessary information on an auxiliary file so
+that production versions of \TeX\ are able to initialize their
+memory at high speed. The present section of the program takes
+care of such output and input. We shall consider simultaneously
+the processes of storing and restoring,
+so that the inverse relation between them is clear.
+@.INITEX@>
+
+The global variable |format_ident| is a string that is printed right
+after the |banner| line when \TeX\ is ready to start. For \.{INITEX} this
+string says simply `\.{ (INITEX)}'; for other versions of \TeX\ it says,
+for example, `\.{ (preloaded format=plain 1982.11.19)}', showing the year,
+month, and day that the format file was created. We have |format_ident==0|
+before \TeX's tables are loaded.
+
+@<Glob...@>=
+static str_number @!format_ident, frozen_format_ident;
+
+@ @<Set init...@>=
+format_ident=frozen_format_ident=0;
+
+@ We keep a copy of the initial value, be able to test for it later.
+
+@<Initialize table entries...@>=
+format_ident=frozen_format_ident=s_no(" (INITEX)");
+
+@ @<Declare act...@>=
+#ifdef @!INIT
+static void store_fmt_file(void)
+{@+
+int j, @!k, @!l; /*all-purpose indices*/
+int @!p, @!q; /*all-purpose pointers*/
+int @!x; /*something to dump*/
+four_quarters @!w; /*four ASCII codes*/
+@<If dumping is not allowed, abort@>;
+@<Create the |format_ident|, open the format file, and inform the user that
+dumping has begun@>;
+@<Dump constants for consistency check@>;
+@<Dump the string pool@>;
+@<Dump the dynamic memory@>;
+@<Dump the table of equivalents@>;
+@<Dump the font information@>;
+@<Dump the hyphenation tables@>;
+@<Dump the full file names@>;
+@<Dump the file and line information@>;
+@<Dump a couple more things and the closing check word@>;
+@<Close the format file@>;
+}
+#endif
+
+@ Corresponding to the procedure that dumps a format file, we have a function
+that reads one in. The function returns |false| if the dumped format is
+incompatible with the present \TeX\ table sizes, etc.
+
+@d too_small(X) {@+wake_up_terminal;
+ wterm_ln("---! Must increase the %s", X);
+@.Must increase the x@>
+ goto bad_fmt;
+ }
+
+@p @t\4@>@<Declare the function called |open_fmt_file|@>@;
+static bool load_fmt_file(void)
+{@+
+int j, @!k; /*all-purpose indices*/
+int @!p, @!q; /*all-purpose pointers*/
+int @!x; /*something undumped*/
+four_quarters @!w; /*four ASCII codes*/
+@<Undump constants for consistency check@>;
+@<Undump the string pool@>;
+@<Undump the dynamic memory@>;
+@<Undump the table of equivalents@>;
+@<Undump the font information@>;
+@<Undump the hyphenation tables@>;
+@<Undump the full file names@>;
+@<Undump the file and line information@>;
+@<Undump a couple more things and the closing check word@>;
+return true; /*it worked!*/
+bad_fmt: wake_up_terminal;
+ wterm_ln("(Fatal format file error; I'm stymied)");
+@.Fatal format file error@>
+return false;
+}
+
+@ The user is not allowed to dump a format file unless |save_ptr==0|.
+This condition implies that |cur_level==level_one|, hence
+the |xeq_level| array is constant and it need not be dumped.
+
+@<If dumping is not allowed, abort@>=
+if (save_ptr!=0)
+ {@+print_err("You can't dump inside a group");
+@.You can't dump...@>
+ help1("`{...\\dump}' is a no-no.");succumb;
+ }
+
+@ Format files consist of |memory_word| items, and we use the following
+macros to dump words of different types:
+
+@d dump_wd(A) {@+fmt_file.d=A;put(fmt_file);@+}
+@d dump_int(A) {@+fmt_file.d.i=A;put(fmt_file);@+}
+@d dump_hh(A) {@+fmt_file.d.hh=A;put(fmt_file);@+}
+@d dump_qqqq(A) {@+fmt_file.d.qqqq=A;put(fmt_file);@+}
+
+@<Glob...@>=
+static word_file @!fmt_file; /*for input or output of format information*/
+
+@ The inverse macros are slightly more complicated, since we need to check
+the range of the values we are reading in. We say `|undump(a)(b)(x)|' to
+read an integer value |x| that is supposed to be in the range |a <= x <= b|.
+System error messages should be suppressed when undumping.
+@^system dependencies@>
+
+@d undump_wd(A) {@+get(fmt_file);A=fmt_file.d;@+}
+@d undump_int(A) {@+get(fmt_file);A=fmt_file.d.i;@+}
+@d undump_hh(A) {@+get(fmt_file);A=fmt_file.d.hh;@+}
+@d undump_qqqq(A) {@+get(fmt_file);A=fmt_file.d.qqqq;@+}
+@d undump(A, B, C) {@+undump_int(x);if ((x < A)||(x > B)) goto bad_fmt;@+else C=x;@+}
+@d undump_size(A, B, C, D) {@+undump_int(x);
+ if (x < A) goto bad_fmt;if (x > B) too_small(C)@;@+else D=x;@+}
+
+@ The next few sections of the program should make it clear how we use the
+dump/undump macros.
+
+@<Dump constants for consistency check@>=
+dump_int(0);@/
+@<Dump the \eTeX\ state@>@/
+@<Dump the \Prote\ state@>@/
+@<Dump the |ROM| array@>@/
+dump_int(mem_bot);@/
+dump_int(mem_top);@/
+dump_int(eqtb_size);@/
+dump_int(hash_prime);@/
+dump_int(hyph_size)
+
+@ Sections of a \.{WEB} program that are ``commented out'' still contribute
+strings to the string pool; therefore \.{INITEX} and \TeX\ will have
+the same strings. (And it is, of course, a good thing that they do.)
+@.WEB@>
+@^string pool@>
+
+@<Undump constants for consistency check@>=
+x=fmt_file.d.i;
+if (x!=0) goto bad_fmt; /*check that strings are the same*/
+@/@<Undump the \eTeX\ state@>@/
+@/@<Undump the \Prote\ state@>@/
+@/@<Undump the |ROM| array@>@/
+undump_int(x);
+if (x!=mem_bot) goto bad_fmt;
+undump_int(x);
+if (x!=mem_top) goto bad_fmt;
+undump_int(x);
+if (x!=eqtb_size) goto bad_fmt;
+undump_int(x);
+if (x!=hash_prime) goto bad_fmt;
+undump_int(x);
+if (x!=hyph_size) goto bad_fmt
+
+@ @d dump_four_ASCII
+ w.b0=qi(so(str_pool[k]));w.b1=qi(so(str_pool[k+1]));
+ w.b2=qi(so(str_pool[k+2]));w.b3=qi(so(str_pool[k+3]));
+ dump_qqqq(w)
+
+@<Dump the string pool@>=
+dump_int(pool_ptr);
+dump_int(str_ptr);
+for (k=0; k<=str_ptr; k++) dump_int(str_start[k]);
+k=0;
+while (k+4 < pool_ptr)
+ {@+dump_four_ASCII;k=k+4;
+ }
+k=pool_ptr-4;dump_four_ASCII;
+print_ln();print_int(str_ptr);print(" strings of total length ");
+print_int(pool_ptr)
+
+@ @d undump_four_ASCII
+ undump_qqqq(w);
+ str_pool[k]=si(qo(w.b0));str_pool[k+1]=si(qo(w.b1));
+ str_pool[k+2]=si(qo(w.b2));str_pool[k+3]=si(qo(w.b3))
+
+@<Undump the string pool@>=
+undump_size(0, pool_size,"string pool size", pool_ptr);
+undump_size(0, max_strings,"max strings", str_ptr);
+for (k=0; k<=str_ptr; k++) undump(0, pool_ptr, str_start[k]);
+k=0;
+while (k+4 < pool_ptr)
+ {@+undump_four_ASCII;k=k+4;
+ }
+k=pool_ptr-4;undump_four_ASCII;
+init_str_ptr=str_ptr;init_pool_ptr=pool_ptr
+
+@ By sorting the list of available spaces in the variable-size portion of
+|mem|, we are usually able to get by without having to dump very much
+of the dynamic memory.
+
+We recompute |var_used| and |dyn_used|, so that \.{INITEX} dumps valid
+information even when it has not been gathering statistics.
+
+@<Dump the dynamic memory@>=
+sort_avail();var_used=0;
+dump_int(lo_mem_max);dump_int(rover);
+if (eTeX_ex) for (k=int_val; k<=tok_val; k++) dump_int(sa_root[k]);
+p=mem_bot;q=rover;x=0;
+@/do@+{for (k=p; k<=q+1; k++) dump_wd(mem[k]);
+x=x+q+2-p;var_used=var_used+q-p;
+p=q+node_size(q);q=rlink(q);
+}@+ while (!(q==rover));
+var_used=var_used+lo_mem_max-p;dyn_used=mem_end+1-hi_mem_min;@/
+for (k=p; k<=lo_mem_max; k++) dump_wd(mem[k]);
+x=x+lo_mem_max+1-p;
+dump_int(hi_mem_min);dump_int(avail);
+for (k=hi_mem_min; k<=mem_end; k++) dump_wd(mem[k]);
+p=avail;
+while (p!=null)
+{@+fl_mem[p]=0; /*clear unallocated |fl_mem|*/
+ decr(dyn_used);p=link(p);
+}
+dump_int(var_used);dump_int(dyn_used);
+print_ln();print_int(x);print_char('&');print_int(mem_end+1-hi_mem_min);
+print(" memory locations dumped; current usage is ");
+print_int(var_used);print_char('&');print_int(dyn_used)
+
+@ @<Undump the dynamic memory@>=
+undump(lo_mem_stat_max+1000, hi_mem_stat_min-1, lo_mem_max);
+undump(lo_mem_stat_max+1, lo_mem_max, rover);
+if (eTeX_ex) for (k=int_val; k<=tok_val; k++)
+ undump(null, lo_mem_max, sa_root[k]);
+p=mem_bot;q=rover;
+@/do@+{for (k=p; k<=q+1; k++) undump_wd(mem[k]);
+p=q+node_size(q);
+if ((p > lo_mem_max)||((q >= rlink(q))&&(rlink(q)!=rover))) goto bad_fmt;
+q=rlink(q);
+}@+ while (!(q==rover));
+for (k=p; k<=lo_mem_max; k++) undump_wd(mem[k]);
+if (mem_min < mem_bot-2) /*make more low memory available*/
+ {@+p=llink(rover);q=mem_min+1;
+ link(mem_min)=null;info(mem_min)=null; /*we don't use the bottom word*/
+ rlink(p)=q;llink(rover)=q;@/
+ rlink(q)=rover;llink(q)=p;link(q)=empty_flag;
+ node_size(q)=mem_bot-q;
+ }
+undump(lo_mem_max+1, hi_mem_stat_min, hi_mem_min);
+undump(null, mem_top, avail);mem_end=mem_top;
+for (k=hi_mem_min; k<=mem_end; k++) undump_wd(mem[k]);
+undump_int(var_used);undump_int(dyn_used)
+
+@ @<Dump the table of equivalents@>=
+@<Dump regions 1 to 4 of |eqtb|@>;
+@<Dump regions 5 and 6 of |eqtb|@>;
+dump_int(par_loc);dump_int(write_loc);@/
+dump_int(input_loc);@/
+@<Dump the hash table@>@;
+
+@ @<Undump the table of equivalents@>=
+@<Undump regions 1 to 6 of |eqtb|@>;
+undump(hash_base, frozen_control_sequence, par_loc);
+par_token=cs_token_flag+par_loc;@/
+undump(hash_base, frozen_control_sequence, write_loc);@/
+undump(hash_base, frozen_control_sequence, input_loc);
+input_token=cs_token_flag+input_loc;@/
+@<Undump the hash table@>@;
+
+@ The table of equivalents usually contains repeated information, so we dump it
+in compressed form: The sequence of $n+2$ values $(n,x_1,\ldots,x_n,m)$ in the
+format file represents $n+m$ consecutive entries of |eqtb|, with |m| extra
+copies of $x_n$, namely $(x_1,\ldots,x_n,x_n,\ldots,x_n)$.
+
+@<Dump regions 1 to 4 of |eqtb|@>=
+k=active_base;
+@/do@+{j=k;
+while (j < int_base-1)
+ {@+if ((equiv(j)==equiv(j+1))&&(eq_type(j)==eq_type(j+1))&&@|
+ (eq_level(j)==eq_level(j+1))) goto found1;
+ incr(j);
+ }
+l=int_base;goto done1; /*|j==int_base-1|*/
+found1: incr(j);l=j;
+while (j < int_base-1)
+ {@+if ((equiv(j)!=equiv(j+1))||(eq_type(j)!=eq_type(j+1))||@|
+ (eq_level(j)!=eq_level(j+1))) goto done1;
+ incr(j);
+ }
+done1: dump_int(l-k);
+while (k < l)
+ {@+dump_wd(eqtb[k]);incr(k);
+ }
+k=j+1;dump_int(k-l);
+}@+ while (!(k==int_base))
+
+@ @<Dump regions 5 and 6 of |eqtb|@>=
+@/do@+{j=k;
+while (j < eqtb_size)
+ {@+if (eqtb[j].i==eqtb[j+1].i) goto found2;
+ incr(j);
+ }
+l=eqtb_size+1;goto done2; /*|j==eqtb_size|*/
+found2: incr(j);l=j;
+while (j < eqtb_size)
+ {@+if (eqtb[j].i!=eqtb[j+1].i) goto done2;
+ incr(j);
+ }
+done2: dump_int(l-k);
+while (k < l)
+ {@+dump_wd(eqtb[k]);incr(k);
+ }
+k=j+1;dump_int(k-l);
+}@+ while (!(k > eqtb_size))
+
+@ @<Undump regions 1 to 6 of |eqtb|@>=
+k=active_base;
+@/do@+{undump_int(x);
+if ((x < 1)||(k+x > eqtb_size+1)) goto bad_fmt;
+for (j=k; j<=k+x-1; j++) undump_wd(eqtb[j]);
+k=k+x;
+undump_int(x);
+if ((x < 0)||(k+x > eqtb_size+1)) goto bad_fmt;
+for (j=k; j<=k+x-1; j++) eqtb[j]=eqtb[k-1];
+k=k+x;
+}@+ while (!(k > eqtb_size))
+
+@ A different scheme is used to compress the hash table, since its lower
+region is usually sparse. When |text(p)!=0| for |p <= hash_used|, we output
+two words, |p| and |hash[p]|. The hash table is, of course, densely packed
+for |p >= hash_used|, so the remaining entries are output in a~block.
+
+@<Dump the hash table@>=
+dump_int(hash_used);cs_count=frozen_control_sequence-1-hash_used;
+for (p=hash_base; p<=hash_used; p++) if (text(p)!=0)
+ {@+dump_int(p);dump_hh(hash[p]);incr(cs_count);
+ }
+for (p=hash_used+1; p<=undefined_control_sequence-1; p++) dump_hh(hash[p]);
+dump_int(cs_count);@/
+print_ln();print_int(cs_count);print(" multiletter control sequences")
+
+@ @<Undump the hash table@>=
+undump(hash_base, frozen_control_sequence, hash_used);p=hash_base-1;
+@/do@+{undump(p+1, hash_used, p);undump_hh(hash[p]);
+}@+ while (!(p==hash_used));
+for (p=hash_used+1; p<=undefined_control_sequence-1; p++) undump_hh(hash[p]);
+undump_int(cs_count)
+
+@ @<Dump the font information@>=
+dump_int(fmem_ptr);
+for (k=0; k<=fmem_ptr-1; k++) dump_wd(font_info[k]);
+dump_int(font_ptr);
+for (k=null_font; k<=font_ptr; k++)
+ @<Dump the array info for internal font number |k|@>;
+print_ln();print_int(fmem_ptr-7);print(" words of font info for ");
+print_int(font_ptr-font_base);print(" preloaded font");
+if (font_ptr!=font_base+1) print_char('s')
+
+@ @<Undump the font information@>=
+undump_size(7, font_mem_size,"font mem size", fmem_ptr);
+for (k=0; k<=fmem_ptr-1; k++) undump_wd(font_info[k]);
+undump_size(font_base, font_max,"font max", font_ptr);
+for (k=null_font; k<=font_ptr; k++)
+ @<Undump the array info for internal font number |k|@>@;
+
+@ @<Dump the array info for internal font number |k|@>=
+{@+dump_qqqq(font_check[k]);
+dump_int(font_size[k]);
+dump_int(font_dsize[k]);
+dump_int(font_params[k]);@/
+dump_int(hyphen_char[k]);
+dump_int(skew_char[k]);@/
+dump_int(font_name[k]);
+dump_int(font_area[k]);@/
+dump_int(font_bc[k]);
+dump_int(font_ec[k]);@/
+dump_int(char_base[k]);
+dump_int(width_base[k]);
+dump_int(height_base[k]);@/
+dump_int(depth_base[k]);
+dump_int(italic_base[k]);
+dump_int(lig_kern_base[k]);@/
+dump_int(kern_base[k]);
+dump_int(exten_base[k]);
+dump_int(param_base[k]);@/
+dump_int(font_glue[k]);@/
+dump_int(bchar_label[k]);
+dump_int(font_bchar[k]);
+dump_int(font_false_bchar[k]);@/
+print_nl("\\font");printn_esc(font_id_text(k));print_char('=');
+print_file_name(font_name[k], font_area[k],empty_string);
+if (font_size[k]!=font_dsize[k])
+ {@+print(" at ");print_scaled(font_size[k]);print("pt");
+ }
+}
+
+@ @<Undump the array info for internal font number |k|@>=
+{@+undump_qqqq(font_check[k]);@/
+undump_int(font_size[k]);
+undump_int(font_dsize[k]);
+undump(min_halfword, max_halfword, font_params[k]);@/
+undump_int(hyphen_char[k]);
+undump_int(skew_char[k]);@/
+undump(0, str_ptr, font_name[k]);
+undump(0, str_ptr, font_area[k]);@/
+undump(0, 255, font_bc[k]);
+undump(0, 255, font_ec[k]);@/
+undump_int(char_base[k]);
+undump_int(width_base[k]);
+undump_int(height_base[k]);@/
+undump_int(depth_base[k]);
+undump_int(italic_base[k]);
+undump_int(lig_kern_base[k]);@/
+undump_int(kern_base[k]);
+undump_int(exten_base[k]);
+undump_int(param_base[k]);@/
+undump(min_halfword, lo_mem_max, font_glue[k]);@/
+undump(0, fmem_ptr-1, bchar_label[k]);
+undump(min_quarterword, non_char, font_bchar[k]);
+undump(min_quarterword, non_char, font_false_bchar[k]);
+}
+
+@ @<Dump the hyphenation tables@>=
+dump_int(hyph_count);
+for (k=0; k<=hyph_size; k++) if (hyph_word[k]!=0)
+ {@+dump_int(k);dump_int(hyph_word[k]);dump_int(hyph_list[k]);
+ }
+print_ln();print_int(hyph_count);print(" hyphenation exception");
+if (hyph_count!=1) print_char('s');
+if (trie_not_ready) init_trie();
+dump_int(trie_max);
+dump_int(hyph_start);
+for (k=0; k<=trie_max; k++) dump_hh(trie[k]);
+dump_int(trie_op_ptr);
+for (k=1; k<=trie_op_ptr; k++)
+ {@+dump_int(hyf_distance[k]);
+ dump_int(hyf_num[k]);
+ dump_int(hyf_next[k]);
+ }
+print_nl("Hyphenation trie of length ");print_int(trie_max);
+@.Hyphenation trie...@>
+print(" has ");print_int(trie_op_ptr);print(" op");
+if (trie_op_ptr!=1) print_char('s');
+print(" out of ");print_int(trie_op_size);
+for (k=255; k>=0; k--) if (trie_used[k] > min_quarterword)
+ {@+print_nl(" ");print_int(qo(trie_used[k]));
+ print(" for language ");print_int(k);
+ dump_int(k);dump_int(qo(trie_used[k]));
+ }
+
+@ Only ``nonempty'' parts of |op_start| need to be restored.
+
+@<Undump the hyphenation tables@>=
+undump(0, hyph_size, hyph_count);
+for (k=1; k<=hyph_count; k++)
+ {@+undump(0, hyph_size, j);
+ undump(0, str_ptr, hyph_word[j]);
+ undump(min_halfword, max_halfword, hyph_list[j]);
+ }
+undump_size(0, trie_size,"trie size", j);
+#ifdef @!INIT
+trie_max=j;
+#endif
+undump(0, j, hyph_start);
+for (k=0; k<=j; k++) undump_hh(trie[k]);
+undump_size(0, trie_op_size,"trie op size", j);
+#ifdef @!INIT
+trie_op_ptr=j;
+#endif
+for (k=1; k<=j; k++)
+ {@+undump(0, 63, hyf_distance[k]); /*a |small_number|*/
+ undump(0, 63, hyf_num[k]);
+ undump(min_quarterword, max_quarterword, hyf_next[k]);
+ }
+#ifdef @!INIT
+for (k=0; k<=255; k++) trie_used[k]=min_quarterword;
+#endif
+@;@/
+k=256;
+while (j > 0)
+ {@+undump(0, k-1, k);undump(1, j, x);
+#ifdef @!INIT
+trie_used[k]=qi(x);
+#endif
+@;@/
+ j=j-x;op_start[k]=qo(j);
+ }
+#ifdef @!INIT
+trie_not_ready=false
+#endif
+
+@ We have already printed a lot of statistics, so we set |tracing_stats=0|
+to prevent them from appearing again.
+
+@<Dump a couple more things and the closing check word@>=
+dump_int(interaction);dump_int(format_ident);dump_int(69069);
+tracing_stats=0
+
+@ @<Undump a couple more things and the closing check word@>=
+undump(batch_mode, error_stop_mode, interaction);
+if (interaction_option>=0) interaction=interaction_option; /* \TeX\ Live */
+undump(0, str_ptr, format_ident);
+undump_int(x);
+if ((x!=69069)||eof(fmt_file)) goto bad_fmt
+
+@ @<Create the |format_ident|...@>=
+selector=new_string;
+print(" (preloaded format=");printn(job_name);print_char(' ');
+print_int(year);print_char('.');
+print_int(month);print_char('.');print_int(day);print_char(')');
+if (interaction==batch_mode) selector=log_only;
+else selector=term_and_log;
+str_room(1);
+format_ident=make_string();
+pack_job_name(format_extension);
+while (!w_open_out(&fmt_file))
+ prompt_file_name("format file name", format_extension);
+print_nl("Beginning to dump on file ");
+@.Beginning to dump...@>
+slow_print(w_make_name_string(&fmt_file));flush_string;
+print_nl("");slow_print(format_ident)
+
+@ @<Close the format file@>=
+w_close(&fmt_file)
+
+@* The main program.
+This is it: the part of \TeX\ that executes all those procedures we have
+written.
+
+Well---almost. Let's leave space for a few more routines that we may
+have forgotten.
+
+@p @<Last-minute procedures@>@;
+
+@ We have noted that there are two versions of \TeX82. One, called \.{INITEX},
+@.INITEX@>
+has to be run first; it initializes everything from scratch, without
+reading a format file, and it has the capability of dumping a format file.
+The other one is called `\.{VIRTEX}'; it is a ``virgin'' program that needs
+@.VIRTEX@>
+to input a format file in order to get started. \.{VIRTEX} typically has
+more memory capacity than \.{INITEX}, because it does not need the space
+consumed by the auxiliary hyphenation tables and the numerous calls on
+|primitive|, etc.
+
+The \.{VIRTEX} program cannot read a format file instantaneously, of course;
+the best implementations therefore allow for production versions of \TeX\ that
+not only avoid the loading routine for \PASCAL\ object code, they also have
+a format file pre-loaded. This is impossible to do if we stick to standard
+\PASCAL; but there is a simple way to fool many systems into avoiding the
+initialization, as follows:\quad(1)~We declare a global integer variable
+called |ready_already|. The probability is negligible that this
+variable holds any particular value like 314159 when \.{VIRTEX} is first
+loaded.\quad(2)~After we have read in a format file and initialized
+everything, we set |ready_already=314159|.\quad(3)~Soon \.{VIRTEX}
+will print `\.*', waiting for more input; and at this point we
+interrupt the program and save its core image in some form that the
+operating system can reload speedily.\quad(4)~When that core image is
+activated, the program starts again at the beginning; but now
+|ready_already==314159| and all the other global variables have
+their initial values too. The former chastity has vanished!
+
+In other words, if we allow ourselves to test the condition
+|ready_already==314159|, before |ready_already| has been
+assigned a value, we can avoid the lengthy initialization. Dirty tricks
+rarely pay off so handsomely.
+@^dirty \PASCAL@>
+@^system dependencies@>
+
+On systems that allow such preloading, the standard program called \.{TeX}
+should be the one that has \.{plain} format preloaded, since that agrees
+with {\sl The \TeX book}. Other versions, e.g., \.{AmSTeX}, should also
+@:TeXbook}{\sl The \TeX book@>
+@.AmSTeX@>
+@.plain@>
+be provided for commonly used formats.
+
+@<Glob...@>=
+static int @!ready_already; /*a sacrifice of purity for economy*/
+
+@ Now this is really it: \TeX\ starts and ends here.
+
+The initial test involving |ready_already| should be deleted if the
+\PASCAL\ runtime system is smart enough to detect such a ``mistake.''
+@^system dependencies@>
+
+@p
+int main(int argc, char *argv[]) {@! /*|start_here|*/
+ main_init(argc, argv); /* \TeX\ Live */
+history=fatal_error_stop; /*in case we quit during initialization*/
+t_open_out; /*open the terminal for output*/
+if (ready_already==314159) goto start_of_TEX;
+@<Check the ``constant'' values...@>@;
+if (bad > 0)
+ {@+wterm_ln("Ouch---my internal constants have been clobbered!"
+ "---case %d", bad);
+@.Ouch...clobbered@>
+ exit(0);
+ }
+get_strings_started();
+initialize(); /*set global variables to their starting values*/
+#ifdef @!INIT
+if (iniversion) /* \TeX\ Live */
+{ init_prim(); /*call |primitive| for each primitive*/
+ init_str_ptr=str_ptr;init_pool_ptr=pool_ptr;fix_date_and_time(); }
+#endif
+ready_already=314159;
+start_of_TEX: @<Initialize the output routines@>;
+@<Get the first line of input and prepare to start@>;
+history=spotless; /*ready to go!*/
+main_control(); /*come to life*/
+final_cleanup(); /*prepare for death*/
+close_files_and_terminate();
+ready_already=0;
+return 0; }
+
+@ Here we do whatever is needed to complete \TeX's job gracefully on the
+local operating system. The code here might come into play after a fatal
+error; it must therefore consist entirely of ``safe'' operations that
+cannot produce error messages. For example, it would be a mistake to call
+|str_room| or |make_string| at this time, because a call on |overflow|
+might lead to an infinite loop.
+@^system dependencies@>
+(Actually there's one way to get error messages, via |prepare_mag|;
+but that can't cause infinite recursion.)
+@^recursion@>
+
+If |final_cleanup| is bypassed, this program doesn't bother to close
+the input files that may still be open.
+
+@<Last-minute...@>=
+static void close_files_and_terminate(void)
+{@+int k; /*all-purpose index*/
+@<Finish the extensions@>;new_line_char=-1;
+#ifdef @!STAT
+if (tracing_stats > 0) @<Output statistics about this job@>;@;
+#endif
+wake_up_terminal;@<Finish the \.{DVI} file@>;
+if (log_opened)
+ {@+wlog_cr;a_close(&log_file);selector=selector-2;
+ if (selector==term_only)
+ {@+print_nl("Transcript written on ");
+@.Transcript written...@>
+ slow_print(log_name);print_char('.');print_nl("");
+ }
+ }
+}
+
+@ The present section goes directly to the log file instead of using
+|print| commands, because there's no need for these strings to take
+up |str_pool| memory when a non-{\bf stat} version of \TeX\ is being used.
+
+@<Output statistics...@>=
+if (log_opened)
+ {@+wlog_ln(" ");
+ wlog_ln("Here is how much of TeX's memory you used:");
+@.Here is how much...@>
+ wlog(" %d string", str_ptr-init_str_ptr);
+ if (str_ptr!=init_str_ptr+1) wlog( "s" );
+ wlog_ln( " out of %d", max_strings-init_str_ptr);@/
+ wlog_ln( " %d string characters out of %d", pool_ptr-init_pool_ptr,
+ pool_size-init_pool_ptr);@/
+ wlog_ln(" %d words of memory out of %d", lo_mem_max-mem_min+mem_end-hi_mem_min+2,@|
+ mem_end+1-mem_min);@/
+ wlog_ln(" %d multiletter control sequences out of %d", cs_count, hash_size);@/
+ wlog(" %d words of font info for %d font", fmem_ptr, font_ptr-font_base);
+ if (font_ptr!=font_base+1) wlog("s");
+ wlog_ln( ", out of %d for %d", font_mem_size, font_max-font_base);@/
+ wlog(" %d hyphenation exception", hyph_count);
+ if (hyph_count!=1) wlog("s");
+ wlog_ln( " out of %d", hyph_size);@/
+ wlog_ln(" %di,%dn,%dp,%db,%ds stack positions out of %di,%dn,%dp,%db,%ds",
+ max_in_stack, max_nest_stack,@|
+ max_param_stack,@|
+ max_buf_stack+1,@|
+ max_save_stack+6,@|
+ stack_size,
+ nest_size,
+ param_size,
+ buf_size,
+ save_size );
+ }
+
+@ We get to the |final_cleanup| routine when \.{\\end} or \.{\\dump} has
+been scanned and |its_all_over|\kern-2pt.
+
+@<Last-minute...@>=
+static void final_cleanup(void)
+{@+
+int c; /*0 for \.{\\end}, 1 for \.{\\dump}*/
+c=cur_chr;if (c!=1) new_line_char=-1;
+if (job_name==0) open_log_file();
+while (input_ptr > 0)
+ if (state==token_list) end_token_list();@+else end_file_reading();
+while (open_parens > 0)
+ {@+print(" )");decr(open_parens);
+ }
+if (cur_level > level_one)
+ {@+print_nl("(");print_esc("end occurred ");
+ print("inside a group at level ");
+@:end\_}{\.{(\\end occurred...)}@>
+ print_int(cur_level-level_one);print_char(')');
+ if (eTeX_ex) show_save_groups();
+ }
+while (cond_ptr!=null)
+ {@+print_nl("(");print_esc("end occurred ");
+ print("when ");print_cmd_chr(if_test, cur_if);
+ if (if_line!=0)
+ {@+print(" on line ");print_int(if_line);
+ }
+ print(" was incomplete)");
+ if_line=if_line_field(cond_ptr);
+ cur_if=subtype(cond_ptr);temp_ptr=cond_ptr;
+ cond_ptr=link(cond_ptr);free_node(temp_ptr, if_node_size);
+ }
+if (history!=spotless)
+ if (((history==warning_issued)||(interaction < error_stop_mode)))
+ if (selector==term_and_log)
+ {@+selector=term_only;
+ print_nl("(see the transcript file for additional information)");
+@.see the transcript file...@>
+ selector=term_and_log;
+ }
+if (c==1)
+ {
+#ifdef @!INIT
+for (c=top_mark_code; c<=split_bot_mark_code; c++)
+ if (cur_mark[c]!=null) delete_token_ref(cur_mark[c]);
+ if (sa_mark!=null)
+ if (do_marks(destroy_marks, 0, sa_mark)) sa_mark=null;
+ for (c=last_box_code; c<=vsplit_code; c++) flush_node_list(disc_ptr[c]);
+ if (last_glue!=max_halfword) delete_glue_ref(last_glue);
+ store_fmt_file();return;
+#endif
+ print_nl("(\\dump is performed only by INITEX)");return;
+@:dump\_}{\.{\\dump...only by INITEX}@>
+ }
+}
+
+@ @<Last-minute...@>=
+#ifdef @!INIT
+static void init_prim(void) /*initialize all the primitives*/
+{@+no_new_control_sequence=false;
+first=0;
+@<Put each...@>;
+no_new_control_sequence=true;
+}
+#endif
+
+@ When we begin the following code, \TeX's tables may still contain garbage;
+the strings might not even be present. Thus we must proceed cautiously to get
+bootstrapped in.
+
+But when we finish this part of the program, \TeX\ is ready to call on the
+|main_control| routine to do its work.
+
+@<Get the first line...@>=
+{@+@<Initialize the input routines@>;
+@<Enable \eTeX\ and furthermore Prote, if requested@>@;@/
+if ((format_ident==0)||(buffer[loc]=='&'))
+ {@+if (format_ident!=0) initialize(); /*erase preloaded format*/
+ if (!open_fmt_file()) exit(0);
+ if (!load_fmt_file())
+ {@+w_close(&fmt_file);exit(0);
+ }
+ w_close(&fmt_file);
+ while ((loc < limit)&&(buffer[loc]==' ')) incr(loc);
+ }
+if (eTeX_ex) wterm_ln("entering extended mode");
+if (Prote_ex) {@+Prote_initialize();
+ }
+if (end_line_char_inactive) decr(limit);
+else buffer[limit]=end_line_char;
+fix_date_and_time();@/
+@<Initialize the print |selector|...@>;
+if ((loc < limit)&&(cat_code(buffer[loc])!=escape)) start_input();
+ /*\.{\\input} assumed*/
+}
+
+@* Debugging.
+Once \TeX\ is working, you should be able to diagnose most errors with
+the \.{\\show} commands and other diagnostic features. But for the initial
+stages of debugging, and for the revelation of really deep mysteries, you
+can compile \TeX\ with a few more aids, including the \PASCAL\ runtime
+checks and its debugger. An additional routine called |debug_help|
+will also come into play when you type `\.D' after an error message;
+|debug_help| also occurs just before a fatal error causes \TeX\ to succumb.
+@^debugging@>
+@^system dependencies@>
+
+The interface to |debug_help| is primitive, but it is good enough when used
+with a \PASCAL\ debugger that allows you to set breakpoints and to read
+variables and change their values. After getting the prompt `\.{debug \#}', you
+type either a negative number (this exits |debug_help|), or zero (this
+goes to a location where you can set a breakpoint, thereby entering into
+dialog with the \PASCAL\ debugger), or a positive number |m| followed by
+an argument |n|. The meaning of |m| and |n| will be clear from the
+program below. (If |m==13|, there is an additional argument, |l|.)
+@.debug \#@>
+
+@<Last-minute...@>=
+#ifdef @!DEBUG
+static void debug_help(void) /*routine to display various things*/
+{@+
+int k, @!l, @!m, @!n;
+clear_terminal;
+ loop{@+wake_up_terminal;
+ print_nl("debug # (-1 to exit):");update_terminal;
+@.debug \#@>
+ if (fscanf(term_in.f," %d",&m)<1 ||
+ m < 0) return;
+ else if (m==0)
+ {@+goto breakpoint;@/ /*go to every declared label at least once*/
+ breakpoint: m=0; /*'BREAKPOINT'*/@/
+ }
+ else{@+fscanf(term_in.f," %d",&n);
+ switch (m) {
+ @t\4@>@<Numbered cases for |debug_help|@>@;
+ default:print("?");
+ }
+ }
+ }
+}
+#endif
+
+@ @<Numbered cases...@>=
+case 1: print_word(mem[n]);@+break; /*display |mem[n]| in all forms*/
+case 2: print_int(info(n));@+break;
+case 3: print_int(link(n));@+break;
+case 4: print_word(eqtb[n]);@+break;
+case 5: print_word(font_info[n]);@+break;
+case 6: print_word(save_stack[n]);@+break;
+case 7: show_box(n);@+break;
+ /*show a box, abbreviated by |show_box_depth| and |show_box_breadth|*/
+case 8: {@+breadth_max=10000;depth_threshold=pool_size-pool_ptr-10;
+ show_node_list(n); /*show a box in its entirety*/
+ } @+break;
+case 9: show_token_list(n, null, 1000);@+break;
+case 10: slow_print(n);@+break;
+case 11: check_mem(n > 0);@+break; /*check wellformedness; print new busy locations if |n > 0|*/
+case 12: search_mem(n);@+break; /*look for pointers to |n|*/
+case 13: {@+fscanf(term_in.f," %d",&l);print_cmd_chr(n, l);
+ } @+break;
+case 14: for (k=0; k<=n; k++) printn(buffer[k]);@+break;
+case 15: {@+font_in_short_display=null_font;short_display(n);
+ } @+break;
+case 16: panicking=!panicking;@+break;
+
+@* Extensions.
+The program above includes a bunch of ``hooks'' that allow further
+capabilities to be added without upsetting \TeX's basic structure.
+Most of these hooks are concerned with ``whatsit'' nodes, which are
+intended to be used for special purposes; whenever a new extension to
+\TeX\ involves a new kind of whatsit node, a corresponding change needs
+to be made to the routines below that deal with such nodes,
+but it will usually be unnecessary to make many changes to the
+other parts of this program.
+
+In order to demonstrate how extensions can be made, we shall treat
+`\.{\\write}', `\.{\\openout}', `\.{\\closeout}', `\.{\\immediate}',
+`\.{\\special}', and `\.{\\setlanguage}' as if they were extensions.
+These commands are actually primitives of \TeX, and they should
+appear in all implementations of the system; but let's try to imagine
+that they aren't. Then the program below illustrates how a person
+could add them.
+
+Sometimes, of course, an extension will require changes to \TeX\ itself;
+no system of hooks could be complete enough for all conceivable extensions.
+The features associated with `\.{\\write}' are almost all confined to the
+following paragraphs, but there are small parts of the |print_ln| and
+|print_char| procedures that were introduced specifically to \.{\\write}
+characters. Furthermore one of the token lists recognized by the scanner
+is a |write_text|; and there are a few other miscellaneous places where we
+have already provided for some aspect of \.{\\write}. The goal of a \TeX\
+extender should be to minimize alterations to the standard parts of the
+program, and to avoid them completely if possible. He or she should also
+be quite sure that there's no easy way to accomplish the desired goals
+with the standard features that \TeX\ already has. ``Think thrice before
+extending,'' because that may save a lot of work, and it will also keep
+incompatible extensions of \TeX\ from proliferating.
+@^system dependencies@>
+@^extensions to \TeX@>
+
+@ First let's consider the format of whatsit nodes that are used to represent
+the data associated with \.{\\write} and its relatives. Recall that a whatsit
+has |type==whatsit_node|, and the |subtype| is supposed to distinguish
+different kinds of whatsits. Each node occupies two or more words; the
+exact number is immaterial, as long as it is readily determined from the
+|subtype| or other data.
+
+We shall introduce five |subtype| values here, corresponding to the
+control sequences \.{\\openout}, \.{\\write}, \.{\\closeout}, \.{\\special}, and
+\.{\\setlanguage}. The second word of I/O whatsits has a |write_stream| field
+that identifies the write-stream number (0 to 15, or 16 for out-of-range and
+positive, or 17 for out-of-range and negative).
+In the case of \.{\\write} and \.{\\special}, there is also a field that
+points to the reference count of a token list that should be sent. In the
+case of \.{\\openout}, we need three words and three auxiliary subfields
+to hold the string numbers for name, area, and extension.
+
+@d write_node_size 2 /*number of words in a write/whatsit node*/
+@d open_node_size 3 /*number of words in an open/whatsit node*/
+@d open_node 0 /*|subtype| in whatsits that represent files to \.{\\openout}*/
+@d write_node 1 /*|subtype| in whatsits that represent things to \.{\\write}*/
+@d close_node 2 /*|subtype| in whatsits that represent streams to \.{\\closeout}*/
+@d special_node 3 /*|subtype| in whatsits that represent \.{\\special} things*/
+@d language_node 4 /*|subtype| in whatsits that change the current language*/
+@d what_lang(A) link(A+1) /*language number, in the range |0 dotdot 255|*/
+@d what_lhm(A) type(A+1) /*minimum left fragment, in the range |1 dotdot 63|*/
+@d what_rhm(A) subtype(A+1) /*minimum right fragment, in the range |1 dotdot 63|*/
+@d write_tokens(A) link(A+1) /*reference count of token list to write*/
+@d write_stream(A) info(A+1) /*stream number (0 to 17)*/
+@d open_name(A) link(A+1) /*string number of file name to open*/
+@d open_area(A) info(A+2) /*string number of file area for |open_name|*/
+@d open_ext(A) link(A+2) /*string number of file extension for |open_name|*/
+
+@ The sixteen possible \.{\\write} streams are represented by the |write_file|
+array. The |j|th file is open if and only if |write_open[j]==true|. The last
+two streams are special; |write_open[16]| represents a stream number
+greater than 15, while |write_open[17]| represents a negative stream number,
+and both of these variables are always |false|.
+
+@<Glob...@>=
+static alpha_file @!write_file[16];
+static bool @!write_open[18];
+
+@ @<Set init...@>=
+for (k=0; k<=17; k++) write_open[k]=false;
+
+@ Extensions might introduce new command codes; but it's best to use
+|extension| with a modifier, whenever possible, so that |main_control|
+stays the same.
+
+@d immediate_code 4 /*command modifier for \.{\\immediate}*/
+@d latex_first_extension_code 5
+@d latespecial_node (latex_first_extension_code+0) /*|subtype| in whatsits that represent \.{\\special} things expanded during output*/
+@d set_language_code (latex_first_extension_code+1) /*command modifier for \.{\\setlanguage}*/
+@d TeX_last_extension_cmd_mod set_language_code
+
+@<Put each...@>=
+primitive("openout", extension, open_node);@/
+@!@:open\_out\_}{\.{\\openout} primitive@>
+primitive("write", extension, write_node);write_loc=cur_val;@/
+@!@:write\_}{\.{\\write} primitive@>
+primitive("closeout", extension, close_node);@/
+@!@:close\_out\_}{\.{\\closeout} primitive@>
+primitive("special", extension, special_node);@/
+@!@:special\_}{\.{\\special} primitive@>
+primitive("immediate", extension, immediate_code);@/
+@!@:immediate\_}{\.{\\immediate} primitive@>
+primitive("setlanguage", extension, set_language_code);@/
+@!@:set\_language\_}{\.{\\setlanguage} primitive@>
+
+@ The variable |write_loc| just introduced is used to provide an
+appropriate error message in case of ``runaway'' write texts.
+
+@<Glob...@>=
+static pointer @!write_loc; /*|eqtb| address of \.{\\write}*/
+
+@ @<Cases of |print_cmd_chr|...@>=
+case extension: switch (chr_code) {
+ case open_node: print_esc("openout");@+break;
+ case write_node: print_esc("write");@+break;
+ case close_node: print_esc("closeout");@+break;
+ case special_node: print_esc("special");@+break;
+ case immediate_code: print_esc("immediate");@+break;
+ case set_language_code: print_esc("setlanguage");@+break;
+ @/@<Cases of |extension| for |print_cmd_chr|@>@/
+ default:print("[unknown extension!]");
+ } @+break;
+
+
+
+@ When an |extension| command occurs in |main_control|, in any mode,
+the |do_extension| routine is called.
+
+@<Cases of |main_control| that are for extensions...@>=
+any_mode(extension): do_extension();
+
+@ @<Declare act...@>=
+@t\4@>@<Declare procedures needed in |do_extension|@>@;
+static void do_extension(void)
+{@+int @!k; /*all-purpose integer*/
+pointer @!p; /*all-purpose pointer*/
+switch (cur_chr) {
+case open_node: @<Implement \.{\\openout}@>@;@+break;
+case write_node: @<Implement \.{\\write}@>@;@+break;
+case close_node: @<Implement \.{\\closeout}@>@;@+break;
+case special_node: @<Implement \.{\\special}@>@;@+break;
+case immediate_code: @<Implement \.{\\immediate}@>@;@+break;
+case set_language_code: @<Implement \.{\\setlanguage}@>@;@+break;
+@/@<Cases for |do_extension|@>@/
+default:confusion("ext1");
+@:this can't happen ext1}{\quad ext1@>
+}
+}
+
+@ Here is a subroutine that creates a whatsit node having a given |subtype|
+and a given number of words. It initializes only the first word of the whatsit,
+and appends it to the current list.
+
+@<Declare procedures needed in |do_extension|@>=
+static void new_whatsit(small_number @!s, small_number @!w)
+{@+pointer p; /*the new node*/
+p=get_node(w);type(p)=whatsit_node;subtype(p)=s;
+link(tail)=p;tail=p;
+}
+
+@ The next subroutine uses |cur_chr| to decide what sort of whatsit is
+involved, and also inserts a |write_stream| number.
+
+@<Declare procedures needed in |do_ext...@>=
+static void new_write_whatsit(small_number @!w)
+{@+new_whatsit(cur_chr, w);
+if (w!=write_node_size) scan_four_bit_int();
+else{@+scan_int();
+ if (cur_val < 0) cur_val=17;
+ else if (cur_val > 15) cur_val=16;
+ }
+write_stream(tail)=cur_val;
+}
+
+@ @<Implement \.{\\openout}@>=
+{@+new_write_whatsit(open_node_size);
+scan_optional_equals();scan_file_name();@/
+open_name(tail)=cur_name;open_area(tail)=cur_area;open_ext(tail)=cur_ext;
+}
+
+@ When `\.{\\write 12\{...\}}' appears, we scan the token list `\.{\{...\}}'
+without expanding its macros; the macros will be expanded later when this
+token list is rescanned.
+
+@<Implement \.{\\write}@>=
+{@+k=cur_cs;new_write_whatsit(write_node_size);@/
+cur_cs=k;p=scan_toks(false, false);write_tokens(tail)=def_ref;
+}
+
+@ @<Implement \.{\\closeout}@>=
+{@+new_write_whatsit(write_node_size);write_tokens(tail)=null;
+}
+
+@ When `\.{\\special\{...\}}' appears, we expand the macros in the token
+list as in \.{\\xdef} and \.{\\mark}. When marked with \.{shipout}, we keep
+tokens unexpanded for now.
+
+@<Implement \.{\\special}@>=
+{@+if (scan_keyword("shipout"))
+{@+new_whatsit(latespecial_node, write_node_size);write_stream(tail)=null;
+p=scan_toks(false, false);write_tokens(tail)=def_ref;
+} else
+{@+new_whatsit(special_node, write_node_size);write_stream(tail)=null;
+p=scan_toks(false, true);write_tokens(tail)=def_ref;
+} }
+
+@ Each new type of node that appears in our data structure must be capable
+of being displayed, copied, destroyed, and so on. The routines that we
+need for write-oriented whatsits are somewhat like those for mark nodes;
+other extensions might, of course, involve more subtlety here.
+
+@<Basic printing...@>=
+static void print_write_whatsit(char *@!s, pointer @!p)
+{@+print_esc(s);
+if (write_stream(p) < 16) print_int(write_stream(p));
+else if (write_stream(p)==16) print_char('*');
+@.*\relax@>
+else print_char('-');
+}
+
+@ @<Display the whatsit...@>=
+switch (subtype(p)) {
+case open_node: {@+print_write_whatsit("openout", p);
+ print_char('=');print_file_name(open_name(p), open_area(p), open_ext(p));
+ } @+break;
+case write_node: {@+print_write_whatsit("write", p);
+ print_mark(write_tokens(p));
+ } @+break;
+case close_node: print_write_whatsit("closeout", p);@+break;
+case latespecial_node: {@+print_esc("special");print(" shipout");
+ print_mark(write_tokens(p));
+ } @+break;
+case special_node: {@+print_esc("special");
+ print_mark(write_tokens(p));
+ } @+break;
+case language_node: {@+print_esc("setlanguage");
+ print_int(what_lang(p));print(" (hyphenmin ");
+ print_int(what_lhm(p));print_char(',');
+ print_int(what_rhm(p));print_char(')');
+ } @+break;
+@/@<Cases for displaying the |whatsit| node@>@/
+default:print("whatsit?");
+}
+
+@ @<Make a partial copy of the whatsit...@>=
+switch (subtype(p)) {
+case open_node: {@+r=get_node(open_node_size);words=open_node_size;
+ } @+break;
+case write_node: case special_node: case latespecial_node: {@+r=get_node(write_node_size);
+ add_token_ref(write_tokens(p));words=write_node_size;
+ } @+break;
+case close_node: case language_node: {@+r=get_node(small_node_size);
+ words=small_node_size;
+ } @+break;
+@/@<Cases for making a partial copy of the whatsit node@>@/
+default:confusion("ext2");
+@:this can't happen ext2}{\quad ext2@>
+}
+
+@ @<Wipe out the whatsit...@>=
+{@+switch (subtype(p)) {
+case open_node: free_node(p, open_node_size);@+break;
+case write_node: case special_node: case latespecial_node: {@+delete_token_ref(write_tokens(p));
+ free_node(p, write_node_size);goto done;
+ }
+case close_node: case language_node: free_node(p, small_node_size);@+break;
+@/@<Cases for wiping out the whatsit node@>@/
+default:confusion("ext3");
+@:this can't happen ext3}{\quad ext3@>
+} @/
+goto done;
+}
+
+@ @<Incorporate a whatsit node into a vbox@>=do_nothing
+
+@ @<Incorporate a whatsit node into an hbox@>=do_nothing
+
+@ @<Let |d| be the width of the whatsit |p|@>=d=0
+
+@ @d adv_past(A) @+if (subtype(A)==language_node)
+ {@+cur_lang=what_lang(A);l_hyf=what_lhm(A);r_hyf=what_rhm(A);
+ set_hyph_index;
+ }
+
+@<Advance \(p)past a whatsit node in the \(l)|line_break| loop@>=@+
+adv_past(cur_p)
+
+@ @<Advance \(p)past a whatsit node in the \(p)pre-hyphenation loop@>=@+
+adv_past(s)
+
+@ @<Prepare to move whatsit |p| to the current page, then |goto contribute|@>=
+goto contribute
+
+@ @<Process whatsit |p| in |vert_break| loop, |goto not_found|@>=
+goto not_found
+
+@ @<Output the whatsit node |p| in a vlist@>=
+out_what(p)
+
+@ @<Output the whatsit node |p| in an hlist@>=
+out_what(p)
+
+@ After all this preliminary shuffling, we come finally to the routines
+that actually send out the requested data. Let's do \.{\\special} first
+(it's easier).
+
+@<Declare procedures needed in |hlist_out|, |vlist_out|@>=
+static void special_out(pointer @!p)
+{@+pointer @!q, @!r; /*temporary variables for list manipulation*/
+int @!old_mode; /*saved |mode|*/
+
+if (subtype(p)==latespecial_node)
+ {@+@<Expand macros in the token list and make |link(def_ref)| point to the
+result@>;
+ write_tokens(p)=def_ref;
+ }
+}
+
+@ To write a token list, we must run it through \TeX's scanner, expanding
+macros and \.{\\the} and \.{\\number}, etc. This might cause runaways,
+if a delimited macro parameter isn't matched, and runaways would be
+extremely confusing since we are calling on \TeX's scanner in the middle
+of a \.{\\shipout} command. Therefore we will put a dummy control sequence as
+a ``stopper,'' right after the token list. This control sequence is
+artificially defined to be \.{\\outer}.
+@:end\_write\_}{\.{\\endwrite}@>
+
+@<Initialize table...@>=
+text(end_write)=s_no("endwrite");eq_level(end_write)=level_one;
+eq_type(end_write)=outer_call;equiv(end_write)=null;
+
+@ @<Declare procedures needed in |hlist_out|, |vlist_out|@>=
+static void write_out(pointer @!p)
+{@+int old_setting; /*holds print |selector|*/
+int @!old_mode; /*saved |mode|*/
+small_number @!j; /*write stream number*/
+pointer @!q, @!r; /*temporary variables for list manipulation*/
+@<Expand macros in the token list and make |link(def_ref)| point to the result@>;
+old_setting=selector;j=write_stream(p);
+if (write_open[j]) selector=j;
+else{@+ /*write to the terminal if file isn't open*/
+ if ((j==17)&&(selector==term_and_log)) selector=log_only;
+ print_nl("");
+ }
+token_show(def_ref);print_ln();
+flush_list(def_ref);selector=old_setting;
+}
+
+@ The final line of this routine is slightly subtle; at least, the author
+didn't think about it until getting burnt! There is a used-up token list
+@^Knuth, Donald Ervin@>
+on the stack, namely the one that contained |end_write_token|. (We
+insert this artificial `\.{\\endwrite}' to prevent runaways, as explained
+above.) If it were not removed, and if there were numerous writes on a
+single page, the stack would overflow.
+
+@d end_write_token cs_token_flag+end_write
+
+@<Expand macros in the token list and...@>=
+q=get_avail();info(q)=right_brace_token+'}';@/
+fl_mem[q]=FILE_LINE(system_file,system_insert);
+r=get_avail();link(q)=r;info(r)=end_write_token;ins_list(q);@/
+fl_mem[r]=FILE_LINE(system_file,system_insert);
+begin_token_list(write_tokens(p), write_text);@/
+q=get_avail();info(q)=left_brace_token+'{';ins_list(q);
+fl_mem[q]=FILE_LINE(system_file,system_insert);
+
+ /*now we're ready to scan
+ `\.\{$\langle\,$token list$\,\rangle$\.{\} \\endwrite}'*/
+old_mode=mode;mode=0;
+ /*disable \.{\\prevdepth}, \.{\\spacefactor}, \.{\\lastskip}, \.{\\prevgraf}*/
+cur_cs=write_loc;q=scan_toks(false, true); /*expand macros, etc.*/
+get_token();@+if (cur_tok!=end_write_token)
+ @<Recover from an unbalanced write command@>;
+mode=old_mode;
+end_token_list() /*conserve stack space*/
+
+@ @<Recover from an unbalanced write command@>=
+{@+print_err("Unbalanced write command");
+@.Unbalanced write...@>
+help2("On this page there's a \\write with fewer real {'s than }'s.",@/
+"I can't handle that very well; good luck.");error();
+@/do@+{get_token();
+}@+ while (!(cur_tok==end_write_token));
+}
+
+@ The |out_what| procedure takes care of outputting whatsit nodes for
+|vlist_out| and |hlist_out|\kern-.3pt.
+
+@<Declare procedures needed in |hlist_out|, |vlist_out|@>=
+@t\4@>@<Declare procedures needed in |out_what|@>@;
+static void out_what(pointer @!p)
+{@+small_number j; /*write stream number*/
+switch (subtype(p)) {
+case open_node: case write_node: case close_node: @<Do some work that has
+been queued up for \.{\\write}@>@;@+break;
+case special_node: special_out(p);@+break;
+case language_node: do_nothing;@+break;
+@/@<Cases for |out_what|@>@/
+default:confusion("ext4");
+@:this can't happen ext4}{\quad ext4@>
+}
+}
+
+@ We don't implement \.{\\write} inside of leaders. (The reason is that
+the number of times a leader box appears might be different in different
+implementations, due to machine-dependent rounding in the glue calculations.)
+@^leaders@>
+
+@<Do some work that has been queued up...@>=
+if (!doing_leaders)
+ {@+j=write_stream(p);
+ if (subtype(p)==write_node) write_out(p);
+ else{@+if (write_open[j]) a_close(&write_file[j]);
+ if (subtype(p)==close_node) write_open[j]=false;
+ else if (j < 16)
+ {@+cur_name=open_name(p);cur_area=open_area(p);
+ cur_ext=open_ext(p);
+ pack_cur_name(".tex");
+ while (!a_open_out(&write_file[j]))
+ prompt_file_name("output file name",".tex");
+ write_open[j]=true;
+ }
+ }
+ }
+
+@ The presence of `\.{\\immediate}' causes the |do_extension| procedure
+to descend to one level of recursion. Nothing happens unless \.{\\immediate}
+is followed by `\.{\\openout}', `\.{\\write}', or `\.{\\closeout}'.
+@^recursion@>
+
+@<Implement \.{\\immediate}@>=
+{@+get_x_token();
+if ((cur_cmd==extension)&&(cur_chr <= close_node))
+ {@+p=tail;do_extension(); /*append a whatsit node*/
+ out_what(tail); /*do the action immediately*/
+ flush_node_list(tail);tail=p;link(p)=null;
+ }
+else back_input();
+}
+
+@ The \.{\\language} extension is somewhat different.
+We need a subroutine that comes into play when a character of
+a non-|clang| language is being appended to the current paragraph.
+
+@<Declare action...@>=
+static void fix_language(void)
+{@+ASCII_code @!l; /*the new current language*/
+if (language <= 0) l=0;
+else if (language > 255) l=0;
+else l=language;
+if (l!=clang)
+ {@+new_whatsit(language_node, small_node_size);
+ what_lang(tail)=l;clang=l;@/
+ what_lhm(tail)=norm_min(left_hyphen_min);
+ what_rhm(tail)=norm_min(right_hyphen_min);
+ }
+}
+
+@ @<Implement \.{\\setlanguage}@>=
+if (abs(mode)!=hmode) report_illegal_case();
+else{@+new_whatsit(language_node, small_node_size);
+ scan_int();
+ if (cur_val <= 0) clang=0;
+ else if (cur_val > 255) clang=0;
+ else clang=cur_val;
+ what_lang(tail)=clang;
+ what_lhm(tail)=norm_min(left_hyphen_min);
+ what_rhm(tail)=norm_min(right_hyphen_min);
+ }
+
+@ @<Finish the extensions@>=
+for (k=0; k<=15; k++) if (write_open[k]) a_close(&write_file[k]);
+
+@* The extended features of \eTeX.
+The program has three modes of operation: (1)~In \TeX\ compatibility mode
+it fully deserves the name \TeX\ and there are neither extended features
+nor additional primitive commands. There are, however, a few
+modifications that would be legitimate in any implementation of \TeX\
+such as, e.g., preventing inadequate results of the glue to \.{DVI}
+unit conversion during |ship_out|. (2)~In extended mode there are
+additional primitive commands and the extended features of \eTeX\ are
+available. (3)~In \Prote\ mode there are supplementary primitive
+commands that will be discussed in the section below.
+
+The distinction between these three modes of operation initially takes
+place when a `virgin' \.{eINITEX} starts without reading a format file.
+Later on the values of all \eTeX\ state variables are inherited when
+\.{eVIRTEX} (or \.{eINITEX}) reads a format file.
+
+The code below is designed to work for cases where `$|@t\#\&{ifdef} \.{INIT}@>|\ldots|@t\#\&{endif}@>|$'
+is a run-time switch.
+
+@<Enable \eTeX\ and furthermore Prote, if requested@>=
+#ifdef @!INIT
+if (iniversion && (buffer[loc]=='*'||etexp)) /* \TeX\ Live */
+ {@+no_new_control_sequence=false;
+ @<Generate all \eTeX\ primitives@>@;
+ if (buffer[loc]=='*') incr(loc); /* \TeX\ Live */
+ eTeX_mode=1; /*enter extended mode*/
+ @<Initialize variables for \eTeX\ extended mode@>@;
+ if (buffer[loc]=='*'||ltxp) {@+
+ @<Check \Prote\ ``constant'' values for consistency@>@;
+ @<Generate all \Prote\ primitives@>@;
+ if (buffer[loc]=='*')incr(loc);
+ Prote_mode=1; /*enter \Prote\ mode*/
+ }
+ }
+#endif
+@;@/
+if (!no_new_control_sequence) /*just entered extended mode ?*/
+ no_new_control_sequence=true;@+else
+
+@ The \eTeX\ features available in extended mode are grouped into two
+categories: (1)~Some of them are permanently enabled and have no
+semantic effect as long as none of the additional primitives are
+executed. (2)~The remaining \eTeX\ features are optional and can be
+individually enabled and disabled. For each optional feature there is
+an \eTeX\ state variable named \.{\\...state}; the feature is enabled,
+resp.\ disabled by assigning a positive, resp.\ non-positive value to
+that integer.
+
+@d eTeX_state_base (int_base+eTeX_state_code)
+@d eTeX_state(A) eqtb[eTeX_state_base+A].i /*an \eTeX\ state variable*/
+@#
+@d eTeX_version_code eTeX_int /*code for \.{\\eTeXversion}*/
+
+@<Generate all \eTeX...@>=
+primitive("lastnodetype", last_item, last_node_type_code);
+@!@:last\_node\_type\_}{\.{\\lastnodetype} primitive@>
+primitive("eTeXversion", last_item, eTeX_version_code);
+@!@:eTeX\_version\_}{\.{\\eTeXversion} primitive@>
+primitive("eTeXrevision", convert, eTeX_revision_code);@/
+@!@:eTeX\_revision\_}{\.{\\eTeXrevision} primitive@>
+
+@ @<Cases of |last_item| for |print_cmd_chr|@>=
+case last_node_type_code: print_esc("lastnodetype");@+break;
+case eTeX_version_code: print_esc("eTeXversion");@+break;
+
+@ @<Cases for fetching an integer value@>=
+case eTeX_version_code: cur_val=eTeX_version;@+break;
+
+@ @d eTeX_ex (eTeX_mode==1) /*is this extended mode?*/
+
+@<Glob...@>=
+static int @!eTeX_mode; /*identifies compatibility and extended mode*/
+
+@ @<Initialize table entries...@>=
+eTeX_mode=0; /*initially we are in compatibility mode*/
+@<Initialize variables for \eTeX\ compatibility mode@>@;
+
+@ @<Dump the \eTeX\ state@>=
+dump_int(eTeX_mode);
+for (j=0; j<=eTeX_states-1; j++) eTeX_state(j)=0; /*disable all enhancements*/
+
+@ @<Undump the \eTeX\ state@>=
+undump(0, 1, eTeX_mode);
+if (eTeX_ex)
+ {@+@<Initialize variables for \eTeX\ extended mode@>;
+ }
+else{@+@<Initialize variables for \eTeX\ compatibility mode@>;
+ }
+
+@ The |eTeX_enabled| function simply returns its first argument as
+result. This argument is |true| if an optional \eTeX\ feature is
+currently enabled; otherwise, if the argument is |false|, the function
+gives an error message.
+
+@<Declare \eTeX\ procedures for use...@>=
+static bool eTeX_enabled(bool @!b, quarterword @!j, halfword @!k)
+{@+if (!b)
+ {@+print_err("Improper ");print_cmd_chr(j, k);
+ help1("Sorry, this optional e-TeX feature has been disabled.");error();
+ }
+return b;
+}
+
+@ First we implement the additional \eTeX\ parameters in the table of
+equivalents.
+
+@<Generate all \eTeX...@>=
+primitive("everyeof", assign_toks, every_eof_loc);
+@!@:every\_eof\_}{\.{\\everyeof} primitive@>
+primitive("tracingassigns", assign_int, int_base+tracing_assigns_code);@/
+@!@:tracing\_assigns\_}{\.{\\tracingassigns} primitive@>
+primitive("tracinggroups", assign_int, int_base+tracing_groups_code);@/
+@!@:tracing\_groups\_}{\.{\\tracinggroups} primitive@>
+primitive("tracingifs", assign_int, int_base+tracing_ifs_code);@/
+@!@:tracing\_ifs\_}{\.{\\tracingifs} primitive@>
+primitive("tracingscantokens", assign_int, int_base+tracing_scan_tokens_code);@/
+@!@:tracing\_scan\_tokens\_}{\.{\\tracingscantokens} primitive@>
+primitive("tracingnesting", assign_int, int_base+tracing_nesting_code);@/
+@!@:tracing\_nesting\_}{\.{\\tracingnesting} primitive@>
+primitive("savingvdiscards", assign_int, int_base+saving_vdiscards_code);@/
+@!@:saving\_vdiscards\_}{\.{\\savingvdiscards} primitive@>
+primitive("savinghyphcodes", assign_int, int_base+saving_hyph_codes_code);@/
+@!@:saving\_hyph\_codes\_}{\.{\\savinghyphcodes} primitive@>
+
+@ @d every_eof equiv(every_eof_loc)
+
+@<Cases of |assign_toks| for |print_cmd_chr|@>=
+case every_eof_loc: print_esc("everyeof");@+break;
+
+@ @<Cases for |print_param|@>=
+case tracing_assigns_code: print_esc("tracingassigns");@+break;
+case tracing_groups_code: print_esc("tracinggroups");@+break;
+case tracing_ifs_code: print_esc("tracingifs");@+break;
+case tracing_scan_tokens_code: print_esc("tracingscantokens");@+break;
+case tracing_nesting_code: print_esc("tracingnesting");@+break;
+case saving_vdiscards_code: print_esc("savingvdiscards");@+break;
+case saving_hyph_codes_code: print_esc("savinghyphcodes");@+break;
+
+@ In order to handle \.{\\everyeof} we need an array |eof_seen| of
+boolean variables.
+
+@<Glob...@>=
+static bool @!eof_seen0[max_in_open],
+ *const @!eof_seen = @!eof_seen0-1; /*has eof been seen?*/
+
+@ The |print_group| procedure prints the current level of grouping and
+the name corresponding to |cur_group|.
+
+@<Declare \eTeX\ procedures for tr...@>=
+static void print_group(bool @!e)
+{@+
+switch (cur_group) {
+ case bottom_level: {@+print("bottom level");return;
+ }
+ case simple_group: case semi_simple_group:
+ {@+if (cur_group==semi_simple_group) print("semi ");
+ print("simple");
+ } @+break;
+ case hbox_group: case adjusted_hbox_group:
+ {@+if (cur_group==adjusted_hbox_group) print("adjusted ");
+ print("hbox");
+ } @+break;
+ case vbox_group: print("vbox");@+break;
+ case vtop_group: print("vtop");@+break;
+ case align_group: case no_align_group:
+ {@+if (cur_group==no_align_group) print("no ");
+ print("align");
+ } @+break;
+ case output_group: print("output");@+break;
+ case disc_group: print("disc");@+break;
+ case insert_group: print("insert");@+break;
+ case vcenter_group: print("vcenter");@+break;
+ case math_group: case math_choice_group:
+ case math_shift_group: case math_left_group:
+ {@+print("math");
+ if (cur_group==math_choice_group) print(" choice");
+ else if (cur_group==math_shift_group) print(" shift");
+ else if (cur_group==math_left_group) print(" left");
+ }
+ } /*there are no other cases*/
+print(" group (level ");print_int(qo(cur_level));print_char(')');
+if (saved(-1)!=0)
+ {@+if (e) print(" entered at line ");else print(" at line ");
+ print_int(saved(-1));
+ }
+}
+
+@ The |group_trace| procedure is called when a new level of grouping
+begins (|e==false|) or ends (|e==true|) with |saved(-1)| containing the
+line number.
+
+@<Declare \eTeX\ procedures for tr...@>=
+#ifdef @!STAT
+static void group_trace(bool @!e)
+{@+begin_diagnostic();print_char('{');
+if (e) print("leaving ");else print("entering ");
+print_group(e);print_char('}');end_diagnostic(false);
+}
+#endif
+
+@ The \.{\\currentgrouplevel} and \.{\\currentgrouptype} commands return
+the current level of grouping and the type of the current group
+respectively.
+
+@d current_group_level_code (eTeX_int+1) /*code for \.{\\currentgrouplevel}*/
+@d current_group_type_code (eTeX_int+2) /*code for \.{\\currentgrouptype}*/
+
+@<Generate all \eTeX...@>=
+primitive("currentgrouplevel", last_item, current_group_level_code);
+@!@:current\_group\_level\_}{\.{\\currentgrouplevel} primitive@>
+primitive("currentgrouptype", last_item, current_group_type_code);
+@!@:current\_group\_type\_}{\.{\\currentgrouptype} primitive@>
+
+@ @<Cases of |last_item| for |print_cmd_chr|@>=
+case current_group_level_code: print_esc("currentgrouplevel");@+break;
+case current_group_type_code: print_esc("currentgrouptype");@+break;
+
+@ @<Cases for fetching an integer value@>=
+case current_group_level_code: cur_val=cur_level-level_one;@+break;
+case current_group_type_code: cur_val=cur_group;@+break;
+
+@ The \.{\\currentiflevel}, \.{\\currentiftype}, and
+\.{\\currentifbranch} commands return the current level of conditionals
+and the type and branch of the current conditional.
+
+@d current_if_level_code (eTeX_int+3) /*code for \.{\\currentiflevel}*/
+@d current_if_type_code (eTeX_int+4) /*code for \.{\\currentiftype}*/
+@d current_if_branch_code (eTeX_int+5) /*code for \.{\\currentifbranch}*/
+
+@<Generate all \eTeX...@>=
+primitive("currentiflevel", last_item, current_if_level_code);
+@!@:current\_if\_level\_}{\.{\\currentiflevel} primitive@>
+primitive("currentiftype", last_item, current_if_type_code);
+@!@:current\_if\_type\_}{\.{\\currentiftype} primitive@>
+primitive("currentifbranch", last_item, current_if_branch_code);
+@!@:current\_if\_branch\_}{\.{\\currentifbranch} primitive@>
+
+@ @<Cases of |last_item| for |print_cmd_chr|@>=
+case current_if_level_code: print_esc("currentiflevel");@+break;
+case current_if_type_code: print_esc("currentiftype");@+break;
+case current_if_branch_code: print_esc("currentifbranch");@+break;
+
+@ @<Cases for fetching an integer value@>=
+case current_if_level_code: {@+q=cond_ptr;cur_val=0;
+ while (q!=null)
+ {@+incr(cur_val);q=link(q);
+ }
+ } @+break;
+case current_if_type_code: if (cond_ptr==null) cur_val=0;
+ else if (cur_if < unless_code) cur_val=cur_if+1;
+ else cur_val=-(cur_if-unless_code+1);@+break;
+case current_if_branch_code:
+ if ((if_limit==or_code)||(if_limit==else_code)) cur_val=1;
+ else if (if_limit==fi_code) cur_val=-1;
+ else cur_val=0;@+break;
+
+@ The \.{\\fontcharwd}, \.{\\fontcharht}, \.{\\fontchardp}, and
+\.{\\fontcharic} commands return information about a character in a
+font.
+
+@d font_char_wd_code eTeX_dim /*code for \.{\\fontcharwd}*/
+@d font_char_ht_code (eTeX_dim+1) /*code for \.{\\fontcharht}*/
+@d font_char_dp_code (eTeX_dim+2) /*code for \.{\\fontchardp}*/
+@d font_char_ic_code (eTeX_dim+3) /*code for \.{\\fontcharic}*/
+
+@<Generate all \eTeX...@>=
+primitive("fontcharwd", last_item, font_char_wd_code);
+@!@:font\_char\_wd\_}{\.{\\fontcharwd} primitive@>
+primitive("fontcharht", last_item, font_char_ht_code);
+@!@:font\_char\_ht\_}{\.{\\fontcharht} primitive@>
+primitive("fontchardp", last_item, font_char_dp_code);
+@!@:font\_char\_dp\_}{\.{\\fontchardp} primitive@>
+primitive("fontcharic", last_item, font_char_ic_code);
+@!@:font\_char\_ic\_}{\.{\\fontcharic} primitive@>
+
+@ @<Cases of |last_item| for |print_cmd_chr|@>=
+case font_char_wd_code: print_esc("fontcharwd");@+break;
+case font_char_ht_code: print_esc("fontcharht");@+break;
+case font_char_dp_code: print_esc("fontchardp");@+break;
+case font_char_ic_code: print_esc("fontcharic");@+break;
+
+@ @<Cases for fetching a dimension value@>=
+case font_char_wd_code:
+case font_char_ht_code:
+case font_char_dp_code:
+case font_char_ic_code: {@+scan_font_ident();q=cur_val;scan_char_num();
+ if ((font_bc[q] <= cur_val)&&(font_ec[q] >= cur_val))
+ {@+i=char_info(q, qi(cur_val));
+ switch (m) {
+ case font_char_wd_code: cur_val=char_width(q, i);@+break;
+ case font_char_ht_code: cur_val=char_height(q, height_depth(i));@+break;
+ case font_char_dp_code: cur_val=char_depth(q, height_depth(i));@+break;
+ case font_char_ic_code: cur_val=char_italic(q, i);
+ } /*there are no other cases*/
+ }
+ else cur_val=0;
+ } @+break;
+
+@ The \.{\\parshapedimen}, \.{\\parshapeindent}, and \.{\\parshapelength}
+commands return the indent and length parameters of the current
+\.{\\parshape} specification.
+
+@d par_shape_length_code (eTeX_dim+4) /*code for \.{\\parshapelength}*/
+@d par_shape_indent_code (eTeX_dim+5) /*code for \.{\\parshapeindent}*/
+@d par_shape_dimen_code (eTeX_dim+6) /*code for \.{\\parshapedimen}*/
+
+@<Generate all \eTeX...@>=
+primitive("parshapelength", last_item, par_shape_length_code);
+@!@:par\_shape\_length\_}{\.{\\parshapelength} primitive@>
+primitive("parshapeindent", last_item, par_shape_indent_code);
+@!@:par\_shape\_indent\_}{\.{\\parshapeindent} primitive@>
+primitive("parshapedimen", last_item, par_shape_dimen_code);
+@!@:par\_shape\_dimen\_}{\.{\\parshapedimen} primitive@>
+
+@ @<Cases of |last_item| for |print_cmd_chr|@>=
+case par_shape_length_code: print_esc("parshapelength");@+break;
+case par_shape_indent_code: print_esc("parshapeindent");@+break;
+case par_shape_dimen_code: print_esc("parshapedimen");@+break;
+
+@ @<Cases for fetching a dimension value@>=
+case par_shape_length_code:
+case par_shape_indent_code:
+case par_shape_dimen_code: {@+q=cur_chr-par_shape_length_code;scan_int();
+ if ((par_shape_ptr==null)||(cur_val <= 0)) cur_val=0;
+ else{@+if (q==2)
+ {@+q=cur_val%2;cur_val=(cur_val+q)/2;
+ }
+ if (cur_val > info(par_shape_ptr)) cur_val=info(par_shape_ptr);
+ cur_val=mem[par_shape_ptr+2*cur_val-q].sc;
+ }
+ cur_val_level=dimen_val;
+ } @+break;
+
+@ The \.{\\showgroups} command displays all currently active grouping
+levels.
+
+@d show_groups 4 /* \.{\\showgroups} */
+
+@<Generate all \eTeX...@>=
+primitive("showgroups", xray, show_groups);
+@!@:show\_groups\_}{\.{\\showgroups} primitive@>
+
+@ @<Cases of |xray| for |print_cmd_chr|@>=
+case show_groups: print_esc("showgroups");@+break;
+
+@ @<Cases for |show_whatever|@>=
+case show_groups: {@+begin_diagnostic();show_save_groups();
+ } @+break;
+
+@ @<Types...@>=
+typedef int32_t save_pointer; /*index into |save_stack|*/
+
+@ The modifications of \TeX\ required for the display produced by the
+|show_save_groups| procedure were first discussed by Donald~E. Knuth in
+{\sl TUGboat\/} {\bf 11}, 165--170 and 499--511, 1990.
+@^Knuth, Donald Ervin@>
+
+In order to understand a group type we also have to know its mode.
+Since unrestricted horizontal modes are not associated with grouping,
+they are skipped when traversing the semantic nest.
+
+@<Declare \eTeX\ procedures for use...@>=
+static void show_save_groups(void)
+{@+
+int p; /*index into |nest|*/
+int @!m; /*mode*/
+save_pointer @!v; /*saved value of |save_ptr|*/
+quarterword @!l; /*saved value of |cur_level|*/
+group_code @!c; /*saved value of |cur_group|*/
+int @!a; /*to keep track of alignments*/
+int @!i;
+quarterword @!j;
+char *@!s;
+p=nest_ptr;nest[p]=cur_list; /*put the top level into the array*/
+v=save_ptr;l=cur_level;c=cur_group;
+save_ptr=cur_boundary;decr(cur_level);@/
+a=1;
+print_nl("");print_ln();
+loop@+{@+print_nl("### ");print_group(true);
+ if (cur_group==bottom_level) goto done;
+ @/do@+{m=nest[p].mode_field;
+ if (p > 0) decr(p);else m=vmode;
+ }@+ while (!(m!=hmode));
+ print(" (");
+ switch (cur_group) {
+ case simple_group: {@+incr(p);goto found2;
+ }
+ case hbox_group: case adjusted_hbox_group: s="hbox";@+break;
+ case vbox_group: s="vbox";@+break;
+ case vtop_group: s="vtop";@+break;
+ case align_group: if (a==0)
+ {@+if (m==-vmode) s="halign";else s="valign";
+ a=1;goto found1;
+ }
+ else{@+if (a==1) print("align entry");else print_esc("cr");
+ if (p >= a) p=p-a;
+ a=0;goto found;
+ } @+break;
+ case no_align_group:
+ {@+incr(p);a=-1;print_esc("noalign");goto found2;
+ }
+ case output_group:
+ {@+print_esc("output");goto found;
+ }
+ case math_group: goto found2;
+ case disc_group: case math_choice_group:
+ {@+if (cur_group==disc_group) print_esc("discretionary");
+ else print_esc("mathchoice");
+ for (i=1; i<=3; i++) if (i <= saved(-2)) print("{}");
+ goto found2;
+ }
+ case insert_group:
+ {@+if (saved(-2)==255) print_esc("vadjust");
+ else{@+print_esc("insert");print_int(saved(-2));
+ }
+ goto found2;
+ }
+ case vcenter_group: {@+s="vcenter";goto found1;
+ }
+ case semi_simple_group: {@+incr(p);print_esc("begingroup");goto found;
+ }
+ case math_shift_group:
+ {@+if (m==mmode) print_char('$');
+ else if (nest[p].mode_field==mmode)
+ {@+print_cmd_chr(eq_no, saved(-2));goto found;
+ }
+ print_char('$');goto found;
+ }
+ case math_left_group:
+ {@+if (type(nest[p+1].eTeX_aux_field)==left_noad) print_esc("left");
+ else print_esc("middle");
+ goto found;
+ }
+ } /*there are no other cases*/
+ @<Show the box context@>;
+ found1: print_esc(s);@<Show the box packaging info@>;
+ found2: print_char('{');
+ found: print_char(')');decr(cur_level);
+ cur_group=save_level(save_ptr);save_ptr=save_index(save_ptr);
+ }
+done: save_ptr=v;cur_level=l;cur_group=c;
+}
+
+@ @<Show the box packaging info@>=
+if (saved(-2)!=0)
+ {@+print_char(' ');
+ if (saved(-3)==exactly) print("to");else print("spread");
+ print_scaled(saved(-2));print("pt");
+ }
+
+@ @<Show the box context@>=
+i=saved(-4);
+if (i!=0)
+ if (i < box_flag)
+ {@+if (abs(nest[p].mode_field)==vmode) j=hmove;else j=vmove;
+ if (i > 0) print_cmd_chr(j, 0);else print_cmd_chr(j, 1);
+ print_scaled(abs(i));print("pt");
+ }
+ else if (i < ship_out_flag)
+ {@+if (i >= global_box_flag)
+ {@+print_esc("global");i=i-(global_box_flag-box_flag);
+ }
+ print_esc("setbox");print_int(i-box_flag);print_char('=');
+ }
+ else print_cmd_chr(leader_ship, i-(leader_flag-a_leaders))
+
+@ The |scan_general_text| procedure is much like |scan_toks(false, false)|,
+but will be invoked via |expand|, i.e., recursively.
+@^recursion@>
+
+@<Declare \eTeX\ procedures for sc...@>=
+static void scan_general_text(void);
+
+@ The token list (balanced text) created by |scan_general_text| begins
+at |link(temp_head)| and ends at |cur_val|. (If |cur_val==temp_head|,
+the list is empty.)
+
+@<Declare \eTeX\ procedures for tok...@>=
+static void scan_general_text(void)
+{@+
+int s; /*to save |scanner_status|*/
+pointer @!w; /*to save |warning_index|*/
+pointer @!d; /*to save |def_ref|*/
+pointer @!p; /*tail of the token list being built*/
+pointer @!q; /*new node being added to the token list via |store_new_token|*/
+halfword @!unbalance; /*number of unmatched left braces*/
+s=scanner_status;w=warning_index;d=def_ref;
+scanner_status=absorbing;warning_index=cur_cs;
+def_ref=get_avail();token_ref_count(def_ref)=null;p=def_ref;
+scan_left_brace(); /*remove the compulsory left brace*/
+unbalance=1;
+loop@+{@+get_token();
+ if (cur_tok < right_brace_limit)
+ if (cur_cmd < right_brace) incr(unbalance);
+ else{@+decr(unbalance);
+ if (unbalance==0) goto found;
+ }
+ store_new_token(cur_tok);
+ }
+found: q=link(def_ref);free_avail(def_ref); /*discard reference count*/
+if (q==null) cur_val=temp_head;@+else cur_val=p;
+link(temp_head)=q;
+scanner_status=s;warning_index=w;def_ref=d;
+}
+
+@ The \.{\\showtokens} command displays a token list.
+
+@d show_tokens 5 /* \.{\\showtokens} , must be odd! */
+
+@<Generate all \eTeX...@>=
+primitive("showtokens", xray, show_tokens);
+@!@:show\_tokens\_}{\.{\\showtokens} primitive@>
+
+@ @<Cases of |xray| for |print_cmd_chr|@>=
+case show_tokens: print_esc("showtokens");@+break;
+
+@ The \.{\\unexpanded} primitive prevents expansion of tokens much as
+the result from \.{\\the} applied to a token variable. The
+\.{\\detokenize} primitive converts a token list into a list of
+character tokens much as if the token list were written to a file. We
+use the fact that the command modifiers for \.{\\unexpanded} and
+\.{\\detokenize} are odd whereas those for \.{\\the} and \.{\\showthe}
+are even.
+
+@<Generate all \eTeX...@>=
+primitive("unexpanded", the, 1);@/
+@!@:unexpanded\_}{\.{\\unexpanded} primitive@>
+primitive("detokenize", the, show_tokens);@/
+@!@:detokenize\_}{\.{\\detokenize} primitive@>
+
+@ @<Cases of |the| for |print_cmd_chr|@>=;
+else if (chr_code==1) print_esc("unexpanded");
+else print_esc("detokenize")
+
+@ @<Handle \.{\\unexpanded} or \.{\\detokenize} and |return|@>=
+if (odd(cur_chr))
+ {@+c=cur_chr;scan_general_text();
+ if (c==1) return cur_val;
+ else{@+old_setting=selector;selector=new_string;b=pool_ptr;
+ p=get_avail();link(p)=link(temp_head);
+ token_show(p);flush_list(p);
+ selector=old_setting;return str_toks(b);
+ }
+
+ }
+
+@ The \.{\\showifs} command displays all currently active conditionals.
+
+@d show_ifs 6 /* \.{\\showifs} */
+
+@<Generate all \eTeX...@>=
+primitive("showifs", xray, show_ifs);
+@!@:show\_ifs\_}{\.{\\showifs} primitive@>
+
+@ @<Cases of |xray| for |print_cmd_chr|@>=
+case show_ifs: print_esc("showifs");@+break;
+
+@ @d print_if_line(A) if (A!=0)
+ {@+print(" entered on line ");print_int(A);
+ }
+
+@<Cases for |show_whatever|@>=
+case show_ifs: {@+begin_diagnostic();print_nl("");print_ln();
+ if (cond_ptr==null)
+ {@+print_nl("### ");print("no active conditionals");
+ }
+ else{@+p=cond_ptr;n=0;
+ @/do@+{incr(n);p=link(p);@+}@+ while (!(p==null));
+ p=cond_ptr;t=cur_if;l=if_line;m=if_limit;
+ @/do@+{print_nl("### level ");print_int(n);print(": ");
+ print_cmd_chr(if_test, t);
+ if (m==fi_code) print_esc("else");
+ print_if_line(l);
+ decr(n);t=subtype(p);l=if_line_field(p);m=type(p);p=link(p);
+ }@+ while (!(p==null));
+ }
+ } @+break;
+
+@ The \.{\\interactionmode} primitive allows to query and set the
+interaction mode.
+
+@<Generate all \eTeX...@>=
+primitive("interactionmode", set_page_int, 2);
+@!@:interaction\_mode\_}{\.{\\interactionmode} primitive@>
+
+@ @<Cases of |set_page_int| for |print_cmd_chr|@>=;
+else if (chr_code==2) print_esc("interactionmode")
+
+@ @<Cases for `Fetch the |dead_cycles| or the |insert_penalties|'@>=;
+else if (m==2) cur_val=interaction
+
+@ @<Declare \eTeX\ procedures for use...@>=
+static void new_interaction(void);
+
+@ @<Cases for |alter_integer|@>=;
+else if (c==2)
+ {@+if ((cur_val < batch_mode)||(cur_val > error_stop_mode))
+ {@+print_err("Bad interaction mode");
+@.Bad interaction mode@>
+ help2("Modes are 0=batch, 1=nonstop, 2=scroll, and",@/
+ "3=errorstop. Proceed, and I'll ignore this case.");
+ int_error(cur_val);
+ }
+ else{@+cur_chr=cur_val;new_interaction();
+ }
+ }
+
+@ The |middle| feature of \eTeX\ allows one ore several \.{\\middle}
+delimiters to appear between \.{\\left} and \.{\\right}.
+
+@<Generate all \eTeX...@>=
+primitive("middle", left_right, middle_noad);
+@!@:middle\_}{\.{\\middle} primitive@>
+
+@ @<Cases of |left_right| for |print_cmd_chr|@>=;
+else if (chr_code==middle_noad) print_esc("middle")
+
+@ The |scan_tokens| feature of \eTeX\ defines the \.{\\scantokens}
+primitive.
+
+@<Generate all \eTeX...@>=
+primitive("scantokens", input, 2);
+@!@:scan\_tokens\_}{\.{\\scantokens} primitive@>
+
+@ @<Cases of |input| for |print_cmd_chr|@>=;
+else if (chr_code==2) print_esc("scantokens")
+
+@ @<Cases for |input|@>=;
+else if (cur_chr==2) pseudo_start()
+
+@ The global variable |pseudo_files| is used to maintain a stack of
+pseudo files. The |info| field of each pseudo file points to a linked
+list of variable size nodes representing lines not yet processed: the
+|info| field of the first word contains the size of this node, all the
+following words contain ASCII codes.
+
+@<Glob...@>=
+static pointer @!pseudo_files; /*stack of pseudo files*/
+
+@ @<Set init...@>=
+pseudo_files=null;
+
+@ The |pseudo_start| procedure initiates reading from a pseudo file.
+
+@<Declare \eTeX\ procedures for ex...@>=
+static void pseudo_start(void);
+
+@ @<Declare \eTeX\ procedures for tok...@>=
+static void pseudo_start(void)
+{@+int old_setting; /*holds |selector| setting*/
+str_number @!s; /*string to be converted into a pseudo file*/
+pool_pointer @!l, @!m; /*indices into |str_pool|*/
+pointer @!p, @!q, @!r; /*for list construction*/
+four_quarters @!w; /*four ASCII codes*/
+int @!nl, @!sz;
+scan_general_text();
+old_setting=selector;selector=new_string;
+token_show(temp_head);selector=old_setting;
+flush_list(link(temp_head));
+str_room(1);s=make_string();
+@<Convert string |s| into a new pseudo file@>;
+flush_string;
+@<Initiate input from new pseudo file@>;
+}
+
+@ @<Convert string |s| into a new pseudo file@>=
+str_pool[pool_ptr]=si(' ');l=str_start[s];
+nl=si(new_line_char);
+p=get_avail();q=p;
+while (l < pool_ptr)
+ {@+m=l;
+ while ((l < pool_ptr)&&(str_pool[l]!=nl)) incr(l);
+ sz=(l-m+7)/4;
+ if (sz==1) sz=2;
+ r=get_node(sz);link(q)=r;q=r;info(q)=hi(sz);
+ while (sz > 2)
+ {@+decr(sz);incr(r);
+ w.b0=qi(so(str_pool[m]));w.b1=qi(so(str_pool[m+1]));
+ w.b2=qi(so(str_pool[m+2]));w.b3=qi(so(str_pool[m+3]));
+ mem[r].qqqq=w;m=m+4;
+ }
+ w.b0=qi(' ');w.b1=qi(' ');w.b2=qi(' ');w.b3=qi(' ');
+ if (l > m)
+ {@+w.b0=qi(so(str_pool[m]));
+ if (l > m+1)
+ {@+w.b1=qi(so(str_pool[m+1]));
+ if (l > m+2)
+ {@+w.b2=qi(so(str_pool[m+2]));
+ if (l > m+3) w.b3=qi(so(str_pool[m+3]));
+ }
+ }
+ }
+ mem[r+1].qqqq=w;
+ if (str_pool[l]==nl) incr(l);
+ }
+info(p)=link(p);link(p)=pseudo_files;pseudo_files=p
+
+@ @<Initiate input from new pseudo file@>=
+begin_file_reading(); /*set up |cur_file| and new level of input*/
+line=0;limit=start;loc=limit+1; /*force line read*/
+if (tracing_scan_tokens > 0)
+ {@+if (term_offset > max_print_line-3) print_ln();
+ else if ((term_offset > 0)||(file_offset > 0)) print_char(' ');
+ name=19;print("( ");incr(open_parens);update_terminal;
+ }
+else name=18
+
+@ Here we read a line from the current pseudo file into |buffer|.
+
+@<Declare \eTeX\ procedures for tr...@>=
+static bool pseudo_input(void) /*inputs the next line or returns |false|*/
+{@+pointer p; /*current line from pseudo file*/
+int @!sz; /*size of node |p|*/
+four_quarters @!w; /*four ASCII codes*/
+int @!r; /*loop index*/
+last=first; /*cf.\ Matthew 19\thinspace:\thinspace30*/
+p=info(pseudo_files);
+if (p==null) return false;
+else{@+info(pseudo_files)=link(p);sz=ho(info(p));
+ if (4*sz-3 >= buf_size-last)
+ @<Report overflow of the input buffer, and abort@>;
+ last=first;
+ for (r=p+1; r<=p+sz-1; r++)
+ {@+w=mem[r].qqqq;
+ buffer[last]=w.b0;buffer[last+1]=w.b1;
+ buffer[last+2]=w.b2;buffer[last+3]=w.b3;
+ last=last+4;
+ }
+ if (last >= max_buf_stack) max_buf_stack=last+1;
+ while ((last > first)&&(buffer[last-1]==' ')) decr(last);
+ free_node(p, sz);
+ return true;
+ }
+}
+
+@ When we are done with a pseudo file we `close' it.
+
+@<Declare \eTeX\ procedures for tr...@>=
+static void pseudo_close(void) /*close the top level pseudo file*/
+{@+pointer p, @!q;
+p=link(pseudo_files);q=info(pseudo_files);
+free_avail(pseudo_files);pseudo_files=p;
+while (q!=null)
+ {@+p=q;q=link(p);free_node(p, ho(info(p)));
+ }
+}
+
+@ @<Dump the \eTeX\ state@>=
+while (pseudo_files!=null) pseudo_close(); /*flush pseudo files*/
+
+@ @<Generate all \eTeX...@>=
+primitive("readline", read_to_cs, 1);@/
+@!@:read\_line\_}{\.{\\readline} primitive@>
+
+@ @<Cases of |read| for |print_cmd_chr|@>=;
+else print_esc("readline")
+
+@ @<Handle \.{\\readline} and |goto done|@>=
+if (j==1)
+ {@+while (loc <= limit) /*current line not yet finished*/
+ {@+cur_chr=buffer[loc];incr(loc);
+ if (cur_chr==' ') cur_tok=space_token;
+ @+else cur_tok=cur_chr+other_token;
+ store_new_token(cur_tok);
+ }
+ goto done;
+ }
+
+@ Here we define the additional conditionals of \eTeX\ as well as the
+\.{\\unless} prefix.
+
+@d if_def_code 17 /* `\.{\\ifdefined}' */
+@d if_cs_code 18 /* `\.{\\ifcsname}' */
+@d if_font_char_code 19 /* `\.{\\iffontchar}' */
+@d eTeX_last_if_test_cmd_mod if_font_char_code
+@d eTeX_last_expand_after_cmd_mod 1
+
+@<Generate all \eTeX...@>=
+primitive("unless", expand_after, 1);@/
+@!@:unless\_}{\.{\\unless} primitive@>
+primitive("ifdefined", if_test, if_def_code);
+@!@:if\_defined\_}{\.{\\ifdefined} primitive@>
+primitive("ifcsname", if_test, if_cs_code);
+@!@:if\_cs\_name\_}{\.{\\ifcsname} primitive@>
+primitive("iffontchar", if_test, if_font_char_code);
+@!@:if\_font\_char\_}{\.{\\iffontchar} primitive@>
+
+@ @<Cases of |expandafter| for |print_cmd_chr|@>=
+case 1: print_esc("unless");@+break;
+
+@ @<Cases of |if_test| for |print_cmd_chr|@>=
+case if_def_code: print_esc("ifdefined");@+break;
+case if_cs_code: print_esc("ifcsname");@+break;
+case if_font_char_code: print_esc("iffontchar");@+break;
+
+@ The result of a boolean condition is reversed when the conditional is
+preceded by \.{\\unless}.
+
+@<Negate a boolean conditional and |goto reswitch|@>=
+{@+get_token();
+if ((cur_cmd==if_test)&&(cur_chr!=if_case_code))
+ {@+cur_chr=cur_chr+unless_code;goto reswitch;
+ }
+print_err("You can't use `");print_esc("unless");print("' before `");
+@.You can't use \\unless...@>
+print_cmd_chr(cur_cmd, cur_chr);print_char('\'');
+help1("Continue, and I'll forget that it ever happened.");
+back_error();
+}
+
+@ The conditional \.{\\ifdefined} tests if a control sequence is
+defined.
+
+We need to reset |scanner_status|, since \.{\\outer} control sequences
+are allowed, but we might be scanning a macro definition or preamble.
+
+@<Cases for |conditional|@>=
+case if_def_code: {@+save_scanner_status=scanner_status;
+ scanner_status=normal;
+ get_next();b=(cur_cmd!=undefined_cs);
+ scanner_status=save_scanner_status;
+ } @+break;
+
+@ The conditional \.{\\ifcsname} is equivalent to \.{\{\\expandafter}
+\.{\}\\expandafter} \.{\\ifdefined} \.{\\csname}, except that no new
+control sequence will be entered into the hash table (once all tokens
+preceding the mandatory \.{\\endcsname} have been expanded).
+
+@<Cases for |conditional|@>=
+case if_cs_code: {@+n=get_avail();p=n; /*head of the list of characters*/
+ @/do@+{get_x_token();
+ if (cur_cs==0) store_new_token(cur_tok);
+ }@+ while (!(cur_cs!=0));
+ if (cur_cmd!=end_cs_name) @<Complain about missing \.{\\endcsname}@>;
+ @<Look up the characters of list |n| in the hash table, and set |cur_cs|@>;
+ flush_list(n);
+ b=(eq_type(cur_cs)!=undefined_cs);
+ } @+break;
+
+@ @<Look up the characters of list |n| in the hash table...@>=
+m=first;p=link(n);
+while (p!=null)
+ {@+if (m >= max_buf_stack)
+ {@+max_buf_stack=m+1;
+ if (max_buf_stack==buf_size)
+ overflow("buffer size", buf_size);
+@:TeX capacity exceeded buffer size}{\quad buffer size@>
+ }
+ buffer[m]=info(p)%0400;incr(m);p=link(p);
+ }
+if (m==first) cur_cs=null_cs; /*the list is empty*/
+else if (m > first+1)
+ cur_cs=id_lookup(first, m-first); /*|no_new_control_sequence| is |true|*/
+else cur_cs=single_base+buffer[first] /*the list has length one*/
+
+@ The conditional \.{\\iffontchar} tests the existence of a character in
+a font.
+
+@<Cases for |conditional|@>=
+case if_font_char_code: {@+scan_font_ident();n=cur_val;scan_char_num();
+ if ((font_bc[n] <= cur_val)&&(font_ec[n] >= cur_val))
+ b=char_exists(char_info(n, qi(cur_val)));
+ else b=false;
+ } @+break;
+
+@ The \.{protected} feature of \eTeX\ defines the \.{\\protected} prefix
+command for macro definitions. Such macros are protected against
+expansions when lists of expanded tokens are built, e.g., for \.{\\edef}
+or during \.{\\write}.
+
+@<Generate all \eTeX...@>=
+primitive("protected", prefix, 8);
+@!@:protected\_}{\.{\\protected} primitive@>
+
+@ @<Cases of |prefix| for |print_cmd_chr|@>=;
+else if (chr_code==8) print_esc("protected")
+
+@ The |get_x_or_protected| procedure is like |get_x_token| except that
+protected macros are not expanded.
+
+@<Declare \eTeX\ procedures for sc...@>=
+static void get_x_or_protected(void) /*sets |cur_cmd|, |cur_chr|, |cur_tok|,
+ and expands non-protected macros*/
+{@+
+loop@+{@+get_token();
+ if (cur_cmd <= max_command) return;
+ if ((cur_cmd >= call)&&(cur_cmd < end_template))
+ if (info(link(cur_chr))==protected_token) return;
+ expand();
+ }
+}
+
+@ A group entered (or a conditional started) in one file may end in a
+different file. Such slight anomalies, although perfectly legitimate,
+may cause errors that are difficult to locate. In order to be able to
+give a warning message when such anomalies occur, \eTeX\ uses the
+|grp_stack| and |if_stack| arrays to record the initial |cur_boundary|
+and |cond_ptr| values for each input file.
+
+@<Glob...@>=
+static save_pointer @!grp_stack[max_in_open+1]; /*initial |cur_boundary|*/
+static pointer @!if_stack[max_in_open+1]; /*initial |cond_ptr|*/
+
+@ When a group ends that was apparently entered in a different input
+file, the |group_warning| procedure is invoked in order to update the
+|grp_stack|. If moreover \.{\\tracingnesting} is positive we want to
+give a warning message. The situation is, however, somewhat complicated
+by two facts: (1)~There may be |grp_stack| elements without a
+corresponding \.{\\input} file or \.{\\scantokens} pseudo file (e.g.,
+error insertions from the terminal); and (2)~the relevant information is
+recorded in the |name_field| of the |input_stack| only loosely
+synchronized with the |in_open| variable indexing |grp_stack|.
+
+@<Declare \eTeX\ procedures for tr...@>=
+static void group_warning(void)
+{@+int i; /*index into |grp_stack|*/
+bool @!w; /*do we need a warning?*/
+base_ptr=input_ptr;input_stack[base_ptr]=cur_input;
+ /*store current state*/
+i=in_open;w=false;
+while ((grp_stack[i]==cur_boundary)&&(i > 0))
+ {@+@<Set variable |w| to indicate if this case should be reported@>;
+ grp_stack[i]=save_index(save_ptr);decr(i);
+ }
+if (w)
+ {@+print_nl("Warning: end of ");print_group(true);
+@.Warning: end of...@>
+ print(" of a different file");print_ln();
+ if (tracing_nesting > 1) show_context();
+ if (history==spotless) history=warning_issued;
+ }
+}
+
+@ This code scans the input stack in order to determine the type of the
+current input file.
+
+@<Set variable |w| to...@>=
+if (tracing_nesting > 0)
+ {@+while ((input_stack[base_ptr].state_field==token_list)||@|
+ (input_stack[base_ptr].index_field > i)) decr(base_ptr);
+ if (input_stack[base_ptr].name_field > 17) w=true;
+ }
+
+@ When a conditional ends that was apparently started in a different
+input file, the |if_warning| procedure is invoked in order to update the
+|if_stack|. If moreover \.{\\tracingnesting} is positive we want to
+give a warning message (with the same complications as above).
+
+@<Declare \eTeX\ procedures for tr...@>=
+static void if_warning(void)
+{@+int i; /*index into |if_stack|*/
+bool @!w; /*do we need a warning?*/
+base_ptr=input_ptr;input_stack[base_ptr]=cur_input;
+ /*store current state*/
+i=in_open;w=false;
+while (if_stack[i]==cond_ptr)
+ {@+@<Set variable |w| to...@>;
+ if_stack[i]=link(cond_ptr);decr(i);
+ }
+if (w)
+ {@+print_nl("Warning: end of ");print_cmd_chr(if_test, cur_if);
+@.Warning: end of...@>
+ print_if_line(if_line);print(" of a different file");print_ln();
+ if (tracing_nesting > 1) show_context();
+ if (history==spotless) history=warning_issued;
+ }
+}
+
+@ Conversely, the |file_warning| procedure is invoked when a file ends
+and some groups entered or conditionals started while reading from that
+file are still incomplete.
+
+@<Declare \eTeX\ procedures for tr...@>=
+static void file_warning(void)
+{@+pointer p; /*saved value of |save_ptr| or |cond_ptr|*/
+quarterword @!l; /*saved value of |cur_level| or |if_limit|*/
+quarterword @!c; /*saved value of |cur_group| or |cur_if|*/
+int @!i; /*saved value of |if_line|*/
+p=save_ptr;l=cur_level;c=cur_group;save_ptr=cur_boundary;
+while (grp_stack[in_open]!=save_ptr)
+ {@+decr(cur_level);
+ print_nl("Warning: end of file when ");
+@.Warning: end of file when...@>
+ print_group(true);print(" is incomplete");@/
+ cur_group=save_level(save_ptr);save_ptr=save_index(save_ptr);
+ }
+save_ptr=p;cur_level=l;cur_group=c; /*restore old values*/
+p=cond_ptr;l=if_limit;c=cur_if;i=if_line;
+while (if_stack[in_open]!=cond_ptr)
+ {@+print_nl("Warning: end of file when ");
+@.Warning: end of file when...@>
+ print_cmd_chr(if_test, cur_if);
+ if (if_limit==fi_code) print_esc("else");
+ print_if_line(if_line);print(" is incomplete");@/
+ if_line=if_line_field(cond_ptr);cur_if=subtype(cond_ptr);
+ if_limit=type(cond_ptr);cond_ptr=link(cond_ptr);
+ }
+cond_ptr=p;if_limit=l;cur_if=c;if_line=i; /*restore old values*/
+print_ln();
+if (tracing_nesting > 1) show_context();
+if (history==spotless) history=warning_issued;
+}
+
+@ Here are the additional \eTeX\ primitives for expressions.
+
+@<Generate all \eTeX...@>=
+primitive("numexpr", last_item, eTeX_expr-int_val+int_val);
+@!@:num\_expr\_}{\.{\\numexpr} primitive@>
+primitive("dimexpr", last_item, eTeX_expr-int_val+dimen_val);
+@!@:dim\_expr\_}{\.{\\dimexpr} primitive@>
+primitive("glueexpr", last_item, eTeX_expr-int_val+glue_val);
+@!@:glue\_expr\_}{\.{\\glueexpr} primitive@>
+primitive("muexpr", last_item, eTeX_expr-int_val+mu_val);
+@!@:mu\_expr\_}{\.{\\muexpr} primitive@>
+
+@ @<Cases of |last_item| for |print_cmd_chr|@>=
+case eTeX_expr-int_val+int_val: print_esc("numexpr");@+break;
+case eTeX_expr-int_val+dimen_val: print_esc("dimexpr");@+break;
+case eTeX_expr-int_val+glue_val: print_esc("glueexpr");@+break;
+case eTeX_expr-int_val+mu_val: print_esc("muexpr");@+break;
+
+@ This code for reducing |cur_val_level| and\slash or negating the
+result is similar to the one for all the other cases of
+|scan_something_internal|, with the difference that |scan_expr| has
+already increased the reference count of a glue specification.
+
+@<Process an expression and |return|@>=
+{@+if (m < eTeX_mu)
+ {@+switch (m) {
+ @/@<Cases for fetching a glue value@>@/
+ } /*there are no other cases*/
+ cur_val_level=glue_val;
+ }
+else if (m < eTeX_expr)
+ {@+switch (m) {
+ @/@<Cases for fetching a mu value@>@/
+ } /*there are no other cases*/
+ cur_val_level=mu_val;
+ }
+else{@+cur_val_level=m-eTeX_expr+int_val;scan_expr();
+ }
+while (cur_val_level > level)
+ {@+if (cur_val_level==glue_val)
+ {@+m=cur_val;cur_val=width(m);delete_glue_ref(m);
+ }
+ else if (cur_val_level==mu_val) mu_error();
+ decr(cur_val_level);
+ }
+if (negative)
+ if (cur_val_level >= glue_val)
+ {@+m=cur_val;cur_val=new_spec(m);delete_glue_ref(m);
+ @<Negate all three glue components of |cur_val|@>;
+ }
+ else negate(cur_val);
+return;
+}
+
+@ @<Declare \eTeX\ procedures for sc...@>=
+static void scan_expr(void);
+
+@ The |scan_expr| procedure scans and evaluates an expression.
+
+@<Declare procedures needed for expressions@>=
+@t\4@>@<Declare subprocedures for |scan_expr|@>@;
+static void scan_expr(void) /*scans and evaluates an expression*/
+{@+
+bool a, @!b; /*saved values of |arith_error|*/
+small_number @!l; /*type of expression*/
+small_number @!r; /*state of expression so far*/
+small_number @!s; /*state of term so far*/
+small_number @!o; /*next operation or type of next factor*/
+int @!e; /*expression so far*/
+int @!t; /*term so far*/
+int @!f; /*current factor*/
+int @!n; /*numerator of combined multiplication and division*/
+pointer @!p; /*top of expression stack*/
+pointer @!q; /*for stack manipulations*/
+l=cur_val_level;a=arith_error;b=false;p=null;
+@<Scan and evaluate an expression |e| of type |l|@>;
+if (b)
+ {@+print_err("Arithmetic overflow");
+@.Arithmetic overflow@>
+ help2("I can't evaluate this expression,",@/
+ "since the result is out of range.");
+ error();
+ if (l >= glue_val)
+ {@+delete_glue_ref(e);e=zero_glue;add_glue_ref(e);
+ }
+ else e=0;
+ }
+arith_error=a;cur_val=e;cur_val_level=l;
+}
+
+@ Evaluating an expression is a recursive process: When the left
+parenthesis of a subexpression is scanned we descend to the next level
+of recursion; the previous level is resumed with the matching right
+parenthesis.
+
+@d expr_none 0 /*\.( seen, or \.( $\langle\it expr\rangle$ \.) seen*/
+@d expr_add 1 /*\.( $\langle\it expr\rangle$ \.+ seen*/
+@d expr_sub 2 /*\.( $\langle\it expr\rangle$ \.- seen*/
+@d expr_mult 3 /*$\langle\it term\rangle$ \.* seen*/
+@d expr_div 4 /*$\langle\it term\rangle$ \./ seen*/
+@d expr_scale 5 /*$\langle\it term\rangle$ \.*
+ $\langle\it factor\rangle$ \./ seen*/
+
+@<Scan and eval...@>=
+restart: r=expr_none;e=0;s=expr_none;t=0;n=0;
+resume: if (s==expr_none) o=l;@+else o=int_val;
+@<Scan a factor |f| of type |o| or start a subexpression@>;
+found: @<Scan the next operator and set |o|@>;
+arith_error=b;
+@<Make sure that |f| is in the proper range@>;
+switch (s) {@<Cases for evaluation of the current term@>@;
+} /*there are no other cases*/
+if (o > expr_sub) s=o;@+else@<Evaluate the current expression@>;
+b=arith_error;
+if (o!=expr_none) goto resume;
+if (p!=null) @<Pop the expression stack and |goto found|@>@;
+
+@ @<Scan the next op...@>=
+@<Get the next non-blank non-call token@>;
+if (cur_tok==other_token+'+') o=expr_add;
+else if (cur_tok==other_token+'-') o=expr_sub;
+else if (cur_tok==other_token+'*') o=expr_mult;
+else if (cur_tok==other_token+'/') o=expr_div;
+else{@+o=expr_none;
+ if (p==null)
+ {@+if (cur_cmd!=relax) back_input();
+ }
+ else if (cur_tok!=other_token+')')
+ {@+print_err("Missing ) inserted for expression");
+@.Missing ) inserted@>
+ help1("I was expecting to see `+', `-', `*', `/', or `)'. Didn't.");
+ back_error();
+ }
+ }
+
+@ @<Scan a factor...@>=
+@<Get the next non-blank non-call token@>;
+if (cur_tok==other_token+'(')
+ @<Push the expression stack and |goto restart|@>;
+back_input();
+if (o==int_val) scan_int();
+else if (o==dimen_val) scan_normal_dimen;
+else if (o==glue_val) scan_normal_glue();
+else scan_mu_glue();
+f=cur_val
+
+@ @<Declare \eTeX\ procedures for sc...@>=
+static void scan_normal_glue(void);@/
+static void scan_mu_glue(void);
+
+@ Here we declare two trivial procedures in order to avoid mutually
+recursive procedures with parameters.
+
+@<Declare procedures needed for expressions@>=
+static void scan_normal_glue(void)
+{@+scan_glue(glue_val);
+}
+@#
+static void scan_mu_glue(void)
+{@+scan_glue(mu_val);
+}
+
+@ Parenthesized subexpressions can be inside expressions, and this
+nesting has a stack. Seven local variables represent the top of the
+expression stack: |p| points to pushed-down entries, if any; |l|
+specifies the type of expression currently beeing evaluated; |e| is the
+expression so far and |r| is the state of its evaluation; |t| is the
+term so far and |s| is the state of its evaluation; finally |n| is the
+numerator for a combined multiplication and division, if any.
+
+@d expr_node_size 4 /*number of words in stack entry for subexpressions*/
+@d expr_e_field(A) mem[A+1].i /*saved expression so far*/
+@d expr_t_field(A) mem[A+2].i /*saved term so far*/
+@d expr_n_field(A) mem[A+3].i /*saved numerator*/
+
+@<Push the expression...@>=
+{@+q=get_node(expr_node_size);link(q)=p;type(q)=l;
+subtype(q)=4*s+r;
+expr_e_field(q)=e;expr_t_field(q)=t;expr_n_field(q)=n;
+p=q;l=o;goto restart;
+}
+
+@ @<Pop the expression...@>=
+{@+f=e;q=p;
+e=expr_e_field(q);t=expr_t_field(q);n=expr_n_field(q);
+s=subtype(q)/4;r=subtype(q)%4;
+l=type(q);p=link(q);free_node(q, expr_node_size);
+goto found;
+}
+
+@ We want to make sure that each term and (intermediate) result is in
+the proper range. Integer values must not exceed |infinity|
+($2^{31}-1$) in absolute value, dimensions must not exceed |max_dimen|
+($2^{30}-1$). We avoid the absolute value of an integer, because this
+might fail for the value $-2^{31}$ using 32-bit arithmetic.
+
+@d num_error(A) /*clear a number or dimension and set |arith_error|*/
+ {@+arith_error=true;A=0;
+ }
+@d glue_error(A) /*clear a glue spec and set |arith_error|*/
+ {@+arith_error=true;delete_glue_ref(A);A=new_spec(zero_glue);
+ }
+
+@<Make sure that |f|...@>=
+if ((l==int_val)||(s > expr_sub))
+ {@+if ((f > infinity)||(f < -infinity)) num_error(f);
+ }
+else if (l==dimen_val)
+ {@+if (abs(f) > max_dimen) num_error(f);
+ }
+else{@+if ((abs(width(f)) > max_dimen)||@|
+ (abs(stretch(f)) > max_dimen)||@|
+ (abs(shrink(f)) > max_dimen)) glue_error(f);
+ }
+
+@ Applying the factor |f| to the partial term |t| (with the operator
+|s|) is delayed until the next operator |o| has been scanned. Here we
+handle the first factor of a partial term. A glue spec has to be copied
+unless the next operator is a right parenthesis; this allows us later on
+to simply modify the glue components.
+
+@d normalize_glue(A)
+ if (stretch(A)==0) stretch_order(A)=normal;
+ if (shrink(A)==0) shrink_order(A)=normal
+
+@<Cases for evaluation of the current term@>=
+case expr_none: if ((l >= glue_val)&&(o!=expr_none))
+ {@+t=new_spec(f);delete_glue_ref(f);normalize_glue(t);
+ }
+ else t=f;@+break;
+
+@ When a term |t| has been completed it is copied to, added to, or
+subtracted from the expression |e|.
+
+@d expr_add_sub(A, B, C) add_or_sub(A, B, C, r==expr_sub)
+@d expr_a(A, B) expr_add_sub(A, B, max_dimen)
+
+@<Evaluate the current expression@>=
+{@+s=expr_none;
+if (r==expr_none) e=t;
+else if (l==int_val) e=expr_add_sub(e, t, infinity);
+else if (l==dimen_val) e=expr_a(e, t);
+else@<Compute the sum or difference of two glue specs@>;
+r=o;
+}
+
+@ The function |add_or_sub(x, y, max_answer, negative)| computes the sum
+(for |negative==false|) or difference (for |negative==true|) of |x| and
+|y|, provided the absolute value of the result does not exceed
+|max_answer|.
+
+@<Declare subprocedures for |scan_expr|@>=
+static int add_or_sub(int @!x, int @!y, int @!max_answer, bool @!negative)
+{@+int a; /*the answer*/
+if (negative) negate(y);
+if (x >= 0)
+ if (y <= max_answer-x) a=x+y;@+else num_error(a)@;
+else if (y >= -max_answer-x) a=x+y;@+else num_error(a);
+return a;
+}
+
+@ We know that |stretch_order(e) > normal| implies |stretch(e)!=0| and
+|shrink_order(e) > normal| implies |shrink(e)!=0|.
+
+@<Compute the sum or diff...@>=
+{@+width(e)=expr_a(width(e), width(t));
+if (stretch_order(e)==stretch_order(t))
+ stretch(e)=expr_a(stretch(e), stretch(t));
+else if ((stretch_order(e) < stretch_order(t))&&(stretch(t)!=0))
+ {@+stretch(e)=stretch(t);stretch_order(e)=stretch_order(t);
+ }
+if (shrink_order(e)==shrink_order(t))
+ shrink(e)=expr_a(shrink(e), shrink(t));
+else if ((shrink_order(e) < shrink_order(t))&&(shrink(t)!=0))
+ {@+shrink(e)=shrink(t);shrink_order(e)=shrink_order(t);
+ }
+delete_glue_ref(t);normalize_glue(e);
+}
+
+@ If a multiplication is followed by a division, the two operations are
+combined into a `scaling' operation. Otherwise the term |t| is
+multiplied by the factor |f|.
+
+@d expr_m(A) A=nx_plus_y(A, f, 0)
+
+@<Cases for evaluation of the current term@>=
+case expr_mult: if (o==expr_div)
+ {@+n=f;o=expr_scale;
+ }
+ else if (l==int_val) t=mult_integers(t, f);
+ else if (l==dimen_val) expr_m(t);
+ else{@+expr_m(width(t));expr_m(stretch(t));expr_m(shrink(t));
+ } @+break;
+
+@ Here we divide the term |t| by the factor |f|.
+
+@d expr_d(A) A=quotient(A, f)
+
+@<Cases for evaluation of the current term@>=
+case expr_div: if (l < glue_val) expr_d(t);
+ else{@+expr_d(width(t));expr_d(stretch(t));expr_d(shrink(t));
+ } @+break;
+
+@ The function |quotient(n, d)| computes the rounded quotient
+$q=\lfloor n/d+{1\over2}\rfloor$, when $n$ and $d$ are positive.
+
+@<Declare subprocedures for |scan_expr|@>=
+static int quotient(int @!n, int @!d)
+{@+bool negative; /*should the answer be negated?*/
+int @!a; /*the answer*/
+if (d==0) num_error(a)@;
+else{@+if (d > 0) negative=false;
+ else{@+negate(d);negative=true;
+ }
+ if (n < 0)
+ {@+negate(n);negative=!negative;
+ }
+ a=n/d;n=n-a*d;d=n-d; /*avoid certain compiler optimizations!*/
+ if (d+n >= 0) incr(a);
+ if (negative) negate(a);
+ }
+return a;
+}
+
+@ Here the term |t| is multiplied by the quotient $n/f$.
+
+@d expr_s(A) A=fract(A, n, f, max_dimen)
+
+@<Cases for evaluation of the current term@>=
+case expr_scale: if (l==int_val) t=fract(t, n, f, infinity);
+ else if (l==dimen_val) expr_s(t);
+ else{@+expr_s(width(t));expr_s(stretch(t));expr_s(shrink(t));
+ }
+
+@ Finally, the function |fract(x, n, d, max_answer)| computes the integer
+$q=\lfloor xn/d+{1\over2}\rfloor$, when $x$, $n$, and $d$ are positive
+and the result does not exceed |max_answer|. We can't use floating
+point arithmetic since the routine must produce identical results in all
+cases; and it would be too dangerous to multiply by~|n| and then divide
+by~|d|, in separate operations, since overflow might well occur. Hence
+this subroutine simulates double precision arithmetic, somewhat
+analogous to \MF's |make_fraction| and |take_fraction| routines.
+
+@<Declare subprocedures for |scan_expr|@>=
+static int fract(int @!x, int @!n, int @!d, int @!max_answer)
+{@+
+bool negative; /*should the answer be negated?*/
+int @!a; /*the answer*/
+int @!f; /*a proper fraction*/
+int @!h; /*smallest integer such that |2*h >= d|*/
+int @!r; /*intermediate remainder*/
+int @!t; /*temp variable*/
+if (d==0) goto too_big;
+a=0;
+if (d > 0) negative=false;
+else{@+negate(d);negative=true;
+ }
+if (x < 0)
+ {@+negate(x);negative=!negative;
+ }
+else if (x==0) goto done;
+if (n < 0)
+ {@+negate(n);negative=!negative;
+ }
+t=n/d;
+if (t > max_answer/x) goto too_big;
+a=t*x;n=n-t*d;
+if (n==0) goto found;
+t=x/d;
+if (t > (max_answer-a)/n) goto too_big;
+a=a+t*n;x=x-t*d;
+if (x==0) goto found;
+if (x < n)
+ {@+t=x;x=n;n=t;
+ } /*now |0 < n <= x < d|*/
+@<Compute \(f)$f=\lfloor xn/d+{1\over2}\rfloor$@>@;
+if (f > (max_answer-a)) goto too_big;
+a=a+f;
+found: if (negative) negate(a);
+goto done;
+too_big: num_error(a);
+done: return a;
+}
+
+@ The loop here preserves the following invariant relations
+between |f|, |x|, |n|, and~|r|:
+(i)~$f+\lfloor(xn+(r+d))/d\rfloor=\lfloor x_0n_0/d+{1\over2}\rfloor$;
+(ii)~|-d <= r < 0 < n <= x < d|, where $x_0$, $n_0$ are the original values of~$x$
+and $n$.
+
+Notice that the computation specifies |(x-d)+x| instead of |(x+x)-d|,
+because the latter could overflow.
+
+@<Compute \(f)$f=\lfloor xn/d+{1\over2}\rfloor$@>=
+f=0;r=(d/2)-d;h=-r;
+loop@+{@+if (odd(n))
+ {@+r=r+x;
+ if (r >= 0)
+ {@+r=r-d;incr(f);
+ }
+ }
+ n=n/2;
+ if (n==0) goto found1;
+ if (x < h) x=x+x;
+ else{@+t=x-d;x=t+x;f=f+n;
+ if (x < n)
+ {@+if (x==0) goto found1;
+ t=x;x=n;n=t;
+ }
+ }
+ }
+found1:
+
+@ The \.{\\gluestretch}, \.{\\glueshrink}, \.{\\gluestretchorder}, and
+\.{\\glueshrinkorder} commands return the stretch and shrink components
+and their orders of ``infinity'' of a glue specification.
+
+@d glue_stretch_order_code (eTeX_int+6) /*code for \.{\\gluestretchorder}*/
+@d glue_shrink_order_code (eTeX_int+7) /*code for \.{\\glueshrinkorder}*/
+@d glue_stretch_code (eTeX_dim+7) /*code for \.{\\gluestretch}*/
+@d glue_shrink_code (eTeX_dim+8) /*code for \.{\\glueshrink}*/
+
+@<Generate all \eTeX...@>=
+primitive("gluestretchorder", last_item, glue_stretch_order_code);
+@!@:glue\_stretch\_order\_}{\.{\\gluestretchorder} primitive@>
+primitive("glueshrinkorder", last_item, glue_shrink_order_code);
+@!@:glue\_shrink\_order\_}{\.{\\glueshrinkorder} primitive@>
+primitive("gluestretch", last_item, glue_stretch_code);
+@!@:glue\_stretch\_}{\.{\\gluestretch} primitive@>
+primitive("glueshrink", last_item, glue_shrink_code);
+@!@:glue\_shrink\_}{\.{\\glueshrink} primitive@>
+
+@ @<Cases of |last_item| for |print_cmd_chr|@>=
+case glue_stretch_order_code: print_esc("gluestretchorder");@+break;
+case glue_shrink_order_code: print_esc("glueshrinkorder");@+break;
+case glue_stretch_code: print_esc("gluestretch");@+break;
+case glue_shrink_code: print_esc("glueshrink");@+break;
+
+@ @<Cases for fetching an integer value@>=
+case glue_stretch_order_code: case glue_shrink_order_code:
+ {@+scan_normal_glue();q=cur_val;
+ if (m==glue_stretch_order_code) cur_val=stretch_order(q);
+ else cur_val=shrink_order(q);
+ delete_glue_ref(q);
+ }
+
+@ @<Cases for fetching a dimension value@>=
+case glue_stretch_code: case glue_shrink_code:
+ {@+scan_normal_glue();q=cur_val;
+ if (m==glue_stretch_code) cur_val=stretch(q);
+ else cur_val=shrink(q);
+ delete_glue_ref(q);
+ }
+
+@ The \.{\\mutoglue} and \.{\\gluetomu} commands convert ``math'' glue
+into normal glue and vice versa; they allow to manipulate math glue with
+\.{\\gluestretch} etc.
+
+@d mu_to_glue_code eTeX_glue /*code for \.{\\mutoglue}*/
+@d glue_to_mu_code eTeX_mu /*code for \.{\\gluetomu}*/
+
+@<Generate all \eTeX...@>=
+primitive("mutoglue", last_item, mu_to_glue_code);
+@!@:mu\_to\_glue\_}{\.{\\mutoglue} primitive@>
+primitive("gluetomu", last_item, glue_to_mu_code);
+@!@:glue\_to\_mu\_}{\.{\\gluetomu} primitive@>
+
+@ @<Cases of |last_item| for |print_cmd_chr|@>=
+case mu_to_glue_code: print_esc("mutoglue");@+break;
+case glue_to_mu_code: print_esc("gluetomu");@+break;
+
+@ @<Cases for fetching a glue value@>=
+case mu_to_glue_code: scan_mu_glue();
+
+@ @<Cases for fetching a mu value@>=
+case glue_to_mu_code: scan_normal_glue();
+
+@ \eTeX\ (in extended mode) supports 32768 (i.e., $2^{15}$) count,
+dimen, skip, muskip, box, and token registers. As in \TeX\ the first
+256 registers of each kind are realized as arrays in the table of
+equivalents; the additional registers are realized as tree structures
+built from variable-size nodes with individual registers existing only
+when needed. Default values are used for nonexistent registers: zero
+for count and dimen values, |zero_glue| for glue (skip and muskip)
+values, void for boxes, and |null| for token lists (and current marks
+discussed below).
+
+Similarly there are 32768 mark classes; the command \.{\\marks}|n|
+creates a mark node for a given mark class |0 <= n <= 32767| (where
+\.{\\marks0} is synonymous to \.{\\mark}). The page builder (actually
+the |fire_up| routine) and the |vsplit| routine maintain the current
+values of |top_mark|, |first_mark|, |bot_mark|, |split_first_mark|, and
+|split_bot_mark| for each mark class. They are accessed as
+\.{\\topmarks}|n| etc., and \.{\\topmarks0} is again synonymous to
+\.{\\topmark}. As in \TeX\ the five current marks for mark class zero
+are realized as |cur_mark| array. The additional current marks are
+again realized as tree structure with individual mark classes existing
+only when needed.
+
+@<Generate all \eTeX...@>=
+primitive("marks", mark, marks_code);
+@!@:marks\_}{\.{\\marks} primitive@>
+primitive("topmarks", top_bot_mark, top_mark_code+marks_code);
+@!@:top\_marks\_}{\.{\\topmarks} primitive@>
+primitive("firstmarks", top_bot_mark, first_mark_code+marks_code);
+@!@:first\_marks\_}{\.{\\firstmarks} primitive@>
+primitive("botmarks", top_bot_mark, bot_mark_code+marks_code);
+@!@:bot\_marks\_}{\.{\\botmarks} primitive@>
+primitive("splitfirstmarks", top_bot_mark, split_first_mark_code+marks_code);
+@!@:split\_first\_marks\_}{\.{\\splitfirstmarks} primitive@>
+primitive("splitbotmarks", top_bot_mark, split_bot_mark_code+marks_code);
+@!@:split\_bot\_marks\_}{\.{\\splitbotmarks} primitive@>
+
+@ The |scan_register_num| procedure scans a register number that must
+not exceed 255 in compatibility mode resp.\ 32767 in extended mode.
+
+@<Declare \eTeX\ procedures for ex...@>=
+static void scan_register_num(void);
+
+@ @<Declare procedures that scan restricted classes of integers@>=
+static void scan_register_num(void)
+{@+scan_int();
+if ((cur_val < 0)||(cur_val > max_reg_num))
+ {@+print_err("Bad register code");
+@.Bad register code@>
+ help2(max_reg_help_line,"I changed this one to zero.");
+ int_error(cur_val);cur_val=0;
+ }
+}
+
+@ @<Initialize variables for \eTeX\ comp...@>=
+max_reg_num=255;
+max_reg_help_line="A register number must be between 0 and 255.";
+
+@ @<Initialize variables for \eTeX\ ext...@>=
+max_reg_num=32767;
+max_reg_help_line="A register number must be between 0 and 32767.";
+
+@ @<Glob...@>=
+static halfword @!max_reg_num; /*largest allowed register number*/
+static char *@!max_reg_help_line; /*first line of help message*/
+
+@ There are seven almost identical doubly linked trees, one for the
+sparse array of the up to 32512 additional registers of each kind and
+one for the sparse array of the up to 32767 additional mark classes.
+The root of each such tree, if it exists, is an index node containing 16
+pointers to subtrees for 4096 consecutive array elements. Similar index
+nodes are the starting points for all nonempty subtrees for 4096, 256,
+and 16 consecutive array elements. These four levels of index nodes are
+followed by a fifth level with nodes for the individual array elements.
+
+Each index node is nine words long. The pointers to the 16 possible
+subtrees or are kept in the |info| and |link| fields of the last eight
+words. (It would be both elegant and efficient to declare them as
+array, unfortunately \PASCAL\ doesn't allow this.)
+
+The fields in the first word of each index node and in the nodes for the
+array elements are closely related. The |link| field points to the next
+lower index node and the |sa_index| field contains four bits (one
+hexadecimal digit) of the register number or mark class. For the lowest
+index node the |link| field is |null| and the |sa_index| field indicates
+the type of quantity (|int_val|, |dimen_val|, |glue_val|, |mu_val|,
+|box_val|, |tok_val|, or |mark_val|). The |sa_used| field in the index
+nodes counts how many of the 16 pointers are non-null.
+
+The |sa_index| field in the nodes for array elements contains the four
+bits plus 16 times the type. Therefore such a node represents a count
+or dimen register if and only if |sa_index < dimen_val_limit|; it
+represents a skip or muskip register if and only if
+|dimen_val_limit <= sa_index < mu_val_limit|; it represents a box register
+if and only if |mu_val_limit <= sa_index < box_val_limit|; it represents a
+token list register if and only if
+|box_val_limit <= sa_index < tok_val_limit|; finally it represents a mark
+class if and only if |tok_val_limit <= sa_index|.
+
+The |new_index| procedure creates an index node (returned in |cur_ptr|)
+having given contents of the |sa_index| and |link| fields.
+
+@d box_val 4 /*the additional box registers*/
+@d mark_val 6 /*the additional mark classes*/
+@#
+@d dimen_val_limit 0x20 /*$2^4\cdot(|dimen_val|+1)$*/
+@d mu_val_limit 0x40 /*$2^4\cdot(|mu_val|+1)$*/
+@d box_val_limit 0x50 /*$2^4\cdot(|box_val|+1)$*/
+@d tok_val_limit 0x60 /*$2^4\cdot(|tok_val|+1)$*/
+@#
+@d index_node_size 9 /*size of an index node*/
+@d sa_index(A) type(A) /*a four-bit address or a type or both*/
+@d sa_used(A) subtype(A) /*count of non-null pointers*/
+
+@<Declare \eTeX\ procedures for ex...@>=
+static void new_index(quarterword @!i, pointer @!q)
+{@+int k; /*loop index*/
+cur_ptr=get_node(index_node_size);sa_index(cur_ptr)=i;
+sa_used(cur_ptr)=0;link(cur_ptr)=q;
+for (k=1; k<=index_node_size-1; k++) /*clear all 16 pointers*/
+ mem[cur_ptr+k]=sa_null;
+}
+
+@ The roots of the seven trees for the additional registers and mark
+classes are kept in the |sa_root| array. The first six locations must
+be dumped and undumped; the last one is also known as |sa_mark|.
+
+@d sa_mark sa_root[mark_val] /*root for mark classes*/
+
+@<Glob...@>=
+static pointer @!sa_root0[mark_val-int_val+1],
+ *const @!sa_root = @!sa_root0-int_val; /*roots of sparse arrays*/
+static pointer @!cur_ptr; /*value returned by |new_index| and |find_sa_element|*/
+static memory_word @!sa_null; /*two |null| pointers*/
+
+@ @<Set init...@>=
+sa_mark=null;sa_null.hh.lh=null;sa_null.hh.rh=null;
+
+@ @<Initialize table...@>=
+for (i=int_val; i<=tok_val; i++) sa_root[i]=null;
+
+@ Given a type |t| and a sixteen-bit number |n|, the |find_sa_element|
+procedure returns (in |cur_ptr|) a pointer to the node for the
+corresponding array element, or |null| when no such element exists. The
+third parameter |w| is set |true| if the element must exist, e.g.,
+because it is about to be modified. The procedure has two main
+branches: one follows the existing tree structure, the other (only used
+when |w| is |true|) creates the missing nodes.
+
+We use macros to extract the four-bit pieces from a sixteen-bit register
+number or mark class and to fetch or store one of the 16 pointers from
+an index node.
+
+@d if_cur_ptr_is_null_then_return_or_goto(A) /*some tree element is missing*/
+ {@+if (cur_ptr==null)
+ if (w) goto A;@+else return;
+ }
+@#
+@d hex_dig1(A) A/4096 /*the fourth lowest hexadecimal digit*/
+@d hex_dig2(A) (A/256)%16 /*the third lowest hexadecimal digit*/
+@d hex_dig3(A) (A/16)%16 /*the second lowest hexadecimal digit*/
+@d hex_dig4(A) A%16 /*the lowest hexadecimal digit*/
+@#
+@d get_sa_ptr if (odd(i)) cur_ptr=link(q+(i/2)+1);
+ else cur_ptr=info(q+(i/2)+1)
+ /*set |cur_ptr| to the pointer indexed by |i| from index node |q|*/
+@d put_sa_ptr(A) if (odd(i)) link(q+(i/2)+1)=A;
+ else info(q+(i/2)+1)=A
+ /*store the pointer indexed by |i| in index node |q|*/
+@d add_sa_ptr {@+put_sa_ptr(cur_ptr);incr(sa_used(q));
+ } /*add |cur_ptr| as the pointer indexed by |i| in index node |q|*/
+@d delete_sa_ptr {@+put_sa_ptr(null);decr(sa_used(q));
+ } /*delete the pointer indexed by |i| in index node |q|*/
+
+@<Declare \eTeX\ procedures for ex...@>=
+static void find_sa_element(small_number @!t, halfword @!n, bool @!w)
+ /*sets |cur_val| to sparse array element location or |null|*/
+{@+
+pointer q; /*for list manipulations*/
+small_number @!i; /*a four bit index*/
+cur_ptr=sa_root[t];
+if_cur_ptr_is_null_then_return_or_goto(not_found);@/
+q=cur_ptr;i=hex_dig1(n);get_sa_ptr;
+if_cur_ptr_is_null_then_return_or_goto(not_found1);@/
+q=cur_ptr;i=hex_dig2(n);get_sa_ptr;
+if_cur_ptr_is_null_then_return_or_goto(not_found2);@/
+q=cur_ptr;i=hex_dig3(n);get_sa_ptr;
+if_cur_ptr_is_null_then_return_or_goto(not_found3);@/
+q=cur_ptr;i=hex_dig4(n);get_sa_ptr;
+if ((cur_ptr==null)&&w) goto not_found4;
+return;
+not_found: new_index(t, null); /*create first level index node*/
+sa_root[t]=cur_ptr;q=cur_ptr;i=hex_dig1(n);
+not_found1: new_index(i, q); /*create second level index node*/
+add_sa_ptr;q=cur_ptr;i=hex_dig2(n);
+not_found2: new_index(i, q); /*create third level index node*/
+add_sa_ptr;q=cur_ptr;i=hex_dig3(n);
+not_found3: new_index(i, q); /*create fourth level index node*/
+add_sa_ptr;q=cur_ptr;i=hex_dig4(n);
+not_found4: @<Create a new array element of type |t| with index |i|@>;
+link(cur_ptr)=q;add_sa_ptr;
+}
+
+@ The array elements for registers are subject to grouping and have an
+|sa_lev| field (quite analogous to |eq_level|) instead of |sa_used|.
+Since saved values as well as shorthand definitions (created by e.g.,
+\.{\\countdef}) refer to the location of the respective array element,
+we need a reference count that is kept in the |sa_ref| field. An array
+element can be deleted (together with all references to it) when its
+|sa_ref| value is |null| and its value is the default value.
+@^reference counts@>
+
+Skip, muskip, box, and token registers use two word nodes, their values
+are stored in the |sa_ptr| field.
+Count and dimen registers use three word nodes, their
+values are stored in the |sa_int| resp.\ |sa_dim| field in the third
+word; the |sa_ptr| field is used under the name |sa_num| to store
+the register number. Mark classes use four word nodes. The last three
+words contain the five types of current marks
+
+@d sa_lev sa_used /*grouping level for the current value*/
+@d pointer_node_size 2 /*size of an element with a pointer value*/
+@d sa_type(A) (sa_index(A)/16) /*type part of combined type/index*/
+@d sa_ref(A) info(A+1) /*reference count of a sparse array element*/
+@d sa_ptr(A) link(A+1) /*a pointer value*/
+@#
+@d word_node_size 3 /*size of an element with a word value*/
+@d sa_num(A) sa_ptr(A) /*the register number*/
+@d sa_int(A) mem[A+2].i /*an integer*/
+@d sa_dim(A) mem[A+2].sc /*a dimension (a somewhat esotheric distinction)*/
+@#
+@d mark_class_node_size 4 /*size of an element for a mark class*/
+@#
+@d fetch_box(A) /*fetch |box(cur_val)|*/
+ if (cur_val < 256) A=box(cur_val);
+ else{@+find_sa_element(box_val, cur_val, false);
+ if (cur_ptr==null) A=null;@+else A=sa_ptr(cur_ptr);
+ }
+
+@<Create a new array element...@>=
+if (t==mark_val) /*a mark class*/
+ {@+cur_ptr=get_node(mark_class_node_size);
+ mem[cur_ptr+1]=sa_null;mem[cur_ptr+2]=sa_null;mem[cur_ptr+3]=sa_null;
+ }
+else{@+if (t <= dimen_val) /*a count or dimen register*/
+ {@+cur_ptr=get_node(word_node_size);sa_int(cur_ptr)=0;
+ sa_num(cur_ptr)=n;
+ }
+ else{@+cur_ptr=get_node(pointer_node_size);
+ if (t <= mu_val) /*a skip or muskip register*/
+ {@+sa_ptr(cur_ptr)=zero_glue;add_glue_ref(zero_glue);
+ }
+ else sa_ptr(cur_ptr)=null; /*a box or token list register*/
+ }
+ sa_ref(cur_ptr)=null; /*all registers have a reference count*/
+ }
+sa_index(cur_ptr)=16*t+i;sa_lev(cur_ptr)=level_one
+
+@ The |delete_sa_ref| procedure is called when a pointer to an array
+element representing a register is being removed; this means that the
+reference count should be decreased by one. If the reduced reference
+count is |null| and the register has been (globally) assigned its
+default value the array element should disappear, possibly together with
+some index nodes. This procedure will never be used for mark class
+nodes.
+@^reference counts@>
+
+@d add_sa_ref(A) incr(sa_ref(A)) /*increase reference count*/
+@#
+@d change_box(A) /*change |box(cur_val)|, the |eq_level| stays the same*/
+ if (cur_val < 256) box(cur_val)=A;@+else set_sa_box(A)
+@#
+@d set_sa_box(X) {@+find_sa_element(box_val, cur_val, false);
+ if (cur_ptr!=null)
+ {@+sa_ptr(cur_ptr)=X;add_sa_ref(cur_ptr);delete_sa_ref(cur_ptr);
+ }
+ }
+
+@<Declare \eTeX\ procedures for tr...@>=
+static void delete_sa_ref(pointer @!q) /*reduce reference count*/
+{@+
+pointer p; /*for list manipulations*/
+small_number @!i; /*a four bit index*/
+small_number @!s; /*size of a node*/
+decr(sa_ref(q));
+if (sa_ref(q)!=null) return;
+if (sa_index(q) < dimen_val_limit)
+ if (sa_int(q)==0) s=word_node_size;
+ else return;
+else{@+if (sa_index(q) < mu_val_limit)
+ if (sa_ptr(q)==zero_glue) delete_glue_ref(zero_glue);
+ else return;
+ else if (sa_ptr(q)!=null) return;
+ s=pointer_node_size;
+ }
+@/do@+{i=hex_dig4(sa_index(q));p=q;q=link(p);free_node(p, s);
+if (q==null) /*the whole tree has been freed*/
+ {@+sa_root[i]=null;return;
+ }
+delete_sa_ptr;s=index_node_size; /*node |q| is an index node*/
+}@+ while (!(sa_used(q) > 0));
+}
+
+@ The |print_sa_num| procedure prints the register number corresponding
+to an array element.
+
+@<Basic print...@>=
+static void print_sa_num(pointer @!q) /*print register number*/
+{@+halfword @!n; /*the register number*/
+if (sa_index(q) < dimen_val_limit) n=sa_num(q); /*the easy case*/
+else{@+n=hex_dig4(sa_index(q));q=link(q);n=n+16*sa_index(q);
+ q=link(q);n=n+256*(sa_index(q)+16*sa_index(link(q)));
+ }
+print_int(n);
+}
+
+@ Here is a procedure that displays the contents of an array element
+symbolically. It is used under similar circumstances as is
+|restore_trace| (together with |show_eqtb|) for the quantities kept in
+the |eqtb| array.
+
+@<Declare \eTeX\ procedures for tr...@>=
+#ifdef @!STAT
+static void show_sa(pointer @!p, char *@!s)
+{@+small_number t; /*the type of element*/
+begin_diagnostic();print_char('{');print(s);print_char(' ');
+if (p==null) print_char('?'); /*this can't happen*/
+else{@+t=sa_type(p);
+ if (t < box_val) print_cmd_chr(internal_register, p);
+ else if (t==box_val)
+ {@+print_esc("box");print_sa_num(p);
+ }
+ else if (t==tok_val) print_cmd_chr(toks_register, p);
+ else print_char('?'); /*this can't happen either*/
+ print_char('=');
+ if (t==int_val) print_int(sa_int(p));
+ else if (t==dimen_val)
+ {@+print_scaled(sa_dim(p));print("pt");
+ }
+ else{@+p=sa_ptr(p);
+ if (t==glue_val) print_spec(p,"pt");
+ else if (t==mu_val) print_spec(p,"mu");
+ else if (t==box_val)
+ if (p==null) print("void");
+ else{@+depth_threshold=0;breadth_max=1;show_node_list(p);
+ }
+ else if (t==tok_val)
+ {@+if (p!=null) show_token_list(link(p), null, 32);
+ }
+ else print_char('?'); /*this can't happen either*/
+ }
+ }
+print_char('}');end_diagnostic(false);
+}
+#endif
+
+@ Here we compute the pointer to the current mark of type |t| and mark
+class |cur_val|.
+
+@<Compute the mark pointer...@>=
+{@+find_sa_element(mark_val, cur_val, false);
+if (cur_ptr!=null)
+ if (odd(t)) cur_ptr=link(cur_ptr+(t/2)+1);
+ else cur_ptr=info(cur_ptr+(t/2)+1);
+}
+
+@ The current marks for all mark classes are maintained by the |vsplit|
+and |fire_up| routines and are finally destroyed (for \.{INITEX} only)
+@.INITEX@>
+by the |final_cleanup| routine. Apart from updating the current marks
+when mark nodes are encountered, these routines perform certain actions
+on all existing mark classes. The recursive |do_marks| procedure walks
+through the whole tree or a subtree of existing mark class nodes and
+preforms certain actions indicted by its first parameter |a|, the action
+code. The second parameter |l| indicates the level of recursion (at
+most four); the third parameter points to a nonempty tree or subtree.
+The result is |true| if the complete tree or subtree has been deleted.
+
+@d vsplit_init 0 /*action code for |vsplit| initialization*/
+@d fire_up_init 1 /*action code for |fire_up| initialization*/
+@d fire_up_done 2 /*action code for |fire_up| completion*/
+@d destroy_marks 3 /*action code for |final_cleanup|*/
+@#
+@d sa_top_mark(A) info(A+1) /*\.{\\topmarks}|n|*/
+@d sa_first_mark(A) link(A+1) /*\.{\\firstmarks}|n|*/
+@d sa_bot_mark(A) info(A+2) /*\.{\\botmarks}|n|*/
+@d sa_split_first_mark(A) link(A+2) /*\.{\\splitfirstmarks}|n|*/
+@d sa_split_bot_mark(A) info(A+3) /*\.{\\splitbotmarks}|n|*/
+
+@<Declare the function called |do_marks|@>=
+static bool do_marks(small_number @!a, small_number @!l, pointer @!q)
+{@+int i; /*a four bit index*/
+if (l < 4) /*|q| is an index node*/
+ {@+for (i=0; i<=15; i++)
+ {@+get_sa_ptr;
+ if (cur_ptr!=null) if (do_marks(a, l+1, cur_ptr)) delete_sa_ptr;
+ }
+ if (sa_used(q)==0)
+ {@+free_node(q, index_node_size);q=null;
+ }
+ }
+else /*|q| is the node for a mark class*/
+ {@+switch (a) {
+ @<Cases for |do_marks|@>@;
+ } /*there are no other cases*/
+ if (sa_bot_mark(q)==null) if (sa_split_bot_mark(q)==null)
+ {@+free_node(q, mark_class_node_size);q=null;
+ }
+ }
+return(q==null);
+}
+
+@ At the start of the |vsplit| routine the existing |split_fist_mark|
+and |split_bot_mark| are discarded.
+
+@<Cases for |do_marks|@>=
+case vsplit_init: if (sa_split_first_mark(q)!=null)
+ {@+delete_token_ref(sa_split_first_mark(q));sa_split_first_mark(q)=null;
+ delete_token_ref(sa_split_bot_mark(q));sa_split_bot_mark(q)=null;
+ } @+break;
+
+@ We use again the fact that |split_first_mark==null| if and only if
+|split_bot_mark==null|.
+
+@<Update the current marks for |vsplit|@>=
+{@+find_sa_element(mark_val, mark_class(p), true);
+if (sa_split_first_mark(cur_ptr)==null)
+ {@+sa_split_first_mark(cur_ptr)=mark_ptr(p);
+ add_token_ref(mark_ptr(p));
+ }
+else delete_token_ref(sa_split_bot_mark(cur_ptr));
+sa_split_bot_mark(cur_ptr)=mark_ptr(p);
+add_token_ref(mark_ptr(p));
+}
+
+@ At the start of the |fire_up| routine the old |top_mark| and
+|first_mark| are discarded, whereas the old |bot_mark| becomes the new
+|top_mark|. An empty new |top_mark| token list is, however, discarded
+as well in order that mark class nodes can eventually be released. We
+use again the fact that |bot_mark!=null| implies |first_mark!=null|; it
+also knows that |bot_mark==null| implies |top_mark==first_mark==null|.
+
+@<Cases for |do_marks|@>=
+case fire_up_init: if (sa_bot_mark(q)!=null)
+ {@+if (sa_top_mark(q)!=null) delete_token_ref(sa_top_mark(q));
+ delete_token_ref(sa_first_mark(q));sa_first_mark(q)=null;
+ if (link(sa_bot_mark(q))==null) /*an empty token list*/
+ {@+delete_token_ref(sa_bot_mark(q));sa_bot_mark(q)=null;
+ }
+ else add_token_ref(sa_bot_mark(q));
+ sa_top_mark(q)=sa_bot_mark(q);
+ } @+break;
+
+@ @<Cases for |do_marks|@>=
+case fire_up_done: if ((sa_top_mark(q)!=null)&&(sa_first_mark(q)==null))
+ {@+sa_first_mark(q)=sa_top_mark(q);add_token_ref(sa_top_mark(q));
+ } @+break;
+
+@ @<Update the current marks for |fire_up|@>=
+{@+find_sa_element(mark_val, mark_class(p), true);
+if (sa_first_mark(cur_ptr)==null)
+ {@+sa_first_mark(cur_ptr)=mark_ptr(p);
+ add_token_ref(mark_ptr(p));
+ }
+if (sa_bot_mark(cur_ptr)!=null) delete_token_ref(sa_bot_mark(cur_ptr));
+sa_bot_mark(cur_ptr)=mark_ptr(p);add_token_ref(mark_ptr(p));
+}
+
+@ Here we use the fact that the five current mark pointers in a mark
+class node occupy the same locations as the the first five pointers of
+an index node. For systems using a run-time switch to distinguish
+between \.{VIRTEX} and \.{INITEX}, the codewords `$|@t\#\&{ifdef} \.{INIT}@>|\ldots|@t\#\&{endif}@>|$'
+surrounding the following piece of code should be removed.
+@.INITEX@>
+@^system dependencies@>
+
+@<Cases for |do_marks|@>=
+#ifdef @!INIT
+case destroy_marks: for (i=top_mark_code; i<=split_bot_mark_code; i++)
+ {@+get_sa_ptr;
+ if (cur_ptr!=null)
+ {@+delete_token_ref(cur_ptr);put_sa_ptr(null);
+ }
+ }
+#endif
+
+@ The command code |internal_register| is used for `\.{\\count}', `\.{\\dimen}',
+etc., as well as for references to sparse array elements defined by
+`\.{\\countdef}', etc.
+
+@<Cases of |register| for |print_cmd_chr|@>=
+{@+if ((chr_code < mem_bot)||(chr_code > lo_mem_stat_max))
+ cmd=sa_type(chr_code);
+else{@+cmd=chr_code-mem_bot;chr_code=null;
+ }
+if (cmd==int_val) print_esc("count");
+else if (cmd==dimen_val) print_esc("dimen");
+else if (cmd==glue_val) print_esc("skip");
+else print_esc("muskip");
+if (chr_code!=null) print_sa_num(chr_code);
+}
+
+@ Similarly the command code |toks_register| is used for `\.{\\toks}' as
+well as for references to sparse array elements defined by
+`\.{\\toksdef}'.
+
+@<Cases of |toks_register| for |print_cmd_chr|@>=
+{@+print_esc("toks");
+if (chr_code!=mem_bot) print_sa_num(chr_code);
+}
+
+@ When a shorthand definition for an element of one of the sparse arrays
+is destroyed, we must reduce the reference count.
+
+@<Cases for |eq_destroy|@>=
+case toks_register: case internal_register:
+ if ((equiv_field(w) < mem_bot)||(equiv_field(w) > lo_mem_stat_max))
+ delete_sa_ref(equiv_field(w));@+break;
+
+@ The task to maintain (change, save, and restore) register values is
+essentially the same when the register is realized as sparse array
+element or entry in |eqtb|. The global variable |sa_chain| is the head
+of a linked list of entries saved at the topmost level |sa_level|; the
+lists for lowel levels are kept in special save stack entries.
+
+@<Glob...@>=
+static pointer @!sa_chain; /*chain of saved sparse array entries*/
+static quarterword @!sa_level; /*group level for |sa_chain|*/
+
+@ @<Set init...@>=
+sa_chain=null;sa_level=level_zero;
+
+@ The individual saved items are kept in pointer or word nodes similar
+to those used for the array elements: a word node with value zero is,
+however, saved as pointer node with the otherwise impossible |sa_index|
+value |tok_val_limit|.
+
+@d sa_loc(A) sa_ref(A) /*location of saved item*/
+
+@<Declare \eTeX\ procedures for tr...@>=
+static void sa_save(pointer @!p) /*saves value of |p|*/
+{@+pointer q; /*the new save node*/
+quarterword @!i; /*index field of node*/
+if (cur_level!=sa_level)
+ {@+check_full_save_stack;save_type(save_ptr)=restore_sa;
+ save_level(save_ptr)=sa_level;save_index(save_ptr)=sa_chain;
+ incr(save_ptr);sa_chain=null;sa_level=cur_level;
+ }
+i=sa_index(p);
+if (i < dimen_val_limit)
+ {@+if (sa_int(p)==0)
+ {@+q=get_node(pointer_node_size);i=tok_val_limit;
+ }
+ else{@+q=get_node(word_node_size);sa_int(q)=sa_int(p);
+ }
+ sa_ptr(q)=null;
+ }
+else{@+q=get_node(pointer_node_size);sa_ptr(q)=sa_ptr(p);
+ }
+sa_loc(q)=p;sa_index(q)=i;sa_lev(q)=sa_lev(p);
+link(q)=sa_chain;sa_chain=q;add_sa_ref(p);
+}
+
+@ @<Declare \eTeX\ procedures for tr...@>=
+static void sa_destroy(pointer @!p) /*destroy value of |p|*/
+{@+if (sa_index(p) < mu_val_limit) delete_glue_ref(sa_ptr(p));
+else if (sa_ptr(p)!=null)
+ if (sa_index(p) < box_val_limit) flush_node_list(sa_ptr(p));
+ else delete_token_ref(sa_ptr(p));
+}
+
+@ The procedure |sa_def| assigns a new value to sparse array elements,
+and saves the former value if appropriate. This procedure is used only
+for skip, muskip, box, and token list registers. The counterpart of
+|sa_def| for count and dimen registers is called |sa_w_def|.
+
+@d sa_define(A, B, C, D, E) if (e)
+ if (global) gsa_def(A, B);@+else sa_def(A, B);
+ else if (global) geq_define(C, D, E);@+else eq_define(C, D, E)
+@#
+@d sa_def_box /*assign |cur_box| to |box(cur_val)|*/
+ {@+find_sa_element(box_val, cur_val, true);
+ if (global) gsa_def(cur_ptr, cur_box);@+else sa_def(cur_ptr, cur_box);
+ }
+@#
+@d sa_word_define(A, B) if (e)
+ if (global) gsa_w_def(A, B);@+else sa_w_def(A, B);
+ else word_define(A, B)
+
+@<Declare \eTeX\ procedures for tr...@>=
+static void sa_def(pointer @!p, halfword @!e)
+ /*new data for sparse array elements*/
+{@+add_sa_ref(p);
+if (sa_ptr(p)==e)
+ {
+#ifdef @!STAT
+if (tracing_assigns > 0) show_sa(p,"reassigning");
+#endif
+@;@/
+ sa_destroy(p);
+ }
+else{
+#ifdef @!STAT
+if (tracing_assigns > 0) show_sa(p,"changing");
+#endif
+@;@/
+ if (sa_lev(p)==cur_level) sa_destroy(p);@+else sa_save(p);
+ sa_lev(p)=cur_level;sa_ptr(p)=e;
+#ifdef @!STAT
+ if (tracing_assigns > 0) show_sa(p,"into");
+#endif
+@;@/
+ }
+delete_sa_ref(p);
+}
+@#
+static void sa_w_def(pointer @!p, int @!w)
+{@+add_sa_ref(p);
+if (sa_int(p)==w)
+ {
+#ifdef @!STAT
+if (tracing_assigns > 0) show_sa(p,"reassigning");
+#endif
+@;@/
+ }
+else{
+#ifdef @!STAT
+if (tracing_assigns > 0) show_sa(p,"changing");
+#endif
+@;@/
+ if (sa_lev(p)!=cur_level) sa_save(p);
+ sa_lev(p)=cur_level;sa_int(p)=w;
+#ifdef @!STAT
+ if (tracing_assigns > 0) show_sa(p,"into");
+#endif
+@;@/
+ }
+delete_sa_ref(p);
+}
+
+@ The |sa_def| and |sa_w_def| routines take care of local definitions.
+@^global definitions@>
+Global definitions are done in almost the same way, but there is no need
+to save old values, and the new value is associated with |level_one|.
+
+@<Declare \eTeX\ procedures for tr...@>=
+static void gsa_def(pointer @!p, halfword @!e) /*global |sa_def|*/
+{@+add_sa_ref(p);
+#ifdef @!STAT
+if (tracing_assigns > 0) show_sa(p,"globally changing");
+#endif
+@;@/
+sa_destroy(p);sa_lev(p)=level_one;sa_ptr(p)=e;
+#ifdef @!STAT
+if (tracing_assigns > 0) show_sa(p,"into");
+#endif
+@;@/
+delete_sa_ref(p);
+}
+@#
+static void gsa_w_def(pointer @!p, int @!w) /*global |sa_w_def|*/
+{@+add_sa_ref(p);
+#ifdef @!STAT
+if (tracing_assigns > 0) show_sa(p,"globally changing");
+#endif
+@;@/
+sa_lev(p)=level_one;sa_int(p)=w;
+#ifdef @!STAT
+if (tracing_assigns > 0) show_sa(p,"into");
+#endif
+@;@/
+delete_sa_ref(p);
+}
+
+@ The |sa_restore| procedure restores the sparse array entries pointed
+at by |sa_chain|
+
+@<Declare \eTeX\ procedures for tr...@>=
+static void sa_restore(void)
+{@+pointer p; /*sparse array element*/
+@/do@+{p=sa_loc(sa_chain);
+if (sa_lev(p)==level_one)
+ {@+if (sa_index(p) >= dimen_val_limit) sa_destroy(sa_chain);
+#ifdef @!STAT
+ if (tracing_restores > 0) show_sa(p,"retaining");
+#endif
+@;@/
+ }
+else{@+if (sa_index(p) < dimen_val_limit)
+ if (sa_index(sa_chain) < dimen_val_limit) sa_int(p)=sa_int(sa_chain);
+ else sa_int(p)=0;
+ else{@+sa_destroy(p);sa_ptr(p)=sa_ptr(sa_chain);
+ }
+ sa_lev(p)=sa_lev(sa_chain);
+#ifdef @!STAT
+ if (tracing_restores > 0) show_sa(p,"restoring");
+#endif
+@;@/
+ }
+delete_sa_ref(p);
+p=sa_chain;sa_chain=link(p);
+if (sa_index(p) < dimen_val_limit) free_node(p, word_node_size);
+else free_node(p, pointer_node_size);
+}@+ while (!(sa_chain==null));
+}
+
+@ When reading \.{\\patterns} while \.{\\savinghyphcodes} is positive
+the current |lc_code| values are stored together with the hyphenation
+patterns for the current language. They will later be used instead of
+the |lc_code| values for hyphenation purposes.
+
+The |lc_code| values are stored in the linked trie analogous to patterns
+$p_1$ of length~1, with |hyph_root==trie_r[0]| replacing |trie_root| and
+|lc_code(p_1)| replacing the |trie_op| code. This allows to compress
+and pack them together with the patterns with minimal changes to the
+existing code.
+
+@d hyph_root trie_r[0] /*root of the linked trie for |hyph_codes|*/
+
+@<Initialize table entries...@>=
+hyph_root=0;hyph_start=0;
+
+@ @<Store hyphenation codes for current language@>=
+{@+c=cur_lang;first_child=false;p=0;
+@/do@+{q=p;p=trie_r[q];
+}@+ while (!((p==0)||(c <= so(trie_c[p]))));
+if ((p==0)||(c < so(trie_c[p])))
+ @<Insert a new trie node between |q| and |p|, and make |p| point to it@>;
+q=p; /*now node |q| represents |cur_lang|*/
+@<Store all current |lc_code| values@>;
+}
+
+@ We store all nonzero |lc_code| values, overwriting any previously
+stored values (and possibly wasting a few trie nodes that were used
+previously and are not needed now). We always store at least one
+|lc_code| value such that |hyph_index| (defined below) will not be zero.
+
+@<Store all current |lc_code| values@>=
+p=trie_l[q];first_child=true;
+for (c=0; c<=255; c++)
+ if ((lc_code(c) > 0)||((c==255)&&first_child))
+ {@+if (p==0)
+ @<Insert a new trie node between |q| and |p|, and make |p| point to
+it@>@;
+ else trie_c[p]=si(c);
+ trie_o[p]=qi(lc_code(c));
+ q=p;p=trie_r[q];first_child=false;
+ }
+if (first_child) trie_l[q]=0;@+else trie_r[q]=0
+
+@ We must avoid to ``take'' location~1, in order to distinguish between
+|lc_code| values and patterns.
+
+@<Pack all stored |hyph_codes|@>=
+{@+if (trie_root==0) for (p=0; p<=255; p++) trie_min[p]=p+2;
+first_fit(hyph_root);trie_pack(hyph_root);
+hyph_start=trie_ref[hyph_root];
+}
+
+@ The global variable |hyph_index| will point to the hyphenation codes
+for the current language.
+
+@d set_hyph_index /*set |hyph_index| for current language*/
+ if (trie_char(hyph_start+cur_lang)!=qi(cur_lang)
+ ) hyph_index=0; /*no hyphenation codes for |cur_lang|*/
+ else hyph_index=trie_link(hyph_start+cur_lang)
+@#
+@d set_lc_code(A) /*set |hc[0]| to hyphenation or lc code for |A|*/
+ if (hyph_index==0) hc[0]=lc_code(A);
+ else if (trie_char(hyph_index+A)!=qi(A)) hc[0]=0;
+ else hc[0]=qo(trie_op(hyph_index+A))
+
+@<Glob...@>=
+static trie_pointer @!hyph_start; /*root of the packed trie for |hyph_codes|*/
+static trie_pointer @!hyph_index; /*pointer to hyphenation codes for |cur_lang|*/
+
+@ When |saving_vdiscards| is positive then the glue, kern, and penalty
+nodes removed by the page builder or by \.{\\vsplit} from the top of a
+vertical list are saved in special lists instead of being discarded.
+
+@d tail_page_disc disc_ptr[copy_code] /*last item removed by page builder*/
+@d page_disc disc_ptr[last_box_code] /*first item removed by page builder*/
+@d split_disc disc_ptr[vsplit_code] /*first item removed by \.{\\vsplit}*/
+
+@<Glob...@>=
+static pointer @!disc_ptr0[vsplit_code-copy_code+1],
+ *const @!disc_ptr = @!disc_ptr0-copy_code; /*list pointers*/
+
+@ @<Set init...@>=
+page_disc=null;split_disc=null;
+
+@ The \.{\\pagediscards} and \.{\\splitdiscards} commands share the
+command code |un_vbox| with \.{\\unvbox} and \.{\\unvcopy}, they are
+distinguished by their |chr_code| values |last_box_code| and
+|vsplit_code|. These |chr_code| values are larger than |box_code| and
+|copy_code|.
+
+@<Generate all \eTeX...@>=
+primitive("pagediscards", un_vbox, last_box_code);@/
+@!@:page\_discards\_}{\.{\\pagediscards} primitive@>
+primitive("splitdiscards", un_vbox, vsplit_code);@/
+@!@:split\_discards\_}{\.{\\splitdiscards} primitive@>
+
+@ @<Cases of |un_vbox| for |print_cmd_chr|@>=;
+else if (chr_code==last_box_code) print_esc("pagediscards");
+else if (chr_code==vsplit_code) print_esc("splitdiscards")
+
+@ @<Handle saved items and |goto done|@>=
+{@+link(tail)=disc_ptr[cur_chr];disc_ptr[cur_chr]=null;
+goto done;
+}
+
+@ The \.{\\interlinepenalties}, \.{\\clubpenalties}, \.{\\widowpenalties},
+and \.{\\displaywidowpenalties} commands allow to define arrays of
+penalty values to be used instead of the corresponding single values.
+
+@d inter_line_penalties_ptr equiv(inter_line_penalties_loc)
+@<Generate all \eTeX...@>=
+primitive("interlinepenalties", set_shape, inter_line_penalties_loc);@/
+@!@:inter\_line\_penalties\_}{\.{\\interlinepenalties} primitive@>
+primitive("clubpenalties", set_shape, club_penalties_loc);@/
+@!@:club\_penalties\_}{\.{\\clubpenalties} primitive@>
+primitive("widowpenalties", set_shape, widow_penalties_loc);@/
+@!@:widow\_penalties\_}{\.{\\widowpenalties} primitive@>
+primitive("displaywidowpenalties", set_shape, display_widow_penalties_loc);@/
+@!@:display\_widow\_penalties\_}{\.{\\displaywidowpenalties} primitive@>
+
+@ @<Cases of |set_shape| for |print_cmd_chr|@>=
+case inter_line_penalties_loc: print_esc("interlinepenalties");@+break;
+case club_penalties_loc: print_esc("clubpenalties");@+break;
+case widow_penalties_loc: print_esc("widowpenalties");@+break;
+case display_widow_penalties_loc: print_esc("displaywidowpenalties");
+
+@ @<Fetch a penalties array element@>=
+{@+scan_int();
+if ((equiv(m)==null)||(cur_val < 0)) cur_val=0;
+else{@+if (cur_val > penalty(equiv(m))) cur_val=penalty(equiv(m));
+ cur_val=penalty(equiv(m)+cur_val);
+ }
+}
+
+@ |expand_depth| and |expand_depth_count| are used in the \eTeX\ code
+above, but not defined. So we correct this in the following modules,
+|expand_depth| having been defined by us as an integer paramater (hence
+there is a new primitive to create in \eTeX\ mode), and
+|expand_depth_count| needing to be a global. Both have to be defined to
+some sensible value.
+
+@<Glob...@>=
+static int @!expand_depth_count; /*current expansion depth*/
+
+@ @<Generate all \eTeX...@>=
+primitive("expanddepth", assign_int, int_base+expand_depth_code);@/
+@!@:expand\_depth\_}{\.{\\expanddepth} primitive@>
+
+@ @<Cases for |print_param|@>=
+case expand_depth_code: print_esc("expanddepth");@+break;
+
+@ @<Initialize variables for \eTeX\ extended mode@>=
+expand_depth=10000; /*value taken for compatibility with Web2C*/
+expand_depth_count=0;
+
+@* The extended features of \Prote.
+\Prote\ extends furthermore \eTeX\ i.e. \eTeX\ is thus required
+before adding \Prote\ own extensions. But if \eTeX\ mode has not
+be enabled, the engine is still compatible with \TeX with no added
+primitive commands and with a modification of code---from
+\eTeX\ exclusively for now---that is sufficiently minor so that
+the engine still deserves the name \TeX.
+
+@d Prote_ex (Prote_mode==1) /*is this prote mode?*/
+
+@<Glob...@>=
+static int @!Prote_mode; /*to be or not to be; but an int to dump*/
+
+@ We begin in \TeX\ compatibility mode. The state |Prote_mode| will be
+set to $1$ only if activated by the supplementary `*' added to the one
+activating the \eTeX\ extensions (in fact, this means for the user two
+initial `*' in a row).
+
+@<Initialize table entries...@>=
+Prote_mode=0; /*initially we are in compatibility mode*/
+
+@ @<Dump the \Prote\ state@>=
+dump_int(Prote_mode);
+
+@ @<Undump the \Prote\ state@>=
+undump(0, 1, Prote_mode);
+
+@ In order to not clobber the global scope with variables that are
+locally used, the initializations for \Prote, if the mode is
+activated, are done in a dedicated procedure. These are not part of
+what is dumped.
+
+@<Last-minute procedures@>=
+static void Prote_initialize(void)
+{@+int k; /*all-purpose index*/
+@<\Prote\ initializations@>;
+}
+
+@ There are commands and command modifiers, these command modifiers
+maybe encoding too a type. So we must not step on each other toes.
+
+@ When we are adding primitives that deal intimately with the variables
+of \TeX, in the |eqtb| regions (in our case regions 5 for integers, and
+6 for dimensions), the command modifier to the various
+\.{assign\_*} classes is simply the address. So we have interpolated
+our added variables above since this is done by the way of WEB
+pre-processing.
+
+@ For the conditional primitives, the way is straightforward.
+
+@d if_incsname_code (eTeX_last_if_test_cmd_mod+1) /* `\.{\\ifincsname}' */
+@d if_primitive_code (eTeX_last_if_test_cmd_mod+2) /* `\.{\\ifprimitive}' */
+@ The |last_item| class is for secondary internal values, that can be
+dereferenced by \.{\\the} but are read-only and are mainly related to
+the value of a current state or are such values but their assignation
+shall trigger an action, and we shall not hook in the \.{assign\_*}
+processing.
+
+The command modifiers for the |last_item| class were, originally,
+encoding too the type of the item (see m.410). But \eTeX\ has added its
+extensions and we won't try to be smart: the type |cur_val_level| will
+be set by switching between contiguous ranges of values of the same
+type.
+
+And we will define here all the instances of |last_item| that we add
+in order to keep our number assignations gathered.
+
+@d Prote_version_code (eTeX_last_last_item_cmd_mod+1)
+ /*code for \.{\\Proteversion}*/
+@d random_seed_code (eTeX_last_last_item_cmd_mod+2) /* \.{\\randomseed} */
+@d elapsed_time_code (eTeX_last_last_item_cmd_mod+3) /* \.{\\elapsedtime} */
+@d shell_escape_code (eTeX_last_last_item_cmd_mod+4) /* \.{\\shellescape} */
+@d last_xpos_code (eTeX_last_last_item_cmd_mod+5) /* \.{\\lastxpos} */
+@d last_ypos_code (eTeX_last_last_item_cmd_mod+6) /* \.{\\lastypos} */
+
+@ The \.{convert} class is for conversion of some external stuff to put
+it, as a token list, into the scanner. It is not an internal value that
+could be dereferenced by \.{\\the} and it is obviously not settable: it
+expands to the token list.
+
+@d Prote_revision_code (eTeX_last_convert_cmd_mod+1) /* \.{\\Proterevision}*/
+@d strcmp_code (eTeX_last_convert_cmd_mod+2) /* \.{\\strcmp} */
+@d set_random_seed_code (eTeX_last_convert_cmd_mod+3) /* \.{\\setrandomseed} */
+@d normal_deviate_code (eTeX_last_convert_cmd_mod+4) /* \.{\\normaldeviate} */
+@d uniform_deviate_code (eTeX_last_convert_cmd_mod+5) /* \.{\\uniformdeviate} */
+@d creation_date_code (eTeX_last_convert_cmd_mod+6) /* \.{\\creationdate} */
+@d file_size_code (eTeX_last_convert_cmd_mod+7) /* \.{\\filesize} */
+@d file_mod_date_code (eTeX_last_convert_cmd_mod+8) /* \.{\\filemodedate} */
+@d file_dump_code (eTeX_last_convert_cmd_mod+9) /* \.{\\filedump} */
+@d mdfive_sum_code (eTeX_last_convert_cmd_mod+10) /* \.{\\mdfivesum} */
+@d Prote_last_convert_code mdfive_sum_code
+
+@ When modifying the meaning of something---in this case, for now,
+switching to the primitive meaning if it exists---or modifying the
+way expansion is done, it seems that it can be thought as a special
+case of expansion, hence a variant of |expand_after|.
+
+@d primitive_code (eTeX_last_expand_after_cmd_mod+1) /* `\.{\\primitive}' */
+@d expanded_code (eTeX_last_expand_after_cmd_mod+2) /* `\.{\\expanded}' */
+@ When the primitive manipulate something really external, whether
+trying to insert something in the output format---\.{DVI} for
+us---or dealing with the system, it doesn't fit in any cmd group and
+could be called an exception. So it will be a variant of the |extension|
+cmd group.
+
+\eTeX\ didn't add new primitives to the extension command group, so we
+add a related macro, equal to |TeX_last_extension_cmd_mod|, simply so
+that it is locally obvious.
+
+@d eTeX_last_extension_cmd_mod TeX_last_extension_cmd_mod /*none added*/
+@d reset_timer_code (eTeX_last_extension_cmd_mod+1) /* `\.{\\resettimer}' */
+@d save_pos_code (eTeX_last_extension_cmd_mod+2) /* `\.{\\savepos}' */
+
+@*1 Identifying \Prote.
+
+We will start by giving a mean to test that \Prote\ is activated and
+to identify the version.
+
+@<Generate all \Prote\ primitives@>=
+if (!pdf_on) {
+primitive("Proteversion", last_item, Prote_version_code);
+@!@:Prote\_version\_}{\.{\\Proteversion} primitive@>
+primitive("Proterevision", convert, Prote_revision_code);@/
+@!@:Prote\_revision\_}{\.{\\Proterevision} primitive@>
+}
+@ We use the different hooks added to insert our cases.
+
+@<Cases of |last_item| for |print_cmd_chr|@>=
+case Prote_version_code: print_esc("Proteversion");@+break;
+
+@ @<Cases for fetching a \Prote\ int value@>=
+case Prote_version_code: cur_val=Prote_version;@+break;
+
+@ @<Cases of |convert| for |print_cmd_chr|@>=
+case Prote_revision_code: print_esc("Proterevision");@+break;
+
+@ @<Cases of `Scan the argument for command |c|'@>=
+case Prote_revision_code: do_nothing;@+break;
+
+@ @<Cases of `Print the result of command |c|'@>=
+case Prote_revision_code: print(Prote_revision);@+break;
+
+@*1 \Prote\ added token lists routines.
+
+We will, more than once, convert a general normally expanded text
+to a string. Due to the unfelicity of \PASCAL\ about forward
+declarations of functions, we declare procedures that do their task
+by defining global variables. In this case, |garbage| is used.
+
+|link(garbage)| will hold the pointer to the head of the token list,
+|info(garbage)| to the tail. If the two are equals, then the list is
+empty. The routine making a string will take |link(garbage)| and
+put the number in |info(garbage)|.
+
+@ The first procedure scan a general text (normally) expanded. The
+head of the reference count is returned in |link(garbage)|, the
+tail in |info(garbage)| and if the two are equals, the list is empty.
+User must keep in mind that this has to be flushed when done with!
+
+@<Forward declarations@>=
+static void scan_general_x_text(void);
+
+@ @<Declare \Prote\ procedures for token lists@>=
+static void scan_general_x_text(void)
+{@+pointer d; /*to save |def_ref|*/
+d=def_ref;info(garbage)=scan_toks(false, true);
+link(garbage)=def_ref;
+def_ref=d; /*restore whatever*/
+}
+
+@ The second procedure takes a token list defined in |link(garbage)|
+and converts it to a string number that is returned in |info(garbage)|.
+Neither the token list nor the string (obviously) are flushed.
+
+@<Forward declarations@>=
+static void toks_to_str(void);
+
+@ Here we are using |token_show| that has to take a reference count.
+
+@<Declare \Prote\ procedures for token lists@>=
+static void toks_to_str(void)
+{@+int old_setting; /*holds |selector| setting*/
+old_setting=selector;selector=new_string;
+ token_show(link(garbage));selector=old_setting;
+ str_room(1); /*flirting with the limit means probably truncation*/
+ info(garbage)=make_string();
+}
+
+@*1 \Prote\ added strings routines.
+
+The next procedure sets |name_of_file| from the string given as an
+argument, mimicking the |input| primitive by adding an |.tex| extension
+if there is none. It silently truncates if the length of the string
+exceeds the size of the name buffer and doesn't use |cur_area| and
+|cur_ext|, but |name_length| is set to the real name length (without
+truncating) so a test about |k <= file_name_size| allows to detect the
+impossibility of opening the file without having to call external code.
+The string is not flushed: it is the responsability of the code calling
+the procedure to flush it if wanted.
+
+@<Declare \Prote\ procedures for strings@>=
+static void str_to_name(str_number @!s)
+{@+int k; /*number of positions filled in |name_of_file|*/
+ASCII_code @!c; /*character being packed*/
+int @!j; /*index into |str_pool|*/
+k=0;for (j=str_start[s]; j<=str_start[s+1]-1; j++) {@+
+ c=so(str_pool[j]);incr(k);
+ if (k <= file_name_size) name_of_file[k]=xchr[c];
+ }
+name_length=k;
+name_of_file[name_length+1]=0;
+}
+
+@*1 Exchanging data with external routines.
+
+In order to try to sever external handling from our core, we
+introduce an all purpose exchange buffer |xchg_buffer|, that will
+be an array of bytes (these can be interpreted as \.{text\_char}
+or \.{ASCII\_char} or \.{eight\_bits}).
+
+The data to be used starts at index $1$ and ends at index
+|xchg_buffer_length|.
+
+For the moment, this buffer must accommodate a numerical MD5
+hash value, i.e. $16$ bytes long; will also be used to exchange $64$
+bytes chunks to feed MD5 hash generation, and will have to accommodate
+too the maximal size of the date returned by \.{\\creationdate} or
+\.{\\filemoddate} that is $23$ \.{text\_char}. So at least $64$ for now.
+
+@<Global...@>=
+static eight_bits @!xchg_buffer0[xchg_buffer_size],
+ *const @!xchg_buffer = @!xchg_buffer0-1;
+ /*exchange buffer for interaction with system routines*/
+static int @!xchg_buffer_length; /*last valid index in this buf; 0 means no data*/
+@ @<Check \Prote\ ``constant'' values for consistency@>=
+if (xchg_buffer_size < 64) bad=51;
+
+@ When there is data in the exchange buffer, the length of the data has
+to be set. When an external routine has consumed the data, it shall
+reset the length to $0$.
+
+@<\Prote\ initializations@>=
+xchg_buffer_length=0;
+
+@*1 \Prote\ states.
+
+\.{\\shellescape} depends on a pdf\TeX\ feature, namely the ability to
+escape to shell. There is no such thing in \Prote. So it expands to $0$.
+Note: this a status primitive; it does not allow to set the status but
+simply expands to a read-only integer reflecting it. In \Prote, it is
+always $0$.
+
+@<Generate all \Prote\ primitives@>=
+if (pdf_on) {
+primitive("pdfshellescape", last_item, shell_escape_code);
+@!@:pdfshellescape\_}{\.{\\pdfshellescape} primitive@>
+} else { primitive("shellescape", last_item, shell_escape_code);
+@!@:shellescape\_}{\.{\\shellescape} primitive@>
+}
+
+@ @<Cases of |last_item| for |print_cmd_chr|@>=
+case shell_escape_code:
+ if (pdf_on) print_esc("pdfshellescape");
+ else print_esc("shellescape");
+ @+break;
+
+@ @<Cases for fetching a \Prote\ int value@>=
+case shell_escape_code: cur_val=0;@+break;
+
+@*1 \Prote\ conditionals.
+
+We add the following conditionals, that are susceptible of the same
+expansion rules as the other |if_test| ones.
+
+@<Generate all \Prote\ primitives@>=
+primitive("ifincsname", if_test, if_incsname_code);
+@!@:if\_incsname\_}{\.{\\if} primitive@>
+if (pdf_on)
+primitive("ifpdfprimitive", if_test, if_primitive_code);
+@!@:if\_pdfprimitive\_}{\.{\\if} pdfprimitive@>
+else primitive("ifprimitive", if_test, if_primitive_code);
+@!@:if\_primitive\_}{\.{\\if} primitive@>
+
+
+@ @<Cases of |if_test| for |print_cmd_chr|@>=
+case if_incsname_code: print_esc("ifincsname");@+break;
+case if_primitive_code:
+ if (pdf_on) print_esc("ifpdfprimitive");
+ else print_esc("ifprimitive");
+ @+break;
+
+@ The conditional \.{\\ifincsname} is simple since we increment a global
+variable |incsname_state| when we enter the \.{\\csname} command and
+decrement it when we have reached and passed the \.{\\endcsname}---a
+scope depth index.
+
+@<\Prote\ initializations@>=
+incsname_state=0;
+
+@ @<Cases for |conditional|@>=
+case if_incsname_code: b=(incsname_state > 0);@+break;
+
+@ The conditional \.{\\ifprimitive} is true when the following control
+sequence is a primitive; false otherwise. |id_lookup| can return
+|undefined_control_sequence| (for a control sequence not entered in the
+hash since |no_new_control_sequence| is |true|), but since it has the
+\.{eq\_type} set to |undefined_cs|, the test of this latter works as
+for a control sequence entered but never defined.
+
+@ @<Cases for |conditional|@>=
+case if_primitive_code: {@+@/do@+{get_token();}@+ while (!(cur_tok!=space_token));
+if ((cur_cs!=0)&&(cur_cmd!=undefined_cs)&&(cur_cmd < call)) b=true;else b=false;
+} @+break;
+
+@*1 \Prote\ primitives changing definition or expansion.
+
+The next primitives, here, are more involved since they are whether
+changing the definition of a control sequence, or modifying how the
+tokens will be treated.
+
+@ Since a user level control sequence can give a new definition to a
+primitive, the \.{primitive}... primitive, if the argument is a control
+sequence whose name is the name of a primitive, will make this primitive
+meaning the meaning of the control sequence {\it hic et nunc}. If there
+was no primitive meaning, no error is raised and nothing is changed.
+It can be seen as a kind of \.{expand\_after} command since it is in the
+external handling of the token list creation.
+
+Since we need to redefine the token and hence give a valid control
+sequence in the |eqtb|, we have defined |frozen_primitive|. This
+``frozen'' is, actually, not quite frozen by itself since we will
+redefine its values according to the primitive definition we have
+to restablish momentarily. But it is indeed ``permanent'' since
+it only refers to the permanently defined meanings. Hence, the
+initialization of the |frozen_primitive| address is just to document
+the code: these values will be overwritten on each actual call.
+
+@<Generate all \Prote\ primitives@>=
+if (pdf_on)
+primitive("pdfprimitive", expand_after, primitive_code);
+@!@:pdfprimitive\_}{\.{\\pdfprimitive} primitive@>
+else
+primitive("primitive", expand_after, primitive_code);
+@!@:primitive\_}{\.{\\primitive} primitive@>
+text(frozen_primitive)=text(cur_val);eqtb[frozen_primitive]=eqtb[cur_val];
+
+@ @<Cases of |expandafter| for |print_cmd_chr|@>=
+case primitive_code:
+ if (pdf_on) print_esc("pdfprimitive");
+ else print_esc("primitive");
+ @+break;
+
+@ The problem is that the primitives are added at |level_one| and that a
+redefinition as a macro at this same level by a user simply overwrites
+the definition. We need then to keep these definitions.
+
+Primitives are only added by \.{INITEX}. So we can consider what we will
+call a |ROM|, since it can be only ``flashed'' by \.{INITEX} and is
+read-only afterwards, a kind of BIOS table holding initial system calls
+(primitives).
+
+Since primitives are not macros (they don't need to expand or to
+evaluate parameters since their definition is directly in the code),
+the definition of a primitive is a couple: the command class (|cur_cmd|)
+and the modifier (|cur_chr|) to distinguish between the cases---the
+instances. But since, at the user level, a primitive is identified by
+its name, and that a redefinition is, mandatorily, a homonym, the
+location of the macro shadowing the primitive is at the same address as
+was the primitive in the |eqtb|. So in order to speed-up the check, we
+should organize things so that the address in the |eqtb| of a control
+sequence (one character or multiletter) can be readily
+converted in an address in the |ROM| array.
+
+This array will be an array of memory word, of type |two_halves|, in
+order to re-use the macro definitions set for the table of equivalents.
+
+The one character primitives are added by direct addressing relative to
+|single_base|. The multiletter primitives are added starting at
+|frozen_control_sequence-1|, downwards; but there are only, at the
+moment, $322$ multileter primitives defined by \TeX, $78$ such
+primitives defined by \eTeX, and we are adding $24$ more. It is clear
+that, looking at primitives, region 2 of |eqtb| is really a sparse array
+and that, when |hash_size| is increased for format needs, there will be
+a fair amount of space wasted if we simply copy, in fact, second part
+of region 1 and region 2 in the |ROM|.
+
+Yes, but it is simpler as a first approach---premature optimization is
+the root of all evil. So a simple translation scheme will be enough.
+
+The index in |ROM| will start at $1$ and will go up to $256 + 1 +
+hash\_size$, that is a simple translation from |single_base| to
+|ROM_base|, but only for addresses of interest, the other pointing to
+an |ROM_undefined_primitive| that will allow an easy test.
+
+@d ROM_base 1
+@d ROM_size (256+1+hash_size) /*256 oc, undefined and ml*/
+@d ROM_undefined_primitive 257
+@d ROM_type_field(A) A.hh.b0
+@d ROM_equiv_field(X) X.hh.rh
+@d ROM_type(A) ROM_type_field(ROM[A]) /*command code for equivalent*/
+@d set_ROM_p_from_cs(A) if ((A >= single_base)
+ &&(A < frozen_control_sequence))
+ p=A-single_base+ROM_base;
+ else p=ROM_undefined_primitive
+
+@<Global...@>=
+static memory_word @!ROM0[ROM_size-ROM_base+1],
+ *const @!ROM = @!ROM0-ROM_base;
+
+@ Even if it will be unused in \TeX\ or \eTeX\ modes, we will initialize
+it since we add code to the |primitive| procedure and we need \TeX\
+and \eTeX\ ones to be registered as well, whether \.{INITEX} switches to
+\Prote\ mode later or not.
+
+@<Initialize table entries...@>=
+ROM[ROM_undefined_primitive]=eqtb[undefined_control_sequence];
+for (k=ROM_base; k<=256; k++) ROM[k]=ROM[ROM_undefined_primitive];
+for (k=ROM_undefined_primitive+1; k<=ROM_size; k++)
+ ROM[k]=ROM[ROM_undefined_primitive];
+
+@ When a primitive is added---and this only happens in \.{INITEX}---we
+have to define the corresponding address in the \.{ROM}.
+
+@ |cur_val| has the pointer in second part of region 1 or in region 2
+of |eqtb|.
+
+@<Add primitive definition to the |ROM| array@>=
+set_ROM_p_from_cs(cur_val);
+ROM[p]=eqtb[cur_val];
+
+@ This array has to be dumped since it is only defined by \.{INITEX}. It
+is always dumped even if it is unused unless in \Prote\ mode.
+
+@<Dump the |ROM| array@>=
+for (k=ROM_base; k<=ROM_size; k++) dump_wd(ROM[k]);
+
+@ And what has been dumped shall be undumped.
+
+@<Undump the |ROM| array@>=
+for (k=ROM_base; k<=ROM_size; k++) undump_wd(ROM[k]);
+
+@ Once all this is done, the processing of \.{\\primitive} is simple:
+we read the next token that has to be a control sequence. If this
+control sequence belongs to region 1 or 2 and is defined in |ROM|, we
+redefine the token to be the |frozen_primitive| control sequence,
+redefining its codes from the |ROM| and setting the text associated for
+printing purposes. If not, the token is unchanged. Then we put back the
+token so that it will be processed again, maybe redefined.
+
+@<Cases for |expandafter|@>=
+case primitive_code: {@+get_token();
+set_ROM_p_from_cs(cur_cs);
+if ((p!=ROM_undefined_primitive)&&(ROM_type(p)!=undefined_cs)) {@+
+ eqtb[frozen_primitive]=ROM[p];
+ text(frozen_primitive)=text(cur_cs);
+ cur_tok=cs_token_flag+frozen_primitive;
+ }
+back_input();
+} @+break;
+
+@ The next primitive changes the expansion of its argument that is like
+a general text expanded, except that protected macros (an \eTeX\
+extension) are not extanded.
+
+@ @<Generate all \Prote\ primitives@>=
+primitive("expanded", expand_after, expanded_code);@/
+@!@:expanded\_}{\.{\\expanded} primitive@>
+
+@ @<Cases of |expandafter| for |print_cmd_chr|@>=
+case expanded_code: print_esc("expanded");
+
+@ This intervenes in |expand| and we must substitute a token list to our
+current token, putting it back for further reprocessing.
+
+@<Cases for |expandafter|@>=
+case expanded_code: {@+scan_general_x_text();
+ back_list(link(link(garbage)));
+ free_avail(link(garbage)); /*drop reference count*/
+ }
+
+@*1 \Prote\ strings related primitives.
+
+The primitive \.{\\strcmp} text two parameters that are general text
+without expansion. The two token lists created are converted to strings
+and this couple of strings is then compared, character by character. If
+the first string is lexicographically sorted before the second, the
+expansion is $-1$; if the two strings are equal, the expansion is $0$;
+if the first string is lexicographically sorted after the second, the
+expansion is $1$.
+
+@<Generate all \Prote\ primitives@>=
+if (pdf_on)
+primitive("pdfstrcmp", convert, strcmp_code);@/
+@!@:pdf\_strcmp\_}{\.{\\pdfstrcmp} primitive@>
+else
+primitive("strcmp", convert, strcmp_code);
+@!@:strcmp\_}{\.{\\strcmp} primitive@>
+
+
+@ @<Cases of |convert| for |print_cmd_chr|@>=
+case strcmp_code:
+ if (pdf_on) print_esc("pdfstrcmp");
+ else print_esc("strcmp");
+ @+break;
+
+@ It should be noted that the strings comparison is \TeX\ strings
+comparison: the arguments are subject to the manipulation done when
+scanning a general text (squeezing non escaped blanks), and the
+characters are converted according to the |xord| array. Thus it is
+an |ASCII_code|---in the \TeX\ sense explained at the very beginning
+of the web file, part 2---comparison and the result is the same,
+as long as relative characters are mapped to the same value, whatever
+the system. Nul strings are valid.
+
+@<Cases of `Scan the argument for command |c|'@>=
+case strcmp_code: @<Scan the argument for |strcmp_code|@>@;@+break;
+
+@ @<Scan the argument for |strcmp_code|@>=
+{@+scan_general_x_text();toks_to_str();
+ s=info(garbage);flush_list(link(garbage));
+ scan_general_x_text();toks_to_str();
+ t=info(garbage);flush_list(link(garbage));
+ if ((length(s)==0)&&(length(t)==0)) cur_val=0;
+ else if (length(s)==0) cur_val=-1;
+ else if (length(t)==0) cur_val=1;
+ else{@+m=str_start[s];n=str_start[t];r=false;
+ while ((!r)&&(m < str_start[s+1])&&(n < str_start[t+1])) {@+
+ cur_val=str_pool[m]-str_pool[n];if (cur_val!=0) r=true;
+ incr(m);incr(n);
+ }
+ if (cur_val==0) {@+if (length(s)!=length(t))
+ if (m!=str_start[s+1]) cur_val=1;else cur_val=-1;
+ }
+ else cur_val=cur_val/(double)abs(cur_val);
+ }
+ flush_string;flush_string;
+ }
+
+@ @<Cases of `Print the result of command |c|'@>=
+case strcmp_code: print_int(cur_val);@+break;
+
+@*1 \Prote\ date and time related primitives.
+
+The following primitives are related to the time elapsed since a
+defined moment in time. The creation date is fixed at the moment when
+|fix_date_and_time| has been called and stays fixed afterwards. This
+moment is also, by default, the reference moment for computing the time
+elapsed.
+
+@ The creation date is retrieved by the \.{\\creationdate} primitive. As
+explained above, the date corresponds to the moment when
+|fix_date_and_time| was called taking into account |FORCE_SOURCE_DATE|
+and |SOURCE_DATE_EPOCH| (see above, m.241). If the creation date is
+forced, the string will be UTC related.
+
+The format of the string is |D: YYYYMMDDHHmmSSOHH"mm"|, `O' being the
+relationship of local time to UT, that is `-' (minus), `+' or `Z';
+HH followed by a single quote being the absolute value of the offset
+from UT in hours (00--23), mm followed by a single quote being the
+absolute value of the offset from UT in minutes (00--59). All fields
+after the year are optional and default to zero values.
+
+@ @<Generate all \Prote\ primitives@>=
+if (!pdf_on)
+primitive("pdfcreationdate", convert, creation_date_code);@/
+@!@:pdf\_creation\_date\_}{\.{\\pdfcreationdate} primitive@>
+else
+primitive("creationdate", convert, creation_date_code);@/
+@!@:creationdate\_}{\.{\\creationdate} primitive@>
+
+
+@ @<Cases of |convert| for |print_cmd_chr|@>=
+case creation_date_code:
+ if (pdf_on) print_esc("pdfcreationdate");
+ else print_esc("creationdate");
+ @+break;
+
+@ |get_creation_date| has to be provided by the system.
+@^system dependencies@>
+
+@<Cases of `Scan the argument for command |c|'@>=
+case creation_date_code: get_creation_date();@+break;
+
+@ The date is in the |time_str| so we have simply to convert the
+characters.
+
+@<Cases of `Print the result of command |c|'@>=
+case creation_date_code: for (k=0; time_str[k]!='\0'; k++)
+ print_char(time_str[k]);@+break;
+
+@ The time elapsed is a scaled integer the unit being scaled seconds,
+i.e. $1/65536$ of a second. Since our scaled integers have a
+defined range, the value can not reach or pass, in plain seconds,
+$32767$.
+
+The elapsed time returned is relative to some defined moment. At
+start, the reference moment is the time the date was set for
+|fix_date_and_time|. This requires system support and the default
+implementation here will then fix this moment at noon on 4 July 1776
+and what would be returned by the function is here simply defined by
+a macro: with this reference time and this basic code, |infinity| is
+the permanent answer.
+@^system dependencies@>
+
+@d get_elapsed_time infinity /*a function should be implemented*/
+
+@ @<Generate all \Prote\ primitives@>=
+if (pdf_on) {
+primitive("pdfresettimer", extension, reset_timer_code);@/
+@!@:reset\_timer\_}{\.{\\pdfresettimer} primitive@>
+primitive("pdfelapsedtime", last_item, elapsed_time_code);
+@!@:elapsed\_time\_}{\.{\\pdfelapsedtime} primitive@>
+} else {
+primitive("resettimer", extension, reset_timer_code);@/
+@!@:resettimer\_}{\.{\\resettimer} primitive@>
+primitive("elapsedtime", last_item, elapsed_time_code);@/
+@!@:elapsedtime\_}{\.{\\elapsedtime} primitive@>
+}
+
+@ @<Cases of |last_item| for |print_cmd_chr|@>=
+case elapsed_time_code:
+ if (pdf_on) print_esc("pdfelapsedtime");
+ else print_esc("elapsedtime");
+ @+break;
+
+@ @<Cases of |extension| for |print_cmd_chr|@>=
+case reset_timer_code:
+ if (pdf_on) print_esc("pdfresettimer");
+ else print_esc("resettimer");
+ @+break;
+
+@ @<Cases for fetching a \Prote\ int value@>=
+case elapsed_time_code:
+ { double t=(start_sec*1.0+start_nsec/1000000000.0-reset_timer)*0x10000;
+ if (t>=infinity) cur_val=infinity;
+ else cur_val=(scaled)t;
+ @+break;
+ }
+
+@ The reference moment can be reset by a call to the primitive
+\.{\\resettimer}. It simply resets the reference moment to the moment
+the primitive was called. The counter is not regularily incremented.
+When asked about the time elapsed what is returned is the difference, in
+scaled seconds, from the moment of the call to the moment of reference.
+So there is no persistent variable neither a kind of clock implemented.
+@^system dependencies@>
+
+Since we maintain the time anyway, we implement the timing function.
+@<Glob...@>=
+static double reset_timer=0.0; /*time of the last reset in seconds*/
+
+
+@ Since to reset the timer a simple call to the routine is necessary,
+we simply add it to \.{main\_control} by adding it to the cases handled
+by \.{do\_extension}. It contributes nothing to the token list: it is a
+``fire and forget'', so no need to handle the special \.{subtype} in the
+other hooks.
+
+@<Cases for |do_extension|@>=
+case reset_timer_code: reset_timer=start_sec*1.0+start_nsec/1000000000.0;@+break;
+
+@*1 \Prote\ file related primitives.
+
+The presence of the following primitives in the engine can be
+questioned. Since they are very external, and their implementation, for
+example in C, requires things that are not in the C standard (the
+date of modification of the file, for example). So these should not be
+multiplied.
+
+@ The \.{\\filesize} primitive expands to the size, in bytes, of the
+file.
+
+@<Generate all \Prote\ primitives@>=
+if (pdf_on)
+primitive("pdffilesize", convert, file_size_code);@/
+@!@:pdf\_file\_size\_}{\.{\\pdffilesize} primitive@>
+else
+primitive("filesize", convert, file_size_code);
+@!@:filesize\_}{\.{\\filesize} primitive@>
+
+
+@ @<Cases of |convert| for |print_cmd_chr|@>=
+case file_size_code:
+ if (pdf_on) print_esc("pdffilesize");
+ else print_esc("filesize");
+ @+break;
+
+@ In order to be able to treat the problem when trying to open the file,
+we open here and pass the file pointer, if success, to a dedicated
+function in order to get its size. In case of problem, nothing is
+returned.
+@^system dependencies@>
+
+@<Cases of `Scan the argument for command |c|'@>=
+case file_size_code: @<Scan the argument for |file_size_code|@>@;@+break;
+
+@ @<Scan the argument for |file_size_code|@>=
+{@+scan_general_x_text();toks_to_str();
+ s=info(garbage);flush_list(link(garbage));str_to_name(s);
+ cur_val=-1; /*invalid value if error*/
+ cur_val=get_file_size();
+ flush_string;
+}
+
+@ @<Cases of `Print the result of command |c|'@>=
+case file_size_code: if (cur_val!=-1) print_int(cur_val);@+break;
+
+@ The \.{\\filemoddate} expands to a date with the same format as the
+creation date (see \.{\\creationdate}).
+
+@<Generate all \Prote\ primitives@>=
+if (pdf_on)
+primitive("pdffilemoddate", convert, file_mod_date_code);@/
+@!@:pdf\_file\_mod\_date\_}{\.{\\pdffilemoddate} primitive@>
+else
+primitive("filemoddate", convert, file_mod_date_code);
+@!@:filemoddate\_}{\.{\\filemoddate} primitive@>
+
+
+@ @<Cases of |convert| for |print_cmd_chr|@>=
+case file_mod_date_code:
+ if (pdf_on) print_esc("pdffilemoddate");
+ else print_esc("filemoddate");
+ @+break;
+
+@ For getting the argument, the treatment resembles that of
+\.{\\filesize} obviously, since it is only the type of information
+returned that changes. The availability of this information in system
+dependent. The information shall be set in |xchg_buffer|.
+@^system dependencies@>
+
+In this basic implementation, we set the string to the empty one by
+simply setting |xchg_buffer_length| to $0$.
+
+@d get_file_mtime xchg_buffer_length=0
+
+@<Cases of `Scan the argument for command |c|'@>=
+case file_mod_date_code: @<Scan the argument for |file_mod_date_code|@>@;@+break;
+
+@ @<Scan the argument for |file_mod_date_code|@>=
+{@+scan_general_x_text();toks_to_str();
+ s=info(garbage);flush_list(link(garbage));str_to_name(s);
+ get_file_mod_date();
+ flush_string;
+} @+break;
+
+@ Printing the result consists simply in printing every
+|text_char| in |time_str|.
+If the length is $0$, nothing is printed.
+
+@<Cases of `Print the result of command |c|'@>=
+case file_mod_date_code: for (k=0; time_str[k]!='\0'; k++)
+ print_char(time_str[k]);@+break;
+
+@ The primitive \.{\\filedump} expands to the dump of the first
+ \.{length} bytes of the file, starting from \.{offset}. Offset and
+length are optional integers given, in that order, introduced resp.
+by the keywords ``offset'' and ``length''. If not specified, they
+default to $0$. A length of $0$ expands to nothing (it is not an error).
+The file name is given as a |general text|.
+
+@<Generate all \Prote\ primitives@>=
+if (pdf_on)
+primitive("pdffiledump", convert, file_dump_code);@/
+@!@:pdf\_file\_dump\_}{\.{\\pdffiledump} primitive@>
+else
+primitive("filedump", convert, file_dump_code);
+@!@:filedump\_}{\.{\\filedump} primitive@>
+
+
+@ @<Cases of |convert| for |print_cmd_chr|@>=
+case file_dump_code:
+ if (pdf_on) print_esc("pdffiledump");
+ else print_esc("filedump");
+ @+break;
+
+@ The scanning of the arguments is obvious from the syntax above.
+
+Since ``offset'' and ``length'' may be given in that order, we assign
+the variables \.{k} and \.{l}, in alphabetical order. These have to be
+positive or nul values.
+
+Contrary to other blocks, and for optimization purposes (in order not to
+clobber the string pool with data that we can read, when necessary, one
+byte at a time), \.{k}, \.{l} and \.{f} will be defined here and used
+when printing.
+
+@<Cases of `Scan the argument for command |c|'@>=
+case file_dump_code: @<Scan the argument for |file_dump_code|@>@;@+break;
+
+@ @<Scan the argument for |file_dump_code|@>=
+{@+k=0;l=0; /*defaults*/
+ if (scan_keyword("offset")) {@+scan_int();
+ if (cur_val < 0) {@+print_err("Bad ");print_esc("filedump");
+@.Bad \\filedump@>
+ help2("I allow only nonnegative values here.",@/
+ "I changed this one to zero.");int_error(cur_val);
+ }
+ else k=cur_val;
+ }
+ if (scan_keyword("length")) {@+scan_int();
+ if (cur_val < 0) {@+print_err("Bad ");print_esc("filedump");
+@.Bad \\filedump@>
+ help2("I allow only nonnegative values here.",@/
+ "I changed this one to zero.");int_error(cur_val);
+ }
+ else l=cur_val;
+ }
+ scan_general_x_text();toks_to_str();s=info(garbage);
+ flush_list(link(garbage));str_to_name(s);
+ flush_string; /*this one was the filename argument*/
+ }
+
+@ The variables have been set, and the file name has been defined. We
+simply print the uppercase hexadecimal transcription of every byte
+requested before closing the file. Here we deal with bytes
+(\.{eight\_bits} values) so there is no transcription.
+
+@<Cases of `Print the result of command |c|'@>=
+case file_dump_code:
+ { FILE *f=fopen((char*)name_of_file0,"rb");
+ if (f!=NULL) {@+
+ fseek(f,k,SEEK_SET);
+ do@+{i=fgetc(f); if (i==EOF) break;
+ dig[0]=i%16;dig[1]=i/16;
+ print_the_digs(2);decr(l);
+ }@+ while (!(feof(f)||(l==0)));
+ fclose(f);
+ }
+ } @+break;
+
+@ The \.{\\mdfivesum} is obviously a variant of the \.{convert} class since
+it takes values from external and put them as a token list in the
+stream.
+
+@<Generate all \Prote\ primitives@>=
+if (pdf_on)
+primitive("pdfmdfivesum", convert, mdfive_sum_code);@/
+@!@:pdf\_mdfive\_sum\_}{\.{\\pdfmdfivesum} primitive@>
+else
+primitive("mdfivesum", convert, mdfive_sum_code);
+@!@:mdfivesum\_}{\.{\\mdfivesum} primitive@>
+
+
+@ @<Cases of |convert| for |print_cmd_chr|@>=
+case mdfive_sum_code:
+ if (pdf_on) print_esc("pdfmdfivesum");
+ else print_esc("mdfivesum");
+ @+break;
+
+@ There is an optional keyword "file" that will tell us if the | < general
+text > | is to be taken as a filename or just as the string to hash. The
+| < balanced text > | is expanded in both cases.
+
+Once this is done, we ask to init the MD5 state; then fill the exchange
+buffer with chunks of data and update the MD5 hash with every chunk
+until source is exhausted and ask for the final ($16$ bytes numerical
+value) result that will be put in the |xchg_buffer|.
+
+Since we are looking for a ``general text'', that must be enclosed (at
+least: ended; the opening brace can be implicit) by a |right_brace|,
+an error will be caught with runaways.
+
+The general text is converted to a string. It is legal to have an empty
+string if the argument is not a file.
+
+@<Cases of `Scan the argument for command |c|'@>=
+case mdfive_sum_code: @<Scan the argument for |mdfive_sum_code|@>@; @+break;
+
+@ @<Scan the argument for |mdfive_sum_code|@>=
+{@+r=scan_keyword("file");scan_general_x_text();
+toks_to_str();s=info(garbage);flush_list(link(garbage));
+l=get_md5_sum(s,r);
+flush_string; /*done with the filename or string to hash*/
+}
+
+@ As a result, there is $16$ bytes in the |md5_digest| representing the
+MD5 hash. We simply print, byte by byte, the uppercase hexadecimal
+representation of this hash.
+
+@<Cases of `Print the result of command |c|'@>=
+case mdfive_sum_code: for (k=0; k<l; k++)
+{@+dig[0]=md5_digest[k]%16;@+dig[1]=md5_digest[k]/16;print_the_digs(2);
+} @+break;
+
+@*1 Pseudo-random number generation.
+
+These routines come from John Hobby's \MP\ and generate pseudo-random
+numbers with the additive scheme recommended in Section 3.6 of
+{\sl The Art of Computer Programming}; however, the results are
+random fractions between 0 and |mpfract_one-1|, inclusive.
+
+\MP\ uses 28~significant bits of precision and we have kept this in
+order for the routines to behave the same way as in \MP. So the name
+|mpfract| will be used instead of |scaled|, while the two are integers,
+in the range defined by \TeX.
+
+@d double(A) A=A+A /*multiply a variable by two*/
+@d halfp(A) (A)/2 /*when quantity is known to be positive or zero*/
+
+@ The subroutines for logarithm and exponential involve two tables.
+The first is simple: |two_to_the[k]| equals $2^k$. The second involves
+a bit more calculation, which the author claims to have done correctly:
+|spec_log[k]| is $2^{27}$ times $\ln\bigl(1/(1-2^{-k})\bigr)=
+2^{-k}+{1\over2}2^{-2k}+{1\over3}2^{-3k}+\cdots\,$, rounded to the
+nearest integer.
+
+@<Glob...@>=
+static int @!two_to_the[31]; /*powers of two*/
+static int @!spec_log0[28], *const @!spec_log = @!spec_log0-1; /*special logarithms*/
+
+@ @<\Prote\ initializations@>=
+two_to_the[0]=1;
+for (k=1; k<=30; k++) two_to_the[k]=2*two_to_the[k-1];
+spec_log[1]=93032640;
+spec_log[2]=38612034;
+spec_log[3]=17922280;
+spec_log[4]=8662214;
+spec_log[5]=4261238;
+spec_log[6]=2113709;
+spec_log[7]=1052693;
+spec_log[8]=525315;
+spec_log[9]=262400;
+spec_log[10]=131136;
+spec_log[11]=65552;
+spec_log[12]=32772;
+spec_log[13]=16385;
+for (k=14; k<=27; k++) spec_log[k]=two_to_the[27-k];
+spec_log[28]=1;
+
+@ Here is the routine that calculates $2^8$ times the natural logarithm
+of a |scaled| quantity; it is an integer approximation to $2^{24}\ln(x/2^{16})$,
+when |x| is a given positive integer.
+
+The method is based on exercise 1.2.2--25 in {\sl The Art of Computer
+Programming\/}: During the main iteration we have $1\L 2^{-30}x<1/(1-2^{1-k})$,
+and the logarithm of $2^{30}x$ remains to be added to an accumulator
+register called~$y$. Three auxiliary bits of accuracy are retained in~$y$
+during the calculation, and sixteen auxiliary bits to extend |y| are
+kept in~|z| during the initial argument reduction. (We add
+$100\cdot2^{16}=6553600$ to~|z| and subtract 100 from~|y| so that |z| will
+not become negative; also, the actual amount subtracted from~|y| is~96,
+not~100, because we want to add~4 for rounding before the final division by~8.)
+
+@<Declare \Prote\ arithmetic routines@>=
+static scaled m_log(scaled @!x)
+{@+int @!y, @!z; /*auxiliary registers*/
+int @!k; /*iteration counter*/
+if (x <= 0) @<Handle non-positive logarithm@>@;
+else{@+y=1302456956+4-100; /*$14\times2^{27}\ln2\approx1302456956.421063$*/
+ z=27595+6553600; /*and $2^{16}\times .421063\approx 27595$*/
+ while (x < mpfract_four)
+ {@+double(x);y=y-93032639;z=z-48782;
+ } /*$2^{27}\ln2\approx 93032639.74436163$
+ and $2^{16}\times.74436163\approx 48782$*/
+ y=y+(z/unity);k=2;
+ while (x > mpfract_four+4)
+ @<Increase |k| until |x| can be multiplied by a factor of $2^{-k}$, and
+adjust $y$ accordingly@>;
+ return y/8;
+ }
+}
+
+@ @<Increase |k| until |x| can...@>=
+{@+z=((x-1)/two_to_the[k])+1; /*$z=\lceil x/2^k\rceil$*/
+while (x < mpfract_four+z)
+ {@+z=halfp(z+1);k=k+1;
+ }
+y=y+spec_log[k];x=x-z;
+}
+
+@ @<Handle non-positive logarithm@>=
+{@+print_err("Logarithm of ");
+@.Logarithm...replaced by 0@>
+print_scaled(x);print(" has been replaced by 0");
+help2("Since I don't take logs of non-positive numbers,",@/
+ "I'm zeroing this one. Proceed, with fingers crossed.");
+error();return 0;
+}
+
+@ Here is introduced the special 28bits significand |mpfract|.
+
+@d el_gordo 017777777777 /*$2^{31}-1$, the largest value that \TeX\ likes*/
+@d mpfract_half 01000000000 /*$2^{27}$, represents 0.50000000*/
+@d mpfract_one 02000000000 /*$2^{28}$, represents 1.00000000*/
+@d mpfract_four 010000000000 /*$2^{30}$, represents 4.00000000*/
+
+@<Types...@>=
+typedef int mpfract; /*this type is used for pseudo-random numbers*/
+
+@ The |make_mpfract| routine produces the |mpfract| equivalent of
+|p/(double)q|, given integers |p| and~|q|; it computes the integer
+$f=\lfloor2^{28}p/q+{1\over2}\rfloor$, when $p$ and $q$ are
+positive. If |p| and |q| are both of the same scaled type |t|,
+the ``type relation'' |make_mpfract(t, t)==mpfract| is valid;
+and it's also possible to use the subroutine ``backwards,'' using
+the relation |make_mpfract(t, mpfract)==t| between scaled types.
+
+If the result would have magnitude $2^{31}$ or more, |make_mpfract|
+sets |arith_error=true|. Most of \TeX's internal computations have
+been designed to avoid this sort of error.
+
+If this subroutine were programmed in assembly language on a typical
+machine, we could simply compute |(@t$2^{28}$@>*p)/q|, since a
+double-precision product can often be input to a fixed-point division
+instruction. But when we are restricted to \PASCAL\ arithmetic it
+is necessary either to resort to multiple-precision maneuvering
+or to use a simple but slow iteration. The multiple-precision technique
+would be about three times faster than the code adopted here, but it
+would be comparatively long and tricky, involving about sixteen
+additional multiplications and divisions.
+
+The present implementation is highly portable, but slow; it avoids
+multiplication and division except in the initial stage. But since it is
+not part of \TeX\ inner loop, it doesn't matter.
+
+@<Declare \Prote\ arithmetic routines@>=
+static mpfract make_mpfract(int @!p, int @!q)
+{@+int @!f; /*the fraction bits, with a leading 1 bit*/
+int @!n; /*the integer part of $\vert p/q\vert$*/
+bool @!negative; /*should the result be negated?*/
+int @!be_careful; /*disables certain compiler optimizations*/
+if (p >= 0) negative=false;
+else{@+negate(p);negative=true;
+ }
+if (q <= 0)
+ {
+#ifdef @!DEBUG
+if (q==0) confusion("/");@;
+#endif
+@;@/
+@:this can't happen /}{\quad \./@>
+ negate(q);negative=!negative;
+ }
+n=p/q;p=p%q;
+if (n >= 8)
+ {@+arith_error=true;
+ if (negative) return-el_gordo;@+else return el_gordo;
+ }
+else{@+n=(n-1)*mpfract_one;
+ @<Compute $f=\lfloor 2^{28}(1+p/q)+{1\over2}\rfloor$@>;
+ if (negative) return-(f+n);@+else return f+n;
+ }
+}
+
+@ The |@/do@+{| loop here preserves the following invariant relations
+between |f|, |p|, and~|q|:
+(i)~|0 <= p < q|; (ii)~$fq+p=2^k(q+p_0)$, where $k$ is an integer and
+$p_0$ is the original value of~$p$.
+
+Notice that the computation specifies
+|(p-q)+p| instead of |(p+p)-q|, because the latter could overflow.
+Let us hope that optimizing compilers do not miss this point; a
+special variable |be_careful| is used to emphasize the necessary
+order of computation. Optimizing compilers should keep |be_careful|
+in a register, not store it in memory.
+@^inner loop@>
+
+@<Compute $f=\lfloor 2^{28}(1+p/q)+{1\over2}\rfloor$@>=
+f=1;
+@/do@+{be_careful=p-q;p=be_careful+p;
+if (p >= 0) f=f+f+1;
+else{@+double(f);p=p+q;
+ }
+}@+ while (!(f >= mpfract_one));
+be_careful=p-q;
+if (be_careful+p >= 0) incr(f)
+
+@ The dual of |make_mpfract| is |take_mpfract|, which multiplies a
+given integer~|q| by a fraction~|f|. When the operands are positive, it
+computes $p=\lfloor qf/2^{28}+{1\over2}\rfloor$, a symmetric function
+of |q| and~|f|.
+
+@<Declare \Prote\ arithmetic routines@>=
+static int take_mpfract(int @!q, mpfract @!f)
+{@+int @!p; /*the fraction so far*/
+bool @!negative; /*should the result be negated?*/
+int @!n; /*additional multiple of $q$*/
+int @!be_careful; /*disables certain compiler optimizations*/
+@<Reduce to the case that |f>=0| and |q>0|@>;
+if (f < mpfract_one) n=0;
+else{@+n=f/mpfract_one;f=f%mpfract_one;
+ if (q <= el_gordo/n) n=n*q;
+ else{@+arith_error=true;n=el_gordo;
+ }
+ }
+f=f+mpfract_one;
+@<Compute $p=\lfloor qf/2^{28}+{1\over2}\rfloor-q$@>;
+be_careful=n-el_gordo;
+if (be_careful+p > 0)
+ {@+arith_error=true;n=el_gordo-p;
+ }
+if (negative) return-(n+p);
+else return n+p;
+}
+
+@ @<Reduce to the case that |f>=0| and |q>0|@>=
+if (f >= 0) negative=false;
+else{@+negate(f);negative=true;
+ }
+if (q < 0)
+ {@+negate(q);negative=!negative;
+ }
+
+@ The invariant relations in this case are (i)~$\lfloor(qf+p)/2^k\rfloor
+=\lfloor qf_0/2^{28}+{1\over2}\rfloor$, where $k$ is an integer and
+$f_0$ is the original value of~$f$; (ii)~$2^k\L f<2^{k+1}$.
+@^inner loop@>
+
+@<Compute $p=\lfloor qf/2^{28}+{1\over2}\rfloor-q$@>=
+p=mpfract_half; /*that's $2^{27}$; the invariants hold now with $k=28$*/
+if (q < mpfract_four)
+ @/do@+{if (odd(f)) p=halfp(p+q);@+else p=halfp(p);
+ f=halfp(f);
+ }@+ while (!(f==1));
+else@/do@+{if (odd(f)) p=p+halfp(q-p);@+else p=halfp(p);
+ f=halfp(f);
+ }@+ while (!(f==1))
+
+@ There's an auxiliary array |randoms| that contains 55 pseudo-random
+fractions. Using the recurrence $x_n=(x_{n-55}-x_{n-31})\bmod 2^{28}$,
+we generate batches of 55 new $x_n$'s at a time by calling |new_randoms|.
+The global variable |j_random| tells which element has most recently
+been consumed.
+
+@<Glob...@>=
+static mpfract @!randoms[55]; /*the last 55 random values generated*/
+static int @!j_random; /*the number of unused |randoms|*/
+
+@ This array of pseudo-random numbers is set starting from a seed value,
+that is kept in the global integer |random_seed|.
+
+@<Global...@>=
+static int @!random_seed; /*seed for pseudo-random number generation*/
+
+@ @<Generate all \Prote\ primitives@>=
+if (pdf_on)
+primitive("pdfrandomseed", last_item, random_seed_code);
+@!@:random\_seed\_}{\.{\\pdfrandomseed} primitive@>
+else
+primitive("randomseed", last_item, random_seed_code);@/
+@!@:randomseed\_}{\.{\\randomseed} primitive@>
+
+
+@ @<Cases of |last_item| for |print_cmd_chr|@>=
+case random_seed_code:
+ if (pdf_on) print_esc("pdfrandomseed");
+ else print_esc("randomseed");
+ @+break;
+
+@ @<Cases for fetching a \Prote\ int value@>=
+case random_seed_code: cur_val=random_seed;@+break;
+
+@ We set the initial value from the system time. System integrators
+could provide a better source of pseudo-randomness.
+
+Every time a new seed value is assigned, the array has to be
+regenerated for consumption by routines explained a little later.
+
+@<\Prote\ initializations@>=
+random_seed=sys_time;
+init_randoms();
+
+@ Since changing the value must trigger the redefinition of the array,
+a dedicated primitive is defined to take the new seed and call
+|init_randoms|.
+
+@<Generate all \Prote\ primitives@>=
+if (pdf_on)
+primitive("pdfsetrandomseed", convert, set_random_seed_code);@/
+@!@:set\_random\_seed\_code}{\.{\\pdfsetrandomseed} primitive@>
+else
+primitive("setrandomseed", convert, set_random_seed_code);@/
+@!@:setrandomseed\_}{\.{\\setrandomseed} primitive@>
+
+
+@ @<Cases of |convert| for |print_cmd_chr|@>=
+case set_random_seed_code:
+ if (pdf_on) print_esc("pdfsetrandomseed");
+ else print_esc("setrandomseed");
+ @+break;
+
+@ Once we have retrieved and redefined |random_seed|, we must regenerate
+the |randoms| array.
+
+@<Cases of `Scan the argument for command |c|'@>=
+case set_random_seed_code: {@+scan_int();random_seed=cur_val;
+ init_randoms();
+ } @+break;
+
+@ @<Cases of `Print the result of command |c|'@>=
+case set_random_seed_code: print_int(random_seed);@+break;
+
+@ To consume a random fraction, the program below will say `|next_random|'
+and then it will fetch |randoms[j_random]|.
+
+@d next_random if (j_random==0) new_randoms();
+ else decr(j_random)
+
+@<Declare \Prote\ arithmetic routines@>=
+static void new_randoms(void)
+{@+int @!k; /*index into |randoms|*/
+int @!x; /*accumulator*/
+for (k=0; k<=23; k++)
+ {@+x=randoms[k]-randoms[k+31];
+ if (x < 0) x=x+mpfract_one;
+ randoms[k]=x;
+ }
+for (k=24; k<=54; k++)
+ {@+x=randoms[k]-randoms[k-24];
+ if (x < 0) x=x+mpfract_one;
+ randoms[k]=x;
+ }
+j_random=54;
+}
+
+@ To initialize the |randoms| table, we call the following routine.
+
+@<Declare \Prote\ arithmetic routines@>=
+static void init_randoms(void)
+{@+mpfract @!j, @!jj, @!k; /*more or less random integers*/
+int @!i; /*index into |randoms|*/
+j=abs(random_seed);
+while (j >= mpfract_one) j=halfp(j);
+k=1;
+for (i=0; i<=54; i++)
+ {@+jj=k;k=j-k;j=jj;
+ if (k < 0) k=k+mpfract_one;
+ randoms[(i*21)%55]=j;
+ }
+new_randoms();new_randoms();new_randoms(); /*``warm up'' the array*/
+}
+
+@ To produce a uniform random number in the range |0 <= u < x| or |0 >= u > x|
+or |0==u==x|, given a |scaled| value~|x|, we proceed as shown here.
+
+Note that the call of |mult_integers| will produce the values 0 and~|x|
+with about half the probability that it will produce any other particular
+values between 0 and~|x|, because it rounds its answers.
+
+@<Declare \Prote\ arithmetic routines@>=
+static scaled unif_rand(scaled @!x)
+{@+scaled @!y; /*trial value*/
+next_random;y=take_mpfract(abs(x), randoms[j_random]);
+if (y==abs(x)) return 0;
+else if (x > 0) return y;
+else return-y;
+}
+
+@ This can be used by calling the following primitive.
+
+@<Generate all \Prote\ primitives@>=
+if (pdf_on)
+primitive("pdfuniformdeviate", convert, uniform_deviate_code);@/
+@!@:uniform\_deviate\_}{\.{\\pdfuniformdeviate} primitive@>
+else
+primitive("uniformdeviate", convert, uniform_deviate_code);@/
+@!@:uniformdeviate\_}{\.{\\uniformdeviate} primitive@>
+
+
+@ @<Cases of |convert| for |print_cmd_chr|@>=
+case uniform_deviate_code:
+ if (pdf_on) print_esc("pdfuniformdeviate");
+ else print_esc("uniformdeviate");
+ @+break;
+
+@ It takes one integer argument obviously that will be the argument
+to the function.
+
+@<Cases of `Scan the argument for command |c|'@>=
+case uniform_deviate_code: {@+scan_int();
+ cur_val=unif_rand(cur_val);
+ } @+break;
+
+@ @<Cases of `Print the result of command |c|'@>=
+case uniform_deviate_code: print_int(cur_val);@+break;
+
+@ The following somewhat different subroutine tests rigorously if $ab$ is
+greater than, equal to, or less than~$cd$,
+given integers $(a,b,c,d)$. In most cases a quick decision is reached.
+The result is $+1$, 0, or~$-1$ in the three respective cases.
+
+@d return_sign(A) {@+return A;
+ }
+
+@<Declare \Prote\ arithmetic routines@>=
+static int ab_vs_cd(int @!a, int b, int c, int d)
+{@+
+int @!q, @!r; /*temporary registers*/
+@<Reduce to the case that |a,c>=0|, |b,d>0|@>;
+loop@+{@+q=a/d;r=c/b;
+ if (q!=r)
+ if (q > r) return_sign(1)@;@+else return_sign(-1);
+ q=a%d;r=c%b;
+ if (r==0)
+ if (q==0) return_sign(0)@;@+else return_sign(1);
+ if (q==0) return_sign(-1);
+ a=b;b=q;c=d;d=r;
+ } /*now |a > d > 0| and |c > b > 0|*/
+}
+
+@ @<Reduce to the case that |a...@>=
+if (a < 0)
+ {@+negate(a);negate(b);
+ }
+if (c < 0)
+ {@+negate(c);negate(d);
+ }
+if (d <= 0)
+ {@+if (b >= 0)
+ if (((a==0)||(b==0))&&((c==0)||(d==0))) return_sign(0)@;
+ else return_sign(1);
+ if (d==0)
+ if (a==0) return_sign(0)@;@+else return_sign(-1);
+ q=a;a=c;c=q;q=-b;b=-d;d=q;
+ }
+else if (b <= 0)
+ {@+if (b < 0) if (a > 0) return_sign(-1);
+ if (c==0) return_sign(0)@;else return_sign(-1);
+ }
+
+@ Finally, a normal deviate with mean zero and unit standard deviation
+can readily be obtained with the ratio method (Algorithm 3.4.1R in
+{\sl The Art of Computer Programming\/}).
+
+@<Declare \Prote\ arithmetic routines@>=
+static scaled norm_rand(void)
+{@+int @!x, @!u, @!l; /*what the book would call $2^{16}X$, $2^{28}U$,
+ and $-2^{24}\ln U$*/
+@/do@+{
+ @/do@+{next_random;
+ x=take_mpfract(112429, randoms[j_random]-mpfract_half);
+ /*$2^{16}\sqrt{8/e}\approx 112428.82793$*/
+ next_random;u=randoms[j_random];
+ }@+ while (!(abs(x) < u));
+x=make_mpfract(x, u);
+l=139548960-m_log(u); /*$2^{24}\cdot12\ln2\approx139548959.6165$*/
+}@+ while (!(ab_vs_cd(1024, l, x, x) >= 0));
+return x;
+}
+
+@ This can be used by calling the following primitive.
+
+@<Generate all \Prote\ primitives@>=
+if (pdf_on)
+primitive("pdfnormaldeviate", convert, normal_deviate_code);@/
+@!@:normal\_deviate\_}{\.{\\pdfnormaldeviate} primitive@>
+else
+primitive("normaldeviate", convert, normal_deviate_code);@/
+@!@:normaldeviate\_}{\.{\\normaldeviate} primitive@>
+
+
+@ @<Cases of |convert| for |print_cmd_chr|@>=
+case normal_deviate_code:
+ if (pdf_on) print_esc("pdfnormaldeviate");
+ else print_esc("normaldeviate");
+ @+break;
+
+
+@ @<Cases of `Scan the argument for command |c|'@>=
+case normal_deviate_code: cur_val=norm_rand();@+break;
+
+@ @<Cases of `Print the result of command |c|'@>=
+case normal_deviate_code: print_int(cur_val);@+break;
+
+@*1 DVI related primitives.
+
+These primitives are related to positions in the DVI output.
+
+The \TeX\ and DVI system coordinates relate to an origin that is at the
+upper left corner. The \TeX\ coordinates are computed relative to an
+origin that has $(0,0)$ coordinates. Coordinates grow then rightward and
+downward. This is the {\sl page} coordinates relative to what is
+typeset (what \TeX\ is dealing with).
+
+But this typesetting material has to be put on what we will call {\sl
+paper}. The material put into shape by \TeX\ is put on the paper. On
+this paper, where will be put the \TeX\ origin? It is considered to be
+$1in$ at the right and $1in$ down from the upper left corner of the
+paper (see m.590, alinea 2).
+
+@d DVI_std_x_offset 4736286 /*1 inch in sp*/
+@d DVI_std_y_offset 4736286 /*1 inch in sp*/
+
+@ But the paper size is not specified in the DVI file and is not being
+dealt with by \TeX.
+
+In order to have a common reference point, and since the \.{\\lastxpos}
+and \.{\\lastypos} primitives originated in pdf\TeX, these two
+primitives give positions, in scaled points, relative to the lower left
+corner of the paper.
+
+Hence the need, for these primitive, to define the paper size, with the
+(misnamed) \.{\\pagewidth} and \.{\\pageheight}.
+
+\.{\\pagewidth} and \.{\\pageheight} are dimension parameters,
+initialized to $0$ by the generic \TeX\ code.
+
+@<Generate all \Prote\ primitives@>=
+if (!pdf_on) {
+primitive("pdfpagewidth", assign_dimen, dimen_base+page_width_code);@/
+@!@:pdf\_page\_width\_}{\.{\\pdfpagewidth} primitive@>
+primitive("pdfpageheight", assign_dimen, dimen_base+page_height_code);@/
+@!@:pdf\_page\_height\_}{\.{\\pdfpageheight} primitive@>
+} else {
+primitive("pagewidth", assign_dimen, dimen_base+page_width_code);@/
+@!@:pagewidth\_}{\.{\\pagewidth} primitive@>
+primitive("pageheight", assign_dimen, dimen_base+page_height_code);@/
+@!@:pageheight\_}{\.{\\pageheight} primitive@>
+}
+
+
+
+@ When instructed to, the \.{h} and \.{v} last values are transformed,
+in the coordinates system defined above and saved in the global
+variables |last_saved_xpos| and |last_saved_ypos|. They are
+initialized to $0$ and we do not make any verification that a
+call to the \.{\\savepos} primitive---to come---has been made before
+retrieving their values.
+
+@<Global...@>=
+static scaled @!last_saved_xpos, last_saved_ypos; /*last (x,y) DVI pos saved*/
+
+@ @<\Prote\ initializations@>=
+last_saved_xpos=0;last_saved_ypos=0;
+
+@ @<Set |last_saved_xpos| and |last_saved_ypos| with transformed coordinates@>=
+last_saved_xpos=cur_h+DVI_std_x_offset;
+last_saved_ypos=page_height-(cur_v+DVI_std_y_offset);
+
+@ @<Generate all \Prote\ primitives@>=
+if (!pdf_on) {
+primitive("pdflastxpos", last_item, last_xpos_code);@/
+@!@:pdflastxpos\_}{\.{\\pdflastxpos} primitive@>
+primitive("lastypos", last_item, last_ypos_code);@/
+@!@:pdflastypos\_}{\.{\\pdflastypos} primitive@>
+} else {
+primitive("lastxpos", last_item, last_xpos_code);@/
+@!@:lastxpos\_}{\.{\\lastxpos} primitive@>
+primitive("lastypos", last_item, last_ypos_code);@/
+@!@:lastypos\_}{\.{\\lastypos} primitive@>
+}
+
+@ @<Cases of |last_item| for |print_cmd_chr|@>=
+case last_xpos_code:
+ if (pdf_on) print_esc("pdflastxpos");
+ else print_esc("lastxpos");
+ @+break;
+case last_ypos_code:
+ if (pdf_on) print_esc("pdflastypos");
+ else print_esc("lastypos");
+ @+break;
+
+@ @<Cases for fetching a \Prote\ int value@>=
+case last_xpos_code: cur_val=last_saved_xpos;@+break;
+case last_ypos_code: cur_val=last_saved_ypos;
+
+@ |last_saved_xpos| and |last_saved_ypos| are only defined when
+instructed to by the call the the \.{\\savepos} primitive. Since the
+real work has to be done at \.{shipout} time, it is a case to be treated
+like the \.{\\special} primitive, that is it belongs to the
+\.{extension} class.
+
+We will add something more in the handling of the primitive: it will
+insert a \.{whatsit} in the DVI file so that one, using the program
+|dvitype|, could retrieve more than one {\it hic}. So there is a counter
+incremented whenever the primitive is called.
+
+@<Global...@>=
+static int last_save_pos_number; /*identifying the order of the call*/
+
+@ @<\Prote\ initializations@>=
+last_save_pos_number=0; /*i.e. none*/
+
+@ @<Generate all \Prote\ primitives@>=
+if (pdf_on)
+primitive("pdfsavepos", extension, save_pos_code);@/
+@!@:pdf\_save\_pos\_}{\.{\\pdfsavepos} primitive@>
+else
+primitive("savepos", extension, save_pos_code);@/
+@!@:savepos\_}{\.{\\savepos} primitive@>
+
+
+@ @<Cases of |extension| for |print_cmd_chr|@>=
+case save_pos_code:
+ if (pdf_on) print_esc("pdfsavepos");
+ else print_esc("savepos");
+ @+break;
+@ @<Cases for |do_extension|@>=
+case save_pos_code: @<Implement \.{\\savepos}@>@;@+break;
+
+@ We need the basic two words node, since we don't pass any parameter
+and it is just an instruction to do something. So the \.{whatsit} node
+is just the call.
+
+@<Implement \.{\\savepos}@>=
+{@+new_whatsit(save_pos_code, small_node_size);write_stream(tail)=null;
+ write_tokens(tail)=null;
+ }
+
+@ @<Cases for displaying the |whatsit| node@>=
+case save_pos_code:
+ if (pdf_on) print_esc("pdfsavepos");
+ else print_esc("savepos");
+ @+break;
+@ @<Cases for making a partial copy of the whatsit node@>=
+case save_pos_code: {@+r=get_node(small_node_size);
+ words=small_node_size;
+ } @+break;
+
+@ @<Cases for wiping out the whatsit node@>=
+case save_pos_code: free_node(p, small_node_size);@+break;
+
+@ So, after these trivial initializations, what will we effectively do?
+When the following procedure will be called, we define |last_saved_xpos|,
+|last_saved_ypos|, increment |last_save_pos_number|, and a |warning|
+followed by three
+|key==value| space separated definitions as a \.{\\special}, the first
+being prefixed by the string |__PROTE_| (shall be considered a reserved
+prefix) and the string |SAVEPOS_|, equal to the index of the call, and
+the |XPOS| and |YPOS| definitions.
+
+This is obviously, from the previous description, a variation around
+|special_out|.
+
+@<Declare procedures needed in |out_what|@>=
+static void save_pos_out(pointer @!p)
+{@+int old_setting; /*holds print |selector|*/
+int @!k; /*index into |str_pool|*/
+synch_h;synch_v;incr(last_save_pos_number);
+@<Set |last_saved_xpos| and |last_saved_ypos|...@>@;
+old_setting=selector;selector=new_string;
+print("warning __PROTE_");print("SAVEPOS");print_char('=');
+print_int(last_save_pos_number);print_char(' ');
+print("XPOS");print("=");print_int(last_saved_xpos);print_char(' ');
+print("YPOS");print("=");print_int(last_saved_ypos);
+selector=old_setting;
+str_room(1); /*abort if probably overflowed and truncated*/
+dvi_out(xxx1);dvi_out(cur_length); /*it's less than 256*/
+for (k=str_start[str_ptr]; k<=pool_ptr-1; k++) dvi_out(so(str_pool[k]));
+pool_ptr=str_start[str_ptr]; /*forget the not commited tentative string*/
+}
+
+@ @<Cases for |out_what|@>=
+case save_pos_code: save_pos_out(p);@+break;
+
+@* System-dependent changes.
+This section should be replaced, if necessary, by any special
+modifications of the program
+that are necessary to make \TeX\ work at a particular installation.
+It is usually best to design your change file so that all changes to
+previous sections preserve the section numbering; then everybody's version
+will be consistent with the published program. More extensive changes,
+which introduce new sections, can be inserted here; then only the index
+itself will get a new section number.
+@^system dependencies@>
+
+@* \TeX\ Live Integration.
+A \TeX\ engine that aspires
+to become a member of the \TeX\ Live family of programs
+must
+
+$-$ respect the \TeX\ Live conventions for command line parameters,\par
+$-$ find its input files using the \.{kpathsearch} library, and\par
+$-$ implement \TeX\ primitives to support
+\LaTeX.
+
+\noindent Naturally, the functions that follow here are taken, with small
+modifications, from the \TeX\ Live sources. What is
+added here, or rather subtracted here, are the parts that are specific
+to some of the \TeX\ engines included in \TeX\ Live. New is also
+that the code is presented in literate programming style.
+
+The code that follows is organized in three parts.
+Some code for \TeX\ Live must come before
+the definition of \TeX's macros because
+it uses include files containing identifiers that are in conflict
+with \TeX's macros or modify these macros. For example
+\TeX's |banner| is modified by adding the \TeX\ Live version.
+
+@<Header files and function declarations@>=
+#ifdef WEB2CVERSION
+#define TL_VERSION "(TeX Live "WEB2CVERSION")"
+#else
+#define TL_VERSION
+#endif
+
+@ The remaining two parts are first auxiliary variables and functions and then
+those functions that are called from the ``classic'' \TeX\ code.
+
+@p @<\TeX\ Live auxiliary variables@>@;
+ @<\TeX\ Live auxiliary functions@>@;
+ @<\TeX\ Live functions@>@;
+
+@
+Most of the code that we present next comes together in the
+function |main_init|
+which is the first function called in the main program of a \TeX\ engine
+belonging to \TeX\ Live. Before doing so, we make copies of
+argument count and argument vector putting them in global variables.
+@<Global...@>=
+static char **argv;
+static int argc;
+
+@ @<\TeX\ Live functions@>=
+static void main_init(int ac, char *av[])
+{@+
+ char* main_input_file;
+ argc = ac;
+ argv = av;
+ interaction = error_stop_mode;
+ kpse_record_input = recorder_record_input;
+ kpse_record_output = recorder_record_output;
+
+ @<parse options@>@;
+ @<set the program and engine name@>@;
+ @<activate configuration lines@>@;
+ @<set the input file name@>@;
+ @<set defaults from the {\tt texmf.cfg} file@>@;
+ @<set the format name@>@;
+ @<enable the generation of input files@>@;
+}
+
+@ @<Forward declarations@>=
+static void main_init(int ac, char *av[]);
+
+
+@*1 Command Line.
+ Let's begin with the beginning: the command line.
+To see how a command line is structured, we first look at the
+help text that is displayed if the user asks for it (or if \TeX\
+decides that the user needs it). The help text is produced by the
+function |usage_help|.
+\def\SP{ }% disable visible spaces in strings.
+
+@<\TeX\ Live auxiliary functions@>=
+static void usage_help(void)
+{@+@<explain the command line@>@;
+ fprintf(stdout,"Options:\n"@,@[@<explain the options@>@]);
+ fprintf(stdout,"\nFor further information and reporting bugs see https://hint.userweb.mwn.de/\n");
+ exit(0);
+}
+
+@ The command line comes in three slightly different versions:
+
+@<explain the command line@>=
+ fprintf(stdout,@/
+ "Usage: %s [OPTION]... [TEXNAME[.tex]] [COMMANDS]\n"@/
+ " or: %s [OPTION]... \\FIRST-LINE\n"@/
+ " or: %s [OPTION]... &FMT ARGS\n\n",@/
+ argv[0],argv[0],argv[0]);@/
+ fprintf(stdout,@/
+ " Run TeX on TEXNAME, creating TEXNAME.dvi.\n"@/
+ " Any remaining COMMANDS are processed\n"@/
+ " as TeX input after TEXNAME is read.\n"@/
+ " If the first line of TEXNAME starts with %%&FMT, and FMT is\n"@/
+ " an existing .fmt file, use it. Else use `NAME.fmt', where\n"@/
+ " NAME is the program invocation name.\n"@/
+ "\n"@/
+ " Alternatively, if the first non-option argument begins\n"@/
+ " with a backslash, interpret all non-option arguments as\n"@/
+ " a line of TeX input.\n"@/
+ "\n"@/
+ " Alternatively, if the first non-option argument begins\n"@/
+ " with a &, the next word is taken as the FMT to read,\n"@/
+ " overriding all else. Any remaining arguments are\n"@/
+ " processed as above.\n"@/
+ "\n"@/
+ " If no arguments or options are specified, prompt for input.\n"@/
+ "\n");
+
+
+@*1 Options.
+Here is the list of possible options and their explanation:
+
+@<explain the options@>=
+ " -help "@/
+ @t\qquad@>"\t display this help and exit\n"@/
+ " -version "@/
+ @t\qquad@>"\t output version information and exit\n"@/
+ " -etex "@/
+ @t\qquad@>"\t enable e-TeX extensions\n"@/
+ " -ltx "@/
+ @t\qquad@>"\t enable LaTeX extensions, implies -etex\n"@/
+ " -ini "@/
+ @t\qquad@>"\t be initex for dumping formats; this is\n"@/
+ @t\qquad@>"\t\t\t also true if the program name is `kinitex'\n"@/
+ " -progname=STRING "@/
+ @t\qquad@>"\t set program (and fmt) name to STRING\n"@/
+ " -fmt=FMTNAME "@/
+ @t\qquad@>"\t use FMTNAME instead of program name or a %%& line\n"@/
+ " -output-directory=DIR "@/
+ @t\qquad@>"\t use existing DIR as the directory to write files to\n"@/
+ " -jobname=STRING "@/
+ @t\qquad@>"\t set the TeX \\jobname to STRING\n"@/
+ " [-no]-mktex=FMT "@/
+ @t\qquad@>"\t disable/enable mktexFMT generation (FMT=tex/tfm/fmt)\n"@/
+ " -interaction=STRING "@/
+ @t\qquad@>"\t set interaction mode (STRING=batchmode/\n"@/
+ @t\qquad@>"\t\t\t nonstopmode/scrollmode/errorstopmode)\n"@/
+ " -kpathsea-debug=NUMBER"@/
+ @t\qquad@>"\t set path searching debugging flags according\n"@/
+ @t\qquad@>"\t\t\t to the bits of NUMBER\n"@/
+ " -recorder"@/
+ @t\qquad@>"\t\t enable filename recorder\n"@/
+ " [-no]-parse-first-line"@/
+ @t\qquad@>"\t disable/enable parsing of the first line of\n"@/
+ @t\qquad@>"\t\t\t the input file\n"@/
+ " [-no]-file-line-error"@/
+ @t\qquad@>"\t disable/enable file:line:error style\n"@/
+ " -cnf-line=STRING"@/
+ @t\qquad@>"\t process STRING like a line in texmf.cnf\n"@/
+
+
+@ The processing of command line options is controlled by the |long_options|
+\def\SP{{\tt\char`\ }}% restore the visible space in a string
+array. Each entry in this array contains first the name of the option,
+then a flag that tells whether the option takes an argument or not.
+If next the (optional) address of a flag variable is given,
+it is followed by the value to store in the flag variable.
+In this case, setting the flag variable is handled by
+the |getopt_long_only| function.
+
+Besides the flag variables that occur in the table,
+a few string variables may be set using the options.
+The following is a complete list of these variables.
+Variables are initialized with |-1| to indicate an undefined value;
+string variables are initialized with |NULL|.
+
+@<Global variables@>=
+static int iniversion=false, etexp=false, ltxp=false, recorder_enabled=false;
+static int parsefirstlinep=-1, filelineerrorstylep=-1, interaction_option=-1;
+static const char *user_progname=NULL, *output_directory=NULL, *c_job_name=NULL,
+ *dump_name=NULL;@#
+
+@ @<\TeX\ Live auxiliary variables@>=
+static struct option long_options[] = {@/
+ { "help", 0, 0, 0 },@/
+ { "version", 0, 0, 0 },@/
+ { "interaction", 1, 0, 0 },@/
+ { "mktex", 1, 0, 0 },@/
+ { "no-mktex", 1, 0, 0 },@/
+ { "kpathsea-debug", 1, 0, 0 },@/
+ { "progname", 1, 0, 0 },@/
+ { "fmt", 1, 0, 0 },@/
+ { "output-directory", 1, 0, 0 },@/
+ { "jobname", 1, 0, 0 },@/
+ { "cnf-line", 1, 0, 0 },@/
+ { "ini", 0, &iniversion, 1 },@/
+ { "etex", 0, &etexp, 1 },@/
+ { "ltx", 0, &ltxp, 1 },@/
+ { "recorder", 0, &recorder_enabled, 1 },@/
+ { "parse-first-line", 0, &parsefirstlinep, 1 },@/
+ { "no-parse-first-line", 0, &parsefirstlinep, 0 },@/
+ { "file-line-error", 0, &filelineerrorstylep, 1 },@/
+ { "no-file-line-error", 0, &filelineerrorstylep, 0 },@/
+ @<more options@>@/
+ { 0, 0, 0, 0 }@+}@+;
+
+
+@ Parsing the command line options is accomplished with the
+|parse_options| function which in turn uses the |getopt_long_only|
+function from the \CEE/ library. This function returns 0 and sets the
+|option_index| parameter to the option found, or it returns $-1$ if
+the end of all options is reached.
+@<\TeX\ Live functions@>=
+static void parse_options (int argc, char *argv[])
+{@+ while (true) {
+ int option_index;
+ int g = getopt_long_only (argc, argv, "+", long_options, &option_index);
+ if (g==0)
+ { @<handle the options@>@;@+ }
+ else if (g == '?')
+ { fprintf(stderr,"Try '%s --help' for more information\n",argv[0]);
+ exit(1);
+ }
+ else if (g == -1) break;
+ }
+ @<Check the environment for extra settings@>@;
+}
+
+@ @<Forward declarations@>=
+static void parse_options (int argc, char *argv[]);
+
+@ Before we can call the |parse_options| function,
+we might need some special preparations for Windows.
+
+@<parse options@>=
+#if defined(WIN32)
+{@+ char* enc;
+ kpse_set_program_name (argv[0], NULL);
+ enc = kpse_var_value("command_line_encoding");
+ get_command_line_args_utf8(enc, &argc, &argv);@/
+ parse_options (argc, argv);
+ @<record {\tt texmf.cnf}@>@;
+}
+#else
+ parse_options (ac, av);
+#endif
+
+
+@ To handle the options, we compare the name at the given |option_index| with
+the different option names. This is not a very efficient method, but the
+impact is low and it's simple to write.
+
+Comparing the name of the argument with the |name| field in the |option|
+structure is done in the auxiliary function |argument_is|.
+Unfortunately the |name| field is in conflict with the |name| macro
+defined by \TeX. To avoid the conflict, the |argument_is| function
+goes just after the \.{kpathsea.h} header file that defines
+the option structure.
+
+
+@<Header files and function declarations@>=
+#include <kpathsea/kpathsea.h>
+static int argument_is(struct option *opt, char * s)
+{@+ return STREQ(opt->name, s); @+}
+#define ARGUMENT_IS(S) argument_is(long_options+option_index,S)
+
+@ Now we can handle the first two options:
+
+@<handle the options@>=
+if (ARGUMENT_IS("help")) usage_help();
+else if (ARGUMENT_IS("version")){@+
+ printf(TeX_banner@, "\n"@/
+ "Prote version "@, Prote_version_string@, "\n");
+ exit(0);@+
+}
+
+
+@ The ``interaction'' option sets the |interaction_option| variable
+based on its string argument contained in the |optarg| variable.
+If defined, the |interaction_option| will be used to set \TeX's
+|interaction| variable in the |initialize| and the |undump| functions.
+
+@<handle the options@>=
+else @+if (ARGUMENT_IS ("interaction"))@t\2@> {
+ if (STREQ (optarg, "batchmode")) interaction_option = batch_mode;
+ else if (STREQ (optarg, "nonstopmode")) interaction_option = nonstop_mode;
+ else if (STREQ (optarg, "scrollmode")) interaction_option = scroll_mode;
+ else if (STREQ (optarg, "errorstopmode")) interaction_option = error_stop_mode;
+ else WARNING1 ("Ignoring unknown argument `%s' to --interaction", optarg);
+ }
+
+@ The next two options pass the string argument to the
+\.{kpathsearch} library.
+
+@<handle the options@>=
+else @+if (ARGUMENT_IS ("mktex")) @t\2@> kpse_maketex_option (optarg, true);
+else if (ARGUMENT_IS ("no-mktex")) kpse_maketex_option (optarg, false);
+
+
+@ To debug the searching done by the \.{kpathsearch} library,
+the following option can be used.
+The argument value 3 is a good choice to start with.
+
+@<handle the options@>=
+else @+if (ARGUMENT_IS ("kpathsea-debug")) @t\2@>
+ kpathsea_debug |= atoi (optarg);
+
+
+
+@ The next set of options take a string argument
+and assign it to the corresponding string variable.
+
+@<handle the options@>=
+else @+if (ARGUMENT_IS ("progname"))@t\2@>
+ user_progname = normalize_quotes(optarg,"program name");
+else if (ARGUMENT_IS ("fmt"))
+ dump_name = normalize_quotes(optarg,"format name");
+else if (ARGUMENT_IS ("output-directory"))
+ output_directory = normalize_quotes(optarg,"output directory");
+else if (ARGUMENT_IS ("jobname"))
+ c_job_name = normalize_quotes (optarg, "job name");
+
+@ When string arguments specify files or directories,
+special care is needed if arguments are quoted and/or contain spaces.
+The function |normalize_quotes| makes sure that arguments containing
+spaces get quotes around them and it checks for unbalanced quotes.
+
+
+@<\TeX\ Live auxiliary functions@>=
+static char *normalize_quotes (const char *nom, const char *mesg)
+{@+ int quoted = false;
+ int must_quote = (strchr(nom, ' ') != NULL);
+ char *ret = xmalloc(strlen(nom)+3);/*room for two quotes and NUL*/
+ char *p=ret;
+ const char *q;
+
+ if (must_quote)
+ *p++ = '"';
+ for (q = nom; *q; q++)
+ if (*q == '"')
+ quoted = !quoted;@+
+ else
+ *p++ = *q;
+
+ if (must_quote)
+ *p++ = '"';
+ *p = '\0';
+ if (quoted) {
+ fprintf(stderr, "! Unbalanced quotes in %s %s\n", mesg, nom);
+ exit(1);
+ }
+ return ret;
+}
+
+@ If the output directory was specified on the command line,
+we save it in an environment variable so that subbrocesses can
+get the value. If on the other hand the environment specifies
+a directory and the command line does not, save the value from
+the environment to the global variable so that it is used in the
+rest of the code.
+
+@<Check the environment for extra settings@>=
+if (output_directory)
+ xputenv ("TEXMF_OUTPUT_DIRECTORY", output_directory);
+else if (getenv ("TEXMF_OUTPUT_DIRECTORY"))
+ output_directory = getenv ("TEXMF_OUTPUT_DIRECTORY");
+
+@*1 Passing a file name as a general text argument.
+
+|scan_file_name| uses the following code to parse a file name given
+as a general text argument. Such an argument can be any token list
+starting with a left brace and ending with a right brace.
+This token list is then expanded (without the leading and trailing braces)
+and printed into the string pool without making it yet an official string.
+After removing all double quotes, because this is current practice
+for \TeX\ engines that are part of \TeX\ Live,
+and setting the area and extension delimiters,
+all temporary garbage used so far is freed.
+
+Due to the expansion of the token list, this code and hence the
+|scan_file_name| procedure is recursive.
+One can provide the name of a file as the content of an other file.
+
+
+% Thierry Laronde
+@<Define a general text file name and |goto done|@>=
+{@+back_input();name_in_progress=false; /*this version is recursive...*/
+ cur_cs=input_loc; /*|scan_toks| will set |warning_index| from it*/
+ scan_general_x_text();
+ old_setting=selector;selector=new_string;
+ token_show(link(garbage));selector=old_setting;
+ @<Suppress double quotes in braced input file name@>@;
+ j=pool_ptr-1;while ((j >= str_start[str_ptr])&&(area_delimiter==0))
+ {@+
+ if ((str_pool[j]=='/')) area_delimiter=j-str_start[str_ptr];
+ if ((ext_delimiter==0)&&(str_pool[j]=='.'))
+ ext_delimiter=j-str_start[str_ptr];
+ decr(j);
+ }
+ flush_list(link(garbage));
+ goto done;
+}
+
+@ A simple loop removes the double quotes and adjusts the |pool_ptr|.
+
+@<Suppress double quotes in braced input file name@>=
+for (k=j=str_start[str_ptr]; k < pool_ptr; k++) {@+
+ if (str_pool[k]!='"') {@+str_pool[j]=str_pool[k];incr(j);}
+}
+pool_ptr=j;
+
+@*1 The {\tt -recorder} Option.
+The recorder option can be used to enable the file name recorder.
+It is crucial for getting a reliable list of files used in a given run.
+Many post-processors use it, and it is used in \TeX\ Live for
+checking the format building infrastructure.
+
+When we start the file name recorder,
+we would like to use mkstemp, but it is not portable,
+and doing the autoconfiscation (and providing fallbacks) is more
+than we want to cope with. So we have to be content with using a
+default name. We throw in the pid so at least parallel builds might
+work. Windows, however, seems to have no |pid_t|, so instead of storing the
+value returned by |getpid|, we immediately consume it.
+
+@<\TeX\ Live auxiliary functions@>=
+static char *recorder_name=NULL;
+static FILE *recorder_file=NULL;
+static void
+recorder_start(void)
+{ char *cwd;
+ char pid_str[MAX_INT_LENGTH];
+ sprintf (pid_str, "%ld", (long) getpid());
+ recorder_name = concat3(kpse_program_name, pid_str, ".fls");
+ if (output_directory) {
+ char *temp = concat3(output_directory, DIR_SEP_STRING, recorder_name);
+ free(recorder_name);
+ recorder_name = temp;
+ }
+ recorder_file = xfopen(recorder_name, FOPEN_W_MODE);
+ cwd = xgetcwd();
+ fprintf(recorder_file, "PWD %s\n", cwd);
+ free(cwd);
+}
+
+@ After we know the log file name, we have used |recorder_change_filename|
+to change the name of the recorder file to the usual thing.
+@<Forward declarations@>=
+static void recorder_change_filename (const char *new_name);
+
+@ Now its time to define this function.
+Unfortunately, we have to explicitly take
+the output directory into account, since the new name we are
+called with does not; it is just the log file name with {\tt .log}
+replaced by {\tt .fls}.
+
+@ @<\TeX\ Live auxiliary functions@>=
+static void
+recorder_change_filename (const char *new_name)
+{ char *temp = NULL;
+ if (!recorder_file)
+ return;
+#if defined(_WIN32)
+ fclose (recorder_file); /* An open file cannot be renamed. */
+#endif /* |_WIN32| */
+ if (output_directory) {
+ temp = concat3(output_directory, DIR_SEP_STRING, new_name);
+ new_name = temp;
+ }
+
+#if defined(_WIN32)
+ remove (new_name); /* A file with the |new_name| must not exist. */
+#endif /* |_WIN32| */
+ rename(recorder_name, new_name);
+ free(recorder_name);
+ recorder_name = xstrdup(new_name);
+#if defined(_WIN32)
+ recorder_file = xfopen (recorder_name, FOPEN_A_MODE); /* A closed file must be opened. */
+#endif /* |_WIN32| */
+ if (temp)
+ free (temp);
+}
+
+@ Now we are ready to record file names. The prefix INPUT is added
+to an input file and the prefix OUTPUT to an output file.
+But both functions for recording a file name use the same
+function otherwise, which on first use will start the recorder.
+
+@<\TeX\ Live auxiliary functions@>=
+static void
+recorder_record_name (const char *pfx, const char *fname)
+{ @+if (recorder_enabled) {
+ if (!recorder_file)
+ recorder_start();
+ fprintf(recorder_file, "%s %s\n", pfx, fname);
+ fflush(recorder_file);
+ }
+}
+
+
+static void
+recorder_record_input (const char *fname)
+{ recorder_record_name ("INPUT", fname);
+}
+
+static void
+recorder_record_output (const char * fname)
+{ recorder_record_name ("OUTPUT", fname);
+}
+
+@ Because input files are also recorded when writing the optional sections,
+we need the following declaration.
+
+@<Forward declarations@>=
+static void recorder_record_input (const char *fname);
+
+@ In WIN32, texmf.cnf is not recorded in
+the case of {\tt -recorder}, because |parse_options| is executed
+after the start of kpathsea due to special initializations.
+Therefore we record {\tt texmf.cnf} with the following code:
+
+@<record {\tt texmf.cnf}@>=
+if (recorder_enabled) {
+ char **p = kpse_find_file_generic ("texmf.cnf", kpse_cnf_format, 0, 1);
+ if (p && *p) {
+ char **pp = p;
+ while (*p) {
+ recorder_record_input (*p);
+ free (*p);
+ p++;
+ }
+ free (pp);
+ }
+}
+
+
+
+
+@*1 The {\tt -cnf-line} Option.
+With the \.{-cnf-line} option it is possible to specify
+a line of text as if this line were part of \TeX's configuration
+file---even taking precedence over conflicting lines in the configuration
+file. For example it is possible to change \TeX's
+\.{TEXINPUTS} variable by saying \.{--cnf-line=TEXINPUTS=/foo}.
+The configuration lines are temporarily stored in the variable
+|cnf_lines| and counted in |cnf_count| because we can send them
+to the \.{kpathsearch} library only after the library has been
+initialized sufficiently.
+
+@<Global...@>=
+static char **cnf_lines=NULL;
+static int cnf_count=0;
+
+@ @<handle the options@>=
+else @+if (ARGUMENT_IS ("cnf-line"))
+ add_cnf_line(optarg);
+
+@ The function |add_cnf_line| stores the
+given command line argument in the variable |cnf_lines|.
+
+@<\TeX\ Live auxiliary functions@>=
+static void add_cnf_line(char *arg)
+{@+ cnf_count++;
+ cnf_lines=xrealloc(cnf_lines,sizeof(char*)*cnf_count);
+ cnf_lines[cnf_count-1]=arg;
+}
+
+@ To activate the configuration lines they are passed to the
+ \.{kpathsearch} library.
+
+@<activate configuration lines@>=
+#if 1 /* this function does not exists always */
+{@+ int i;
+ for (i=0;i<cnf_count;i++)
+ kpathsea_cnf_line_env_progname (kpse_def, cnf_lines[i]);
+ free(cnf_lines);
+}
+#endif
+
+
+@*1 The Input File.
+After we are done with the options,
+we inform the \.{kpathsearch} library about the program name.
+This is an important piece of information for the library
+because the library serves quite different programs
+and its behavior can be customized for each program
+using configuration files.
+After the program and engine name is set,
+the library is ready to use.
+@<set the program and engine name@>=
+if (!user_progname)
+ user_progname = dump_name;
+#if defined(WIN32)
+ if (user_progname)
+ kpse_reset_program_name (user_progname);
+#else
+ kpse_set_program_name (argv[0], user_progname);
+#endif
+ xputenv ("engine", "texprof");@/
+
+
+@ After the options, the command line usually continues with the name of the input
+file. Getting a hold of the input file name can be quite complicated,
+but the \.{kpathsearch} library will help us to do the job.
+
+We start by looking at the first argument after the options:
+If it does not start with an
+``\.{\AM}'' and neither with a ``\.{\BS}'', it's a simple file name.
+Under Windows, however, filenames might start with a drive letter
+followed by a colon and a ``\.{\BS}'' which is used to separate
+directory names. Finally, if the filename is a quoted string, we need
+to remove the quotes before we use the \.{kpathsearch} library to find it
+and reattach the quotes afterward.
+
+@<\TeX\ Live auxiliary functions@>=
+#ifdef WIN32
+static void clean_windows_filename(char *filename)
+{@+if (strlen (filename) > 2 && isalpha (filename[0]) &&
+ filename[1] == ':' && filename[2] == '\\')
+ { char *pp;
+ for (pp = filename; *pp; pp++)
+ if (*pp == '\\')
+ *pp = '/';
+ }
+}
+#endif
+
+static char *find_file(char *fname, kpse_file_format_type t, int mx)
+{@+char *filename;
+ int final_quote=(int)strlen(fname)-1;
+ int quoted = final_quote>1 && fname[0] == '"' && fname[final_quote]=='"';
+ if (quoted) {
+ /* Overwrite last quote and skip first quote. */
+ fname[final_quote] = '\0';
+ fname++;
+ }
+ filename = kpse_find_file(fname, t, mx);
+ if (full_name_of_file!=NULL)
+ { free(full_name_of_file); full_name_of_file=NULL;}
+ if (filename!=NULL)
+ full_name_of_file=strdup(filename);
+ if (quoted) {
+ /* Undo modifications */
+ fname--;
+ fname[final_quote] = '"';
+ }
+ return filename;
+}
+
+static char *get_input_file_name (void)
+{@+
+ char *input_file_name = NULL;
+
+ if (argv[optind] && argv[optind][0] != '&' && argv[optind][0] != '\\')
+ {
+#ifdef WIN32
+ clean_windows_filename(argv[optind]);
+#endif
+ argv[optind] = normalize_quotes(argv[optind], "input file");
+ input_file_name=find_file(argv[optind],kpse_tex_format, false);
+ }
+ return input_file_name;
+}
+
+
+@ After we called |get_input_file_name|, we might need to look at |argv[argc-1]|
+in case we run under Windows.
+
+@<set the input file name@>=
+ main_input_file = get_input_file_name ();
+
+#ifdef WIN32
+ /* Were we given a simple filename? */
+ if (main_input_file == NULL) {
+ char *file_name=argv[argc-1];
+ if (file_name && file_name[0] != '-' && file_name[0] != '&' && file_name[0] != '\\') {
+ clean_windows_filename(file_name);
+ file_name = normalize_quotes(file_name, "argument");
+ main_input_file=find_file(file_name,kpse_tex_format, false);
+ argv[argc-1] = file_name;
+ }
+ }
+#endif
+
+
+@ After we have an input file, we make an attempt at filling in options
+from the {\tt texmf.cfg} file.
+
+@<set defaults from the {\tt texmf.cfg} file@>=
+ if (filelineerrorstylep < 0)
+ filelineerrorstylep = texmf_yesno ("file_line_error_style");
+ if (parsefirstlinep < 0)
+ parsefirstlinep = texmf_yesno ("parse_first_line");@/@t~@>
+
+@ We needed:
+@<\TeX\ Live auxiliary functions@>=
+static int texmf_yesno(const char *var)
+{@+
+ char *value = kpse_var_value (var);
+ return value && (*value == 't' || *value == 'y' || *value == '1');
+}
+
+@ We need a stack, matching the |line_stack| that
+contains the source file names. For the full source filenames we use
+poiters to |char| because these names are just used for output.
+
+@<Global...@>=
+static char * @!source_filename_stack0[max_in_open]={NULL}, **const @!source_filename_stack = @!source_filename_stack0-1;
+static char * @!full_source_filename_stack0[max_in_open]={NULL}, **const @!full_source_filename_stack = @!full_source_filename_stack0-1;
+static char *full_name_of_file=NULL;
+
+@ The function |print_file_line|
+prints ``file:line:error'' style messages using
+the |source_filename_stack|. If it fails to find the file name, it
+falls back to the ``non-file:line:error'' style.
+
+@<Basic printing...@>=
+static void print_file_line(void)
+{@+int level=in_open;
+ while (level>0 && full_source_filename_stack[level]==NULL) level--;
+ if (level==0) print_nl("! ");
+ else
+ { print_nl(""); print(full_source_filename_stack[level]); print_char(':');
+ if (level==in_open) print_int(line);
+ else print_int(line_stack[level]);
+ print(": ");
+ }
+}
+
+@*1 The Format File.
+Most of the time \TeX\ is not running as \.{initex}
+but it runs with a format file preloaded.
+To set the format name, we first check if the format name was given on the
+command line with an ``\.{\AM}'' prefix,
+second we might check the first line of the input file,
+and last, we check if the program is an initex program.
+
+If we still don't have a format, the program
+name is our best guess.
+There is no need to check for an extension, because the
+\.{kpathsearch} library will take care of that.
+We store the format file name in |dump_name| which is
+used in the function |w_open_in| below.
+
+@<set the format name@>=
+if (parsefirstlinep && !dump_name)
+ parse_first_line (main_input_file);
+if (!main_input_file && argv[1] && argv[1][0] == '&')
+ dump_name = argv[1] + 1;
+if (strcmp(kpse_program_name, "initexprof")==0) iniversion = true;
+if (!dump_name)
+ dump_name = kpse_program_name;
+if (!dump_name)
+{ fprintf(stderr,"Unable to determine format name\n");
+ exit(1);
+}
+if (ltxp) etexp=1;
+if (etexp && !iniversion)
+{ fprintf(stderr,"-etex and -ltx require -ini\n");
+ exit(1);
+}
+
+@ Here is the function |parse_first_line |. It searches the first line of
+the file for a \TeX\ comment of the form ``\%\AM format''\footnote{${}^1$}{The idea of using this format came from Wlodzimierz Bzyl.}.
+If found, we will use the format given there.
+
+@<\TeX\ Live auxiliary functions@>=
+static void parse_first_line(char*filename)
+{@+ FILE *f=NULL;
+ if (filename==NULL) return;
+ f = open_in(filename,kpse_tex_format,"r");
+ if (f!=NULL)
+ { char *r,*s,*t=read_line (f);
+ xfclose (f, filename);
+ if (t==NULL) return;
+ s=t;
+ if (s[0] == '%' && s[1] == '&') {
+ s = s+2;
+ while ( ISBLANK(*s)) ++s;
+ r=s;
+ while (*s!=0 && *s!=' ' && *s!='\r' && *s!='\n') s++;
+ *s=0;
+ if (dump_name==NULL) {
+ char *f_name = concat (r, format_extension);
+ char *d_name = kpse_find_file (f_name,kpse_fmt_format, false);
+ if (d_name && kpse_readable_file (d_name)) {
+ dump_name = xstrdup (r);
+ kpse_reset_program_name (dump_name);
+ }
+ free (f_name);
+ }
+ }
+ free (t);
+ }
+}
+
+@*1 Commands.
+In the old days, \TeX\ was a \PASCAL\ program, and standard \PASCAL\
+did say nothing about a command line. So \TeX\ would open the terminal
+file for input and read all the information from the terminal.
+If you don't give \TeX\ command line arguments, this is still true today.
+In our present time, people got so much used to control the behavior
+of a program using command line arguments---especially when writing
+scripts---that \TeX\ Live allows the specification of commands on
+the command line which \TeX\ would normally expect on
+the first line of its terminal input.
+
+So our next task is
+writing a function to add the remainder of the
+command line to \TeX's input buffer.
+The main job is done by the |input_add_str| function
+which duplicates part of the |input_ln| function.
+Further it skips initial spaces and replaces trailing spaces
+and line endings by a single space.
+
+@<\TeX\ Live auxiliary functions@>=
+static void input_add_char(unsigned int c)
+{@+ if (last >= max_buf_stack)
+ {@+max_buf_stack=last+1;
+ if (max_buf_stack==buf_size)
+ @<Report overflow of the input buffer, and abort@>;
+ }
+ buffer[last]=xord[c];incr(last);
+}
+
+static void input_add_str(const char *str)
+{@+ int prev_last;
+ while (*str==' ') str++;
+ prev_last=last;
+ while (*str!=0)
+
+input_add_char(*str++);
+ for (--last; last >= first ; --last)
+ { char c =buffer[last];
+ if ((c) != ' ' && (c) != '\r' && (c) != '\n') break;
+ }
+ last++;
+ if (last>prev_last) input_add_char(' ');
+}
+
+static int input_command_line(void)
+{@+
+last=first;
+while (optind < argc) input_add_str(argv[optind++]);
+loc=first;
+return (loc < last);
+@+ }
+
+@ @<Forward declarations@>=
+static int input_command_line(void);
+
+
+@*1 Opening Files.
+When we open an output file, there is usually no searching necessary.
+In the best case, we have an absolute path and can open it.
+If the path is relative, we try in this order:
+the |file_name| prefixed by the |output_directory|,
+the |file_name| as is, and
+the |file_name| prefixed with the environment variable |TEXMFOUTPUT|.
+
+If we were successful with one of the modified names, we update
+|name_of_file|.
+
+@<\TeX\ Live functions@>=
+static FILE *open_out(const char *file_name, const char *file_mode)
+{@+ FILE *f=NULL;
+ char *new_name=NULL;
+ int absolute = kpse_absolute_p(file_name, false);
+ if (absolute)
+ { f=fopen(file_name,file_mode);
+ if (f!=NULL) recorder_record_output (file_name);
+ return f;
+ }
+ if (output_directory)
+ { new_name = concat3(output_directory, DIR_SEP_STRING, file_name);
+ f = fopen(new_name,file_mode);
+ if (f==NULL) {@+ free(new_name); new_name=NULL;@+}
+ }
+ if (f==NULL)
+ f = fopen(file_name,file_mode);
+ if (f==NULL)
+ { const char *texmfoutput = kpse_var_value("TEXMFOUTPUT");
+ if (texmfoutput!=NULL && texmfoutput[0]!=0)
+ { new_name = concat3(texmfoutput, DIR_SEP_STRING, file_name);@/
+ f = fopen(new_name,file_mode);
+ if (f==NULL) {@+ free(new_name); new_name=NULL; @+}
+ }
+ }
+ if (f!=NULL && new_name!=NULL)
+ update_name_of_file(new_name,(int)strlen(new_name));
+ if (f!=NULL) recorder_record_output ((char*)name_of_file+1);
+ if (new_name!=NULL) free(new_name);
+ return f;
+}
+
+static bool a_open_out(alpha_file *f) /*open a text file for output*/
+{@+f->f=open_out((char*)name_of_file+1,"w");
+ return f->f!=NULL && ferror(f->f)==0;@+
+}
+
+static bool b_open_out(byte_file *f) /*open a binary file for output*/
+{@+f->f=open_out((char *)name_of_file+1,"wb");
+return f->f!=NULL && ferror(f->f)==0;@+
+}
+
+#ifdef @!INIT
+static bool w_open_out(word_file *f) /*open a word file for output*/
+{@+f->f=open_out((char *)name_of_file+1,"wb");
+ return f->f!=NULL && ferror(f->f)==0;@+
+}
+#endif
+
+
+@ Format file names must be scanned
+before \TeX's string mechanism has been initialized.
+The function |update_name_of_file| will set |name_of_file| from
+a \CEE/ string.
+
+We dare not give error messages here, since \TeX\ calls this routine before
+the |error| routine is ready to roll. Instead, we simply drop excess characters,
+since the error will be detected in another way when a strange file name
+isn't found.
+@^system dependencies@>
+
+@<\TeX\ Live auxiliary functions@>=
+static void update_name_of_file(const char *@!s, int @!k)
+{@+ int j;
+ if (k <= file_name_size) name_length=k;@+
+ else name_length=file_name_size;
+ for (j=0; j<name_length;j++) name_of_file[j+1]=xchr[(int)s[j]];
+ name_of_file[name_length+1]=0;
+}
+
+@ In standard \TeX, the |reset| macro is used to open input files.
+The \.{kpathsearch} library uses different search paths for
+different types of files and therefore different functions are needed to
+open these files. The common code is in the function |open_in|.
+
+@<\TeX\ Live auxiliary functions@>=
+static FILE*open_in(char*filename,kpse_file_format_type t,const char*rwb)
+{@+ char *fname=NULL;
+ FILE*f= NULL;
+ fname= find_file(filename,t,true);
+ if(fname!=NULL)
+ {@+
+ f= fopen(fname,rwb);
+ if (f!=NULL) recorder_record_input(fname);
+ if (full_name_of_file!=NULL) free(full_name_of_file);
+ full_name_of_file=fname;@+
+ }
+ return f;
+}
+
+static bool a_open_in(alpha_file *f) /*open a text file for input*/
+{@+f->f= open_in((char *)name_of_file+1,kpse_tex_format,"r");
+ if (f->f!=NULL) get(*f);
+ return f->f!=NULL && ferror(f->f)==0;
+}
+
+static bool b_open_in(byte_file *f) /*open a binary file for input*/
+{@+f->f= open_in((char *)name_of_file+1,kpse_tfm_format,"rb");
+ if (f->f!=NULL) get(*f);
+ return f->f!=NULL && ferror(f->f)==0;
+}
+
+static bool w_open_in(word_file *f) /*open a word file for input*/
+{@+
+ f->f=NULL;
+ if (name_of_file[1]!=0)
+ f->f= open_in((char*)name_of_file+1,kpse_fmt_format,"rb");
+ if (f->f!=NULL) get(*f);
+ return f->f!=NULL && ferror(f->f)==0;
+}
+
+@ \TeX's |open_fmt_file| function will call the following function
+either with the name of a format file as given with an ``\.{\AM}''
+prefix in the input or with |NULL| if no such name was specified.
+The function will try |dump_name| as a last resort before returning |NULL|.
+
+
+
+@<\TeX\ Live functions@>=
+static bool open_fmt_file(void)
+{@+int j=loc;
+ if (buffer[loc]=='&')
+ {@+incr(loc);j=loc;buffer[last]=' ';
+ while (buffer[j]!=' ') incr(j);
+ update_name_of_file((char *)buffer+loc,j-loc);
+ if (w_open_in(&fmt_file)) goto found;
+ }
+ update_name_of_file(dump_name,(int)strlen(dump_name));
+ if (w_open_in(&fmt_file)) goto found;
+ name_of_file[1]=0;
+ wake_up_terminal;
+ wterm_ln("I can't find a format file!");
+ return false;
+found: loc=j;return true;
+}
+
+
+@ The \TeX\ Live infrastructure is able to generate format files,
+font metric files,
+and even some tex files, if required.
+
+@<enable the generation of input files@>=
+ kpse_set_program_enabled (kpse_tfm_format, MAKE_TEX_TFM_BY_DEFAULT,
+ kpse_src_compile);@/
+ kpse_set_program_enabled (kpse_tex_format, MAKE_TEX_TEX_BY_DEFAULT,
+ kpse_src_compile);@/
+ kpse_set_program_enabled (kpse_fmt_format, MAKE_TEX_FMT_BY_DEFAULT,
+ kpse_src_compile);@/
+
+
+
+@*1 Date and Time.
+We conclude this chapter using \.{time.h} to provide a function
+that is used to initialize
+\TeX's date and time information. Because |time| is one of \TeX's
+macros, we add the function |tl_now| before including \TeX's macros
+to wrap the call to the |time| function.
+It sets the variable |start_time| and returns a pointer to a |tm| structure
+to be used later in |fix_date_and_time|.
+
+To support reproducible output, the environment variable |SOURCE_DATE_EPOCH|
+needs to be checked. If it is set, it is an ASCII representation of
+a UNIX timestamp, defined as the number
+of seconds, excluding leap seconds, since 01 Jan 1970 00:00:00 UTC.
+Its value is then used to initialize the |start_time| variable.
+
+The \TeX\ Live conventions further require that setting the
+|FORCE_SOURCE_DATE| environment variable to $1$ will cause also \TeX's primitives
+\.{\\year}, \.{\\month}, \.{\\day}, and \.{\\time} to use this value
+as the current time. Looking at the \TeX\ Live code also reveals that
+these primitives use the local time instead of the GMT if this variable is
+not set to~1.
+
+@<Header files and function declarations@>=
+#include <time.h>
+static time_t start_time = ((time_t)-1);
+static char *source_date_epoch,*force_source_date;
+
+#if defined(_MSC_VER) && _MSC_VER < 1800
+#define strtoull _strtoui64
+#endif
+
+static struct tm *tl_now(void)
+{@+struct tm *tp;
+ time_t t;
+ source_date_epoch= getenv("SOURCE_DATE_EPOCH");
+ force_source_date= getenv("FORCE_SOURCE_DATE");
+ if (force_source_date!=NULL &&
+ (force_source_date[0]!='1' || force_source_date[1]!=0))
+ force_source_date=NULL;
+
+ if (source_date_epoch!=NULL)
+ { start_time= (time_t)strtoull(source_date_epoch, NULL, 10);
+ if (force_source_date!=NULL)
+ t=start_time;
+ else
+ t=time(NULL);
+ }
+ else
+ t=start_time=time(NULL);
+ if (force_source_date)
+ tp=gmtime(&t);
+ else
+ tp=localtime(&t);
+ return tp;
+}
+
+@*1 Retrieving File Properties.
+To support \LaTeX, a few more time related functions are needed.
+@<Header files and function declarations@>=
+#define TIME_STR_SIZE 30
+static char time_str[TIME_STR_SIZE];
+static void get_creation_date(void);
+static void get_file_mod_date(void);
+static int get_file_size(void);
+
+#include <md5.h>
+#define DIGEST_SIZE 16
+#define FILE_BUF_SIZE 1024
+static md5_byte_t md5_digest[DIGEST_SIZE];
+
+static int get_md5_sum(int s, int file);
+
+@ The code that follows was taken from the \.{texmfmp.c} file of
+the \TeX\ Live distribution and slightly modified.
+
+
+@<\TeX\ Live auxiliary functions@>=
+
+static void
+make_time_str(time_t t, bool utc)
+{
+ struct tm lt, gmt;
+ size_t size;
+ int off, off_hours, off_mins;
+
+ /* get the time */
+ if (utc) {
+ lt = *gmtime(&t);
+ }
+ else {
+ lt = *localtime(&t);
+ }
+ size = strftime(time_str, TIME_STR_SIZE, "D:%Y%m%d%H%M%S", &lt);
+ /* expected format: |"D:YYYYmmddHHMMSS"| */
+ if (size == 0) {
+ /* unexpected, contents of |time_str| is undefined */
+ time_str[0] = '\0';
+ return;
+ }
+
+ /* correction for seconds: S can be in range 00 to 61,
+ the PDF reference expects 00 to59,
+ therefore we map |"60"| and |"61"| to |"59"| */
+ if (time_str[14] == '6') {
+ time_str[14] = '5';
+ time_str[15] = '9';
+ time_str[16] = '\0'; /* for safety */
+ }
+
+ /* get the time zone offset */
+ gmt = *gmtime(&t);
+
+ /* this calculation method was found in exim's tod.c */
+ off = 60 * (lt.tm_hour - gmt.tm_hour) + lt.tm_min - gmt.tm_min;
+ if (lt.tm_year != gmt.tm_year) {
+ off += (lt.tm_year > gmt.tm_year) ? 1440 : -1440;
+ } else if (lt.tm_yday != gmt.tm_yday) {
+ off += (lt.tm_yday > gmt.tm_yday) ? 1440 : -1440;
+ }
+
+ if (off == 0) {
+ time_str[size++] = 'Z';
+ time_str[size] = 0;
+ } else {
+ off_hours = off / 60;
+ off_mins = abs(off - off_hours * 60);
+ snprintf(&time_str[size], TIME_STR_SIZE-size, "%+03d'%02d'", off_hours, off_mins);
+ }
+}
+
+static void get_creation_date(void)
+{ make_time_str(start_time,source_date_epoch!=NULL);
+}
+
+/* static structure for file status set by |find_input_file| */
+#ifdef WIN32
+static struct _stat file_stat;
+#define GET_FILE_STAT _stat(fname,&file_stat)
+#else
+static struct stat file_stat;
+#define GET_FILE_STAT stat(fname,&file_stat)
+#endif
+
+
+static char* find_input_file(void)
+{ char *fname;
+ int r;
+ if (output_directory&&!kpse_absolute_p((char*)name_of_file0, false))
+ { int r=-1;
+ fname = concat3(output_directory, DIR_SEP_STRING, (char*)name_of_file0);
+ r=GET_FILE_STAT;
+ if (r==0)
+ return fname;
+ free(fname);
+ }
+ fname = kpse_find_tex((char*)name_of_file0);
+ if (fname!=NULL)
+ { r=GET_FILE_STAT;
+ if (r==0)
+ return fname;
+ free(fname);
+ }
+ fname=(char*)name_of_file0;
+ r=GET_FILE_STAT;
+ if (r==0)
+ return strdup(fname);
+ return NULL;
+}
+
+
+static void get_file_mod_date(void)
+{ char *fname=NULL;
+ fname= find_input_file();
+ time_str[0]=0;
+ if(fname!=NULL)
+ { make_time_str(file_stat.st_mtime,source_date_epoch!=NULL && force_source_date!=NULL);
+ free(fname);@+
+ }
+}
+
+static int get_file_size(void)
+{ int s=-1;
+ char *fname=NULL;
+ fname= find_input_file();
+ if(fname!=NULL)
+ {s=file_stat.st_size;
+ free(fname);@+
+ }
+ return s;
+}
+
+
+static int get_md5_sum(int s, int file)
+{ md5_state_t st;
+ memset(md5_digest,0,DIGEST_SIZE);
+ if (file)
+ { char *fname;
+ pack_file_name(s,empty_string,empty_string,NULL);
+ fname= find_input_file();
+ if(fname!=NULL)
+ { FILE *f;
+ f = fopen(fname, "rb");
+ if (f != NULL)
+ { int r;
+ char file_buf[FILE_BUF_SIZE];
+ recorder_record_input(fname);
+ md5_init(&st);
+ while ((r = fread(&file_buf, 1, FILE_BUF_SIZE, f)) > 0)
+ md5_append(&st, (const md5_byte_t *) file_buf, r);
+ md5_finish(&st, md5_digest);
+ fclose(f);
+ }
+ free(fname);
+ }
+ else
+ return 0;
+ }
+ else
+ { md5_init(&st);
+ md5_append(&st,(md5_byte_t *)&str_pool[str_start[s]],
+ str_start[s + 1] - str_start[s]);
+ md5_finish(&st, md5_digest);
+ }
+ return DIGEST_SIZE;
+}
+
+@* Profiling.
+If we want to find out where and how \TeX\ spends its time, we need
+a profiler. The common profilers, like {\tt gprof},
+examine the executable code of \TeX, then take some measurments at run time,
+and map this information to the functions or source lines of \TeX's code.
+
+But \TeX\ is a programming language by itself, and when we program
+in \TeX, we want to map the timing information gathered at run time
+to the macros and source lines of \TeX's input files.
+The profiler implemented in the follwing code serves this purpose.
+
+By default, profiling is initialy disabled.
+To enable profiling, you can use the {\tt -prof} option on the
+command line.
+
+@<explain the options@>=
+ " -prof "@/
+ @t\qquad@>"\t enable profiling\n"@/
+
+@ This option will set the |profile_on| variable, defined below, to true.
+
+@<more options@>=
+ { "prof", 0, &profile_on, 1 },
+
+@ @<Global variables@>=
+static int @!profile_on=false;
+
+@ To restrict profiling to selected parts of \TeX's input,
+we define two primitives for switching profiling on and off.
+
+@d profile_on_code (eTeX_last_extension_cmd_mod+3) /* `\.{\\profileon}' */
+@d profile_off_code (eTeX_last_extension_cmd_mod+4) /* `\.{\\profileoff}' */
+@d pdftex_first_extension_code profile_off_code+1
+
+@<Put each...@>=
+primitive("profileon", extension, profile_on_code);
+@!@:profileon\_}{\.{\\profileon} primitive@>
+primitive("profileoff", extension, profile_off_code);
+@!@:profileoff\_}{\.{\\profileoff} primitive@>
+
+@ @<Cases of |extension| for |print_cmd_chr|@>=
+case profile_on_code: print_esc("profileon");@+break;
+case profile_off_code: print_esc("profileoff");@+break;
+
+@ @<Cases for |do_extension|@>=
+case profile_on_code:
+if (!profile_on)
+{ profile_on=true;
+ @<record profiling on@>@;
+}
+break;
+case profile_off_code:
+if (profile_on)
+{ @<record profiling off@>@;
+ profile_on=false;
+}
+break;
+
+@*1 Files and Lines.
+The ``instructions'' that drive the \TeX\ interpreter are called ``tokens''
+and we want to keep track of the files and the lines
+where the tokens come from.
+If a token comes from an external file, then \TeX's variable |line| will
+contain the correct line number, and |cur_file| contains the current file.
+|cur_file| is an abbreviation for |input_file[index]| with
+|index| beeing a short hand for |cur_input.index_field|.
+We can not use the |index| number to identify input files,
+because \TeX\ will reuse these numbers, possibly many times, after a file
+has been closed. Instead we make a new array |input_file_num| that
+runs in parallel to |input_file| and contains a unique current file number.
+Further we make a new |read_file_num| array that runs
+in parallel to |read_file|.
+
+@d cur_file_num input_file_num[index] /*the current unique |alpha_file| number*/
+
+@<Glob...@>=
+static uint8_t @!input_file_num0[max_in_open],
+ *const @!input_file_num = @!input_file_num0-1;
+static uint8_t @! read_file_num[20];
+
+@ To implement the association between unique file numbers and full
+file names, we use the array |file_num_name|. We will store file
+numbers with 8 bits and line numbers with 16 bits in a single 32 bit
+number. We do this because we will need to store this information
+together with the tokens in a token list. This representation will
+keep the most significant byte zero, so that we can later use it for
+the ``command'' value.
+
+@<Global...@>=
+#define FILE_NUM_BITS 8
+#define MAX_FILE_NUM ((1<<FILE_NUM_BITS)-1)
+#define LINE_BITS 16
+#define MAX_LINE ((1<<LINE_BITS)-1)
+#define FILE_LINE(F,L) (((F)<<(LINE_BITS))|(L))
+#define FL_FILE(FL) ((FL)>>LINE_BITS)
+#define FL_LINE(FL) ((FL)&MAX_LINE)
+static char * @!file_num_name[MAX_FILE_NUM+1];
+static int file_from_cmd [MAX_FILE_NUM+1]; /* first command from the file */
+static int file_to_cmd [MAX_FILE_NUM+1]; /* last command from the file */
+static int file_num; /* the the last |file_num_name| allocated */
+static uint32_t cur_file_line=0; /* the file and line for |cur_tok| */
+
+@ @<check |line| for overflow@>=
+if (line>=MAX_LINE) overflow("too many input lines", MAX_LINE);
+
+@ Whenever we have a new |full_name_of_file|, we allocate
+a new unique file number.
+
+@<Allocate a file number@>=
+if (file_num>=MAX_FILE_NUM) overflow("file number",file_num);
+else file_num++;
+file_num_name[file_num]=strdup(full_name_of_file);
+file_from_cmd[file_num]=cmd_count;
+
+@ Whenever \TeX\ sets |cur_file| to a new value, we need to
+set |cur_file_num| as well.
+
+@<Set new |cur_file_num|@>=
+@<Allocate a file number@>@;
+cur_file_num=file_num;
+
+@ Whenever \TeX\ opens |read_file[n]| it needs to set the file count.
+@<Set new |read_file_num[n]|@>=
+@<Allocate a file number@>@;
+read_file_num[n]=file_num;
+
+@ A few file numbers are predefined. Some tokens, for instance those
+that are loaded from a format file, have an unknown origin and are
+classified as comming from an |unknown_file|.
+We use |system_file| to assign time intervals to
+the system and use the command field to distinguish between different
+system events. There might also be tokens from the terminal, either
+typed in interactively (which will messup your timing values)
+or from the command line.
+
+For the commands from the |system_file|, we use
+special line numbers to distinguish various cases.
+
+@d unknown_file 0 /*tokens with unknown origin, usualy from a format file*/
+@d system_file 1 /*tokens generated by the system*/
+@d terminal_file 2 /*tokens read from the terminal*/
+@d system_unknown 0 /*should not happen*/
+@d system_start 1 /*startup of \TeX*/
+@d system_end 2 /*end of \TeX*/
+@d system_ship_out 3 /*time spent in |ship_out()|*/
+@d system_line_break 4 /*time spent in |line_break()|*/
+@d system_init_trie 5 /*time spent in |line_break()|*/
+@d system_build_page 6 /*time spent in |line_break()|*/
+@d system_input_ln 7 /*time spent in |line_break()|*/
+@d system_insert 8 /*time spent on tokens inserted by \TeX*/
+
+
+@<Set init...@>=
+file_num_name[unknown_file]="unknown";
+file_num_name[system_file]="system";
+file_num_name[terminal_file]="terminal";
+file_num=terminal_file;
+
+@ When \TeX\ reads a token from an external file, it has to set
+|cur_file_line| based on the information in |cur_input|.
+This code is parallel to the code that is used to read the next line:
+
+@<Set |cur_file_line| based on the information in |cur_input|@>=
+if (terminal_input)
+ cur_file_line=FILE_LINE(terminal_file,0);
+else if (name<=19)
+ cur_file_line=FILE_LINE(read_file_num[name-1],0); /* do I need the line? */
+else
+ cur_file_line=FILE_LINE(cur_file_num,line);
+
+@ When we end a \.{\\read} line, we set the current file and line.
+@<Set |cur_file_line| when a \.{\\read} line ends@>=
+ cur_file_line=FILE_LINE(read_file_num[name-1],0);
+
+
+@ As described above, we store file and line numbers as 32 bit integers.
+When reading input from external files, we know the file and the line;
+but for token lists we need to store this information together
+with the tokens, when we create the token list.
+As a simple solution, we allocate a 32 bit integer for all pointers.
+
+Whenever in \TeX's code a new token is generated, we know what file and
+line is responsible for what is going on. The newly generated token
+will then point to the same file and line.
+
+@<Glob...@>=
+static uint32_t @!fl_mem0[mem_max-mem_min+1],
+ *const @!fl_mem = @!fl_mem0-mem_min; /*the big dynamic file/line storage*/
+
+@*1 Timing.
+When the interpreter of \TeX\ executes a command, we need to measure the
+time needed to execute it and record that information in a file.
+We call such a measurement a ``time stamp'' or ``stamp''
+for short.
+This process should be quick, in order not to disturb the measurements
+too much by the time needed for the measurement itself and its recording.
+So we keep the data in a large array, and postpone writing the output file
+until \TeX\ terminates.
+
+Now let us consider the measurement of such a time stamp.
+As a general rule, the start of a new time interval is the end
+of the previous time interval. There is only a single look up
+of the current time for both. So all time intervals will add up
+to the total run time.
+
+We keep the start time in two variables |start_sec| and |start_nsec|;
+the first counts the seconds, the other (roughly) the nano seconds.
+When we measure the end time, we should know the previous start time,
+and which command was executed and from which file and line this command
+was taken. The measured end time is the start time for the next command.
+
+
+@<Global variables@>=
+static long start_nsec, start_sec, diff_nsec;
+struct timespec ts;
+static int time_error;
+
+@ To get timing information, we use the POSIX function |clock_gettime|.
+Depending on the operating system there are many different variations.
+The timer used for timing can be selected by defining |GETTIME| at
+compile time. If |GETTIME| is not set, we try to find a good default.
+Currently we use |timespec_get| under Windows and |clock_gettime|
+with a tread specific clock otherwise.
+
+@<get current time@>=
+#ifdef GETTIME
+#if GETTIME==0
+ time_error=clock_gettime(CLOCK_THREAD_CPUTIME_ID, &ts);
+#elif GETTIME==1
+ time_error=clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &ts);
+#elif GETTIME==2
+ time_error=clock_gettime(CLOCK_MONOTONIC_RAW, &ts);
+#elif GETTIME==3
+ time_error=clock_gettime(CLOCK_MONOTONIC, &ts);
+#else
+ time_error=clock_gettime(CLOCK_THREAD_CPUTIME_ID, &ts);
+#endif
+#else
+/* guess a default */
+#ifdef _WIN32
+ time_error=timespec_get(&ts, TIME_UTC);
+#else
+ time_error=clock_gettime(CLOCK_THREAD_CPUTIME_ID, &ts);
+#endif
+#endif
+
+
+@ To record the timing information, we compute the time difference in
+nano seconds, assuming that it fits into unsigned 32 bit which means
+the difference should be less than about 4 seconds.
+
+@<compute elapsed time@>=
+ diff_nsec= (ts.tv_sec-start_sec)*1000000000+ (ts.tv_nsec-start_nsec);
+#if 0
+ printf("time: %ld %ld %ld\n", diff_nsec, (ts.tv_sec), (ts.tv_nsec));
+#endif
+
+ start_nsec=ts.tv_nsec;
+ start_sec=ts.tv_sec;
+
+@*1 Commands.
+The fourth piece of information that we store with file, line, and time
+is the command that \TeX\ is executing.
+The elapsed time will fit into an four byte unsigned integer, and
+luckily also the command, the file and the line will do as well.
+When starting a new time interval, often we will not yet know the
+next command, its file, and its line.
+We keep the command, the file, the line, in a set of variables starting with
+|prof_|. These variables my change within a time interval, and the final
+values are written when the end time is known.
+There are also variables related to macro calls which are explainded later
+but are already defined here because we will initialize them together
+with the other variables.
+
+@<Global variables@>=
+#define MAX_STAMPS 80000000
+#define MAX_MACRO_STACK 40000
+#define POP_BIT 0x80
+
+static uint32_t stamp[MAX_STAMPS]; /*one chunk of profiling information*/
+static uint32_t st_count=0; /*the next available entry in the array above*/
+static uint32_t cmd_count=0; /*the number of time stamps in the array above*/
+static int prof_cmd=0; /*the command to be written */
+static uint32_t prof_file_line=0; /*the file and line to be written */
+
+@ The code below initializes the variables. Note that we use the special
+command value |system_cmd|. The ``real'' command values
+are those that occur in the |big_switch| ranging from 0 (|escape|)
+to 100 (|max_command|), the values that are used when entering
+the |main_loop|(|letter|, |other_char|, |char_given|, and |char_num|)
+are a subset of these.
+The pseudo command |system_cmd| is just outside of this range.
+A few more pseudo commands are needed below and we define them here.
+
+@d system_cmd (max_command+1) /*pseudo command value*/
+@d system_profile_on (system_cmd+1)
+@d system_profile_off (system_cmd+2)
+@d system_macro_push (system_cmd+3)
+@d system_macro_pop (system_cmd+4)
+@d system_macro_pop_small (system_cmd+5)
+@d system_macro_pop_0 (system_cmd+6)
+
+
+@<Initialize profiling@>=
+ @<Initialize the macro call stack@>@;
+ prof_cmd=system_cmd;
+ prof_file_line=FILE_LINE(system_file,system_start);
+ @<record macro call information if necessary@>@;
+#if 0
+ clock_getres(CLOCK_MONOTONIC, &ts);
+ printf("CLOCK_MONOTONIC: %ld %ld\n",ts.tv_sec,ts.tv_nsec);
+
+ clock_getres(CLOCK_MONOTONIC_RAW, &ts);
+ printf("CLOCK_MONOTONIC_RAW: %ld %ld\n",ts.tv_sec,ts.tv_nsec);
+
+ clock_getres(CLOCK_PROCESS_CPUTIME_ID, &ts);
+ printf("CLOCK_PROCESS_CPUTIME_ID: %ld %ld\n",ts.tv_sec,ts.tv_nsec);
+
+ clock_getres(CLOCK_THREAD_CPUTIME_ID, &ts);
+ printf("CLOCK_THREAD_CPUTIME_ID: %ld %ld\n",ts.tv_sec,ts.tv_nsec);
+#endif
+ @<get current time@>@;
+ diff_nsec=0;
+ start_nsec=ts.tv_nsec;
+ start_sec=ts.tv_sec;
+
+@ At the end of \TeX\ we check |last_depth| and clean the remaining
+macro stack if necessary. We end the record always with recording
+timing information.
+
+@<pop the macro call stack at the end of \TeX@>=
+prof_cmd=cur_cmd; /* the stop command */
+prof_file_line=cur_file_line;
+prof_depth=0;
+profile_on=true;
+@<get current time@>@; /* advance the time */
+@<compute elapsed time@>@;
+@<record timing information@>@; /* pop to |prof_depth| */
+
+
+@ When we record the timing information,
+we first store command, file, and line, and then store
+the elapsed time.
+
+@d SHOW_RECORD_TIMING 0
+
+@<record timing information@>=
+if (profile_on)
+{ if (st_count+3>MAX_STAMPS)
+ overflow("profile data",MAX_STAMPS);
+ @<record poping the macro stack if necessary@>@;
+ stamp[st_count++]= (prof_cmd<<24) | prof_file_line;
+ prof_cmd=prof_cmd&~POP_BIT; /*remove a possible |POP_BIT|*/
+#if SHOW_RECORD_TIMING
+ print_char('<');print_int(cmd_count);
+ print_char(':');
+ if (prof_cmd&POP_BIT) print_char('-');
+ print_int(prof_cmd&~POP_BIT);
+ print_char(':');print_int(prof_depth);
+ print_char('>');print_ln();
+#endif
+ cmd_count++;
+ @<get current time@>@;
+ @<compute elapsed time@>@;
+ stamp[st_count++]=diff_nsec;
+}
+
+
+@ To mark the execution of starting and ending profiling
+we use the system commands for the start and the end of profiling.
+
+After switching profiling off, no more data is written to the output
+file. So we have to record the macro call and timing information
+explicitly before switching the profiling off.
+
+@<record profiling off@>=
+ prof_file_line=cur_file_line;
+ prof_cmd=system_profile_off;
+ prof_depth=cur_depth;
+ @<record macro call information if necessary@>@;
+ @<record timing information@>@;
+
+@ When switching profiling on, we have to syncronize the start time
+with the current time and prepare everything for the recording
+of the timing before entering the main loop again.
+Setting the command, file and line information is simple.
+Since we can not assume that the macro call stack is unchanged,
+we record the necessary changes.
+
+@<record profiling on@>=
+ @<get current time@>@;
+ diff_nsec=0;
+ start_nsec=ts.tv_nsec;
+ start_sec=ts.tv_sec;
+ prof_file_line=cur_file_line;
+ prof_cmd=system_profile_on;
+ prof_depth=cur_depth;
+ @<record macro call information if necessary@>@;
+
+@ At the end of \TeX\ we record the timing for the |stop| command
+and pop the macro call stack.
+
+@<record the end of \TeX@>=
+@<pop the macro call stack at the end of \TeX@>@;
+
+@ There are two places where we regularly record the next command.
+The first one is when \TeX\ enters the big switch. Here
+we record the timing information before reading the next expanded token
+and set the current file, line, and command after reading the
+next expanded token.
+The other place is at the start of the main loop. Here both
+actions are done one after the other. Whenever we assiciate
+a command with the current time slot by setting |prof_cmd| and
+the profile data associated with it, we need to make sure that
+changes of entries in the macro call are reported to the profiler.
+
+@<set current file, line, and command for the current time slot@>=
+if (profile_on)
+{ prof_cmd=cur_cmd;
+ prof_depth=cur_depth;
+ prof_file_line=cur_file_line;
+ @<record macro call information if necessary@>@;
+}
+
+@ Some time expensive routines, |line_break| and |ship_out|, consume
+time that depends entirely on the output generated by \TeX\ but
+not on how that output is generated. So we charge the time spent in these
+routines on the system.
+
+@<Local variables to save the profiling context@>=
+int tmp_cmd;
+int tmp_file_line;
+int tmp_depth;
+
+@ @<Charge the time used here on |line_break|@>=
+if (profile_on)
+{ @<record timing information@>@;
+ tmp_file_line=prof_file_line;
+ tmp_cmd=prof_cmd;
+ tmp_depth=prof_depth;
+ prof_file_line=FILE_LINE(system_file,system_line_break);
+ prof_cmd=system_cmd;
+}
+
+
+@ @<restore the previous current file, line, and command@>=
+if (profile_on)
+{ @<record timing information@>@;
+ prof_file_line=tmp_file_line;
+ prof_cmd=tmp_cmd;
+ prof_depth=tmp_depth;
+}
+
+
+
+@ @<Charge the time used here on |init_trie|@>=
+if (profile_on)
+{ @<record timing information@>@;
+ tmp_file_line=prof_file_line;
+ tmp_cmd=prof_cmd;
+ tmp_depth=prof_depth;
+ prof_file_line=FILE_LINE(system_file,system_init_trie);
+ prof_cmd=system_cmd;
+}
+
+
+@ @<Charge the time used here on |build_page|@>=
+if (profile_on)
+{ @<record timing information@>@;
+ tmp_file_line=prof_file_line;
+ tmp_cmd=prof_cmd;
+ tmp_depth=prof_depth;
+ prof_file_line=FILE_LINE(system_file,system_build_page);
+ prof_cmd=system_cmd;
+}
+
+
+
+@ @<Charge the time used here on |ship_out|@>=
+if (profile_on)
+{ @<record timing information@>@;
+ tmp_file_line=prof_file_line;
+ tmp_cmd=prof_cmd;
+ tmp_depth=prof_depth;
+ prof_file_line=FILE_LINE(system_file,system_ship_out);
+ prof_cmd=system_cmd;
+}
+
+
+@*1 Macro Calls.
+Let's talk about collecting information about macro calls.
+Macro calls come in various flavours:
+There are active characters and single letter control sequences;
+both have their own region in the |eqtb| table of equivalents.
+Then there is an empty control sequence, and last there
+are lots of (currently up to 45000) multiletter control sequences.
+The plain format defines about 1000 and the \LaTeX\ format about 23000
+of them. When |get_next| finds a control sequence, it sets |cur_cs|
+to the index in the table of equivalents.
+It will further set |cur_cmd=eq_type(cur_cs)|, |cur_chr=equiv(cur_cs)|,
+and |cur_tok=cs_token_flag+cur_cs|.
+
+The |get_x_token| or |expand| procedure will then notice the macro call
+and invoke |macro_call|.
+|macro_call| will inspect the call site and the parameter section
+of the macro and put the actual macro parameters on the parameter
+stack before pushing the macro body on the input stack.
+
+But wait! |macro_call| will apply a technique called last call optimization
+before doing any of the above: It will remove all entries from
+the input stack that have no more tokens left for processing.
+This allows tail recursive macro calls in constant stack space and
+makes long recursive loops possible.
+For example building the \LaTeX\ format uses a loop to read the
+definition of UTF8 codes.
+This loop has more than 35000 iterations and a nesting depth
+of the same size. With last call optimization, the loop uses less than 3000
+entries on the input stack.
+
+Popping the stack early makes it slightly more difficult
+to track who is calling whom---an information that we need if we want
+to give cumulative timing for a macro call. Cumulative timing is the time spent
+during that macro call including all the calls to sub-macros.
+
+The timing information that we collected in the previous section
+assigned the time caused by the execution of \TeX's commands to the
+specific line and file that caused the execution of that command.
+As a consequnce, the time used for the expansion of a macro call
+is assigned to the file and line that contains the call site of the macro.
+The time that is spent on executing the commands in the macros body
+are assigned to the file and line containing the macro definition.
+But there is no information on the call graph that allows us to
+classify one command as comming from a lower level of expansion than
+the other.
+
+In this section, we want to assign the time used for the execution
+of commands to a macro definition, with its name and the file and line
+that contains the definition of the macro.
+While the name of a macro is an important information, it is not
+sufficient to relate the time to the macros name, because
+the same name can be used for several different macros at various times.
+
+So we collect for a macro call the following data:
+
+\yskip\textindent{1)}The index |cur_cs| into the table of equivalents
+|eqtb|. Using |cur_cs| it is possible to access the macros name. For
+active characters and single letter control sequences, we obtain the
+character by subtracting |active_base| or |single_base| from |cur_cs|.
+For multi-letter control sequences, we find the index to |str_start|
+for the macro name in |text(cur_cs)|, the text field of the corresponding
+hash table entry. The association between |cur_cs| and the macro name
+is stored in a separate section of the profilers output file.
+
+\yskip\textindent{2)}The line and file of the macro definition.
+We store this information in the |fl_mem| array using |equiv(cur_cs)|,
+the pointer to the reference count token of the macro.
+
+\yskip\textindent{3)}The ``unoptimized'' nesting depth of the macro call.
+
+We store this information in a 64 bit integer and define macros
+to assemble and disassemble this integer.
+From this information it is possible to reconstruct the call graph
+and compute cumulative timing information for macro calls.
+We keep the information for the whole ``unoptimized'' macro stack
+in the |macro_stack|.
+The varable |prof_depth| tells us the nesting depth of |prof_cmd|.
+The variable |last_depth| tells us the nesting depth
+of the last recorded time stamp and the variable |unchanged_depth|
+tells us which part of the macro stack is unchanged since then.
+
+
+@d CALL_DEPTH(A) ((A)>>48)
+@d CALL_EQTB(A) (((A)>>32)&0xFFFF)
+@d CALL_FILE(A) (((A)>>16)&0xFF)
+@d CALL_LINE(A) ((A)&0xFFFF)
+@d CALL_DE(A) ((A)>>32)
+@d CALL_CFL(A) ((A)&0xFFFFFFFF)
+@d CALL_INFO(D,C,FL) (((uint64_t)(D)<<48)|((uint64_t)(C)<<32)|
+ (system_macro_push<<24)|FL)
+
+@<Global variables@>=
+static uint64_t macro_stack[MAX_MACRO_STACK]={0}; /* the macro calls */
+static int prof_depth; /* the nesting depth of macros */
+static int last_depth; /* the nesting depth last report1ed*/
+static int unchanged_depth; /* up to here no change on |macro_stack|*/
+static int prof_max_depth;
+
+@ @<Initialize the macro call stack@>=
+{ int macro_depth=-1, macro_cs=0;
+ uint32_t macro_fl=FILE_LINE(terminal_file,0);
+ @<update the macro stack@>@;
+ cmd_count++;
+ stamp[st_count++]=CALL_CFL(macro_stack[0]);
+ stamp[st_count++]=CALL_DE(macro_stack[0]);
+ last_depth=unchanged_depth=prof_depth=0;
+}
+
+@ We maintain the macro stack in the |macro_call| procedure
+by adding local variables that capture the information of
+the call token that cause the invocation of |macro_call|.
+
+@<additional local variables for |macro_call|@>=
+uint32_t macro_fl=fl_mem[cur_chr];
+int macro_cs=cur_cs;
+int macro_depth=cur_depth;
+
+@ At the end of |macro_call|, we use this information to
+update the |macro_stack| and related variables.
+
+@<update the macro stack@>=
+macro_depth++;
+cur_depth=macro_depth;
+if (macro_depth>=MAX_MACRO_STACK)
+ overflow("macro stack size", macro_depth);
+macro_stack[macro_depth]=CALL_INFO(macro_depth,macro_cs,macro_fl);
+if (macro_depth<=unchanged_depth)
+ unchanged_depth=macro_depth-1;
+
+@ We add files as part of the macro stack using the following code:
+@<additional local variables for |start_input|@>=
+uint32_t macro_fl=FILE_LINE(cur_file_num,0);
+int macro_cs=0;
+int macro_depth=cur_depth;
+
+@ We add calls to the output routine and hook macros like \.{\\everypar}, \.{\\everymath}, etc. to the macro stack using tis code:
+@<additional local variables for |begin_token_list|@>=
+uint32_t macro_fl=fl_mem[p];
+int macro_cs=undefined_control_sequence+t;
+/*|undefined_control_sequence| is the last pointer in the hash table*/
+int macro_depth=cur_depth;
+
+
+@ As we have seen above, recording timing information consists
+of two steps:
+In the first step we set global variables like |prof_cmd| and |prof_depth|
+based on the current command. Then we wait until the command is executed,
+and in the second step we compute the elapsed time and store the
+information from the first step together with the time in the |stamp|
+array.
+Now we augment this process and store macro call related information.
+
+The key value that guides this process is |prof_depth|:
+the macro nesting depth associated with |prof_cmd|.
+Using it we can get from the |macro_stack| information about
+all currently active macros at lower nesting levels.
+Two more variables |last_depth| and |unchanged_depth| are related to it:
+After recording macro call information for a command,
+we set |last_depth=unchanged_depth=prof_depth|.
+The next time we need to record macro call information,
+|unchanged_depth| and |prof_depth| may have changed, but
+|last_depth| should be unchanged. Because |unchanged_depth|
+can only decrease, we know that $|unchanged_depth|\le|last_depth|$.
+On the other hand, |prof_depth| can increase as well as decrease.
+This gives us the following cases:
+
+\yskip\textindent{$|unchanged_depth|<|prof_depth|$}
+Record the new stack entries from $|unchanged_depth|+1$ up to |prof_depth|.
+
+\yskip\textindent{$|prof_depth|=|unchanged_depth|=|last_depth|$:}
+No information on macro calls is necessary.
+
+\yskip\textindent{$|prof_depth|<|unchanged_depth|=|last_depth|$:}
+Record that the stack was popped from |last_depth| down to |prof_depth|.
+
+\yskip\textindent{$|prof_depth|\le|unchanged_depth|<|last_depth|$:}
+Record that the stack was popped from |last_depth| down to |prof_depth|.
+
+
+The common case that $|prof_dept|=|last_dept|-1$ asks for a more
+compact representation and we use the most significant bit of the
+command value to encode this case.
+Note that we count this as a separate command.
+
+It would be nice if we could wait with recording the stack changes until
+the command is completely executed. The command however might read
+parameters which causes pops and pushes on the macro stack. So by the
+time the command has finished, the stack might have changed considerably.
+There is a further complication: the execusion of some commands does
+not end with a jump to |big_switch| but with a jump to |big_reswitch|.
+This will set a new value for |prof_cmd| and so will invalidate all our
+preparations.
+For this reason we need to make the recording of macro call information
+idempotent. Recording several pops and pushes explicitly does not harm
+(except for extra output) but setting the pop bit asks for trouble.
+Therefore we postpone the recording of poping the macro stack.
+
+
+@<record macro call information if necessary@>=
+if (unchanged_depth<prof_depth)
+ @<record the new stack entries@>@;
+
+
+@ @<record poping the macro stack if necessary@>=
+if (last_depth>prof_depth)
+{ if (last_depth==prof_depth+1)
+ prof_cmd=prof_cmd|POP_BIT;
+ else
+ stamp[st_count++]=(system_macro_pop<<24)| (last_depth-prof_depth);
+#if SHOW_RECORD_TIMING
+ print_char('{');print_int(cmd_count);
+ print_char(':');print_int(system_macro_pop);
+ print_char(':');print_int(last_depth);
+ print_char('>');print_int(prof_depth);
+ print_char('}');print_ln();
+#endif
+ cmd_count++;
+ last_depth=unchanged_depth=prof_depth;
+}
+
+
+@ The information about new stack entries are taken from the |macro_stack|.
+
+@<record the new stack entries@>=
+{ int i;
+ if (last_depth>unchanged_depth)
+ {
+#if SHOW_RECORD_TIMING
+ print_char('{');print_int(cmd_count);
+ print_char(':');print_int(system_macro_pop);
+ print_char(':');print_int(last_depth);
+ print_char('>');print_int(unchanged_depth);
+ print_char('}');print_ln();
+#endif
+ cmd_count++;
+ }
+ if (st_count+2*(prof_depth-unchanged_depth)>MAX_STAMPS)
+ overflow("profile data",MAX_STAMPS);
+ for (i=unchanged_depth+1;i<=prof_depth;i++)
+ {
+#if SHOW_RECORD_TIMING
+ print_char('[');print_int(cmd_count);
+ print_char(':');print_int(i-1);
+ print_char('<');print_int(CALL_DEPTH(macro_stack[i]));
+ print_char(':'); print_cs(CALL_EQTB(macro_stack[i]));
+ print_char(']');print_ln();
+#endif
+ cmd_count++;
+ stamp[st_count++]=CALL_CFL(macro_stack[i]);
+ stamp[st_count++]=CALL_DE(macro_stack[i]);
+ }
+ last_depth=unchanged_depth=prof_depth;
+ if (prof_depth>prof_max_depth) prof_max_depth=prof_depth;
+}
+
+
+@*1 Output the profile data.
+Here is the code that outputs the results.
+The format tries to be simple but flexible.
+The output file has four parts: size data, file names, macro names,
+and time stamps. To enable some error checking, the three file
+parts start and end with an output marker.
+
+% we have typically from 1000 to 5000, in exteme cases up to 30000,
+% file/line entries
+% cut -d : -f 2,3 tmon.out | fgrep -v '.' | sort |uniq | wc -l
+
+@<Finish the extensions@>=
+{ FILE *prof;
+ pack_job_name(".tprof");
+ prof=fopen((char*)name_of_file0,"wb");
+ if (prof==NULL) print_err("Unable to write the profile data\n");
+ else {
+ int i;
+ prof_file_line=cur_file_line;
+ @<output marker@>@;
+ @<output size data@>@;
+ @<output marker@>@;
+ @<output file names@>@;
+ @<output marker@>@;
+ @<output macro names@>@;
+ @<output marker@>@;
+ @<output timing data@>@>
+ @<output marker@>@;
+ fclose(prof);
+ }
+}
+
+@ The output marker consists of eight byte: the ASCII codes of ``TEX PROF''.
+
+@<output marker@>=
+ fputs("TEX PROF",prof);
+
+
+@ The information necessary to process the file comes after the first
+marker. Here and in the following all numbers are written out in
+``big-endian byte order''. We provide the following information:
+
+The number of files (2 byte),
+the number of byte used for filenames
+including the zero bytes (2 byte),
+the number of time stamps (4 byte).
+The number of macro names (2 byte),
+the number of bytes used for macro names (4 byte),
+again including the zero bytes,
+and the maximum nesting depth used on the |macro_stack|.
+
+We start with the macros to write multibyte integers in bigendian order.
+
+@d PUT1(N) fputc((N)&0xFF,prof)
+@d PUT2(N) PUT1((N)>>8),PUT1(N)
+@d PUT4(N) PUT2((N)>>16),PUT2(N)
+
+
+@<output size data@>=
+ PUT2(file_num+1); /* the number of files */
+{ int n, m;
+ m=0;
+ for (i=0; i<=file_num; i++)
+ { n=strlen(file_num_name[i]);
+ m+=n+1;
+ }
+ PUT2(m); /* the number of byte used for file names */
+}
+ PUT4(cmd_count); /* the number of commands */
+{ int k, m;
+ m=k=0;
+ for (i=hash_base; i<undefined_control_sequence; i++)
+ if (text(i)!=0)
+ { k++;
+ m+=(str_start[text(i)+1]-str_start[text(i)])+1;
+ }
+ PUT2(k); /* the number of macro names */
+ PUT4(m); /* the number of byte for the macro names */
+ PUT2(prof_max_depth); /*the maximum macro nesting depth*/
+}
+
+
+@ Writing the file names is simple.
+
+@<output file names@>=
+for (i=0; i<=file_num; i++)
+ { fputs(file_num_name[i], prof);
+ PUT1(0);
+ }
+
+@ Here is the code to write the macro names to the output file.
+It traverses the |hash_table| from |hash_base| up to
+|undefined_control_sequence|. Each macro name is preceeded by
+its index in the hash table stored in two byte.
+
+@<output macro names@>=
+for (i=hash_base; i<undefined_control_sequence; i++)
+ if (text(i)!=0)
+ { int k;
+ PUT2(i);
+ for (k=str_start[text(i)];k<str_start[text(i)+1];k++)
+ PUT1(str_pool[k]);
+ PUT1(0);
+ }
+
+
+@ Next we output the time stamps. We use a variable format
+depending on the command value which is always in the top byte
+of the stamp. We optimize the output size slightly:
+For macro calls the |stamp| array contains the depth values.
+It is of course more efficient to write the relative changes
+into the output instead of the absolute values.
+A most common case is poping the stack by one. This is recorded
+in the |stamp| array by setting the |POP_BIT| in the command value
+and we simply keep this bit. Small relative values $n$ in the range
+$0 \le n \le 10$ are encoded using the command value
+$|system_macro_pop_0|+n$. Values in the range $11 \le n \le 255$
+encoded as single byte values following the commmand code
+|system_macro_pop_small|. All other values are encoded using
+two byte following the command code |system_macro_pop|.
+Encoding the pops separate from the calls, we need no longer
+store the depth values with the calls.
+
+@<encode pop $n$@>=
+#if SHOW_WRITE_TIMING
+ printf("!%d:%d:%d>%d\n",j++,i,last_depth,last_depth-n);
+#endif
+ if (n<=10)
+ PUT1(system_macro_pop_0+n);
+ else if (n<=255)
+ { PUT1(system_macro_pop_small);
+ PUT1(n);
+ }
+ else
+ { PUT1(system_macro_pop);
+ PUT2(n);
+ }
+ last_depth=last_depth-n;
+
+@ Now we can output the timing data.
+
+@d SHOW_WRITE_TIMING 0
+
+@<output timing data@>=
+
+ i=0;
+{ int j=0;
+ last_depth=-1;
+ while (i< st_count)
+ { int8_t c = stamp[i]>>24;
+ if (c==system_macro_pop)
+ { int n=stamp[i]&0xFFFF;
+ @<encode pop $n$@>@;
+ i++;
+ }
+ else if (c==system_macro_push)
+ { int d=stamp[i+1]>>16;
+ if (d<=last_depth)
+ { int n=last_depth-d+1;
+ @<encode pop $n$@>@;
+ /* here I could optimize the case $n=1$ by setting the |POP_BIT|
+ in |system_macro_push|*/
+ }
+#if SHOW_WRITE_TIMING
+ print_char('!');print_int(j++);
+ print_char(':');print_int(i);
+ print_char(':'); print_int(system_macro_push);
+ print_char(':');print_int(d);
+ print_char(':'); print_cs(stamp[i+1]&0xffff);
+ print_char(']');print_ln();
+#endif
+ PUT4(stamp[i]); i++;
+ PUT2(stamp[i]); i++;
+ last_depth=d;
+ }
+ else
+ {
+ if (c&POP_BIT)
+ { last_depth--;
+#if SHOW_WRITE_TIMING
+ printf("!%d:%d:%d>%d}\n",j++,i,last_depth+1,last_depth);
+#endif
+ }
+#if SHOW_WRITE_TIMING
+ printf("!%d:%d:%d:%d>\n",j++,i,stamp[i]>>24,last_depth);
+#endif
+ PUT4(stamp[i]);i++;
+ PUT4(stamp[i]);i++;
+ }
+ }
+}
+
+@*1 Dumping the profiling information.
+
+We dump the number of files first and then dump
+the file names including the
+terminating zero byte.
+We put eight byte into a memory word and dump them together.
+The last memory word is padded with zeros.
+
+@<Dump the full file names@>=
+{ two_halves hw;
+ int i,j;
+ hw.rh=file_num;
+ i=0;
+ for (k=0; k<=file_num; k++)
+ { j=strlen(file_num_name[k]);
+ if (j>i) i=j;
+ }
+ hw.lh=i;
+ dump_hh(hw);
+}
+{ memory_word m;
+ int word_count=0;
+ int byte_count=0;
+ m.w=0;
+ for (k=0;k<=file_num;k++)
+ { char *s=file_num_name[k];
+ do
+ { m.w=(m.w<<8)|*s;
+ byte_count++;
+ if (byte_count==8)
+ { dump_wd(m); word_count++;
+ m.w=0; byte_count=0;
+ }
+ } while (*s++!=0);
+ }
+ if (byte_count >0)
+ { while (byte_count<8)
+ { m.w=(m.w<<8)|0;
+ byte_count++;
+ }
+ dump_wd(m);word_count++;
+ }
+ print_ln();print_int(file_num);
+ print(" file names dumped; total size "); print_int(word_count*8);
+}
+
+@ @<Undump the full file names@>=
+
+{ two_halves hw;
+ memory_word m;
+ int byte_count=0;
+ int i,j;
+ char *buf;
+ undump_hh(hw);
+ file_num=hw.rh;
+ if (file_num>MAX_FILE_NUM) overflow("file number",file_num);
+ i=hw.lh;
+ buf=malloc(i+1);
+ if (buf==NULL)
+ { print_err("Out of memory while reading filenames from format file");
+ succumb;
+ }
+ for (k=0;k<=file_num;k++)
+ { char *s=buf;
+ do
+ { if (byte_count==0)
+ { undump_wd(m); byte_count=8; }
+ *s=(m.w>>56)&0xFF; m.w=m.w<<8;
+ byte_count--;
+ } while (*s++!=0);
+ file_num_name[k]=strdup(buf);
+ if (file_num_name[k]==NULL)
+ { print_err("Out of memory while reading filenames from format file");
+ succumb;
+ }
+ }
+ free(buf);
+}
+
+
+@
+Next we dump the file and line information from the |fl_mem| array.
+Because some formats leave many unallocated memory words in the |avail|
+list we dump only those memory words that are not on the avail list.
+We set the entry |fl_mem[p]| to zero if |p| is on the |avail| list
+while we traverse the list when dumping the memory words.
+Besides the unallocated entries also entries that specify the unknown
+file with line zero are completely zero. We do not dump these as well.
+
+When dumping an entry we set the topmost bit of the entry to 1.
+An entry that has the topmost bit equal to zero contains the count
+of the following zero entries. We dump entries in pairs since
+the dump file is organized as a file of memory words
+
+@<Dump the file and line information@>=
+{ int word_count=0;
+ k=hi_mem_min;
+ while (k<=mem_end)
+ { two_halves hw;
+ halfword e;
+ int word_count=0;
+ @<compute the next file line entry@>@;
+ hw.rh=e;
+ if (k>mem_end) e=0;
+ else
+ { @<compute the next file line entry@>@; }
+ hw.lh=e;
+ dump_hh(hw); word_count++;
+ }
+ print_ln(); print_int(word_count);
+ print(" words of file/line information dumped.");
+}
+@ @<compute the next file line entry@>=
+ if (fl_mem[k]!=0) e=fl_mem[k++]|0x80000000;
+ else
+ { int i;
+ i=1; k++;
+ while (k<=mem_end && fl_mem[k]==0)
+ { i++; k++; }
+ e=i;
+ }
+
+@ @<Undump the file and line information@>=
+k=hi_mem_min;
+while (k<=mem_end)
+{ two_halves hw;
+ halfword e;
+ undump_hh(hw);
+ e=hw.rh;
+ @<get the next file line entry@>@;
+ if (k<=mem_end)
+ { e=hw.lh;
+ @<get the next file line entry@>@;
+ }
+}
+
+@ @<get the next file line entry@>=
+if (e&0x80000000)
+ fl_mem[k++]=e&~0x80000000;
+else
+{ int i;
+ i=e;
+ while (i>0)
+ {i--; k++;}
+}
+
+@*1 Profiling \.{PDF} Specific Macros.
+Macro packages providing \.{PDF} specific features typicaly test
+whether the current engine is generating \.{PDF} output
+before activating \.{PDF}-specific macros, and these macros
+then use \.{PDF}-specific primitives. To be able to profile
+these macros, the engine must provide (or at least pretend to provide)
+the necessary primitives to make the test for \.{PDF} output succeed
+and prevent the macros from failing because of undefined
+primitives or unexpected behaviour. Generating \.{PDF} ouput
+is usualy not required.
+
+The {\tt -pdf} option is designed to provide the necessary behaviour
+by implementing replacements for the primitives of \pdfTeX.
+
+@<explain the options@>=
+ " -pdf "@/
+ @t\qquad@>"\t pretend to be pdftex; requires -ini; implies -ltx\n"@/
+
+@ This option will set the |pdf_on| variable, defined below, to true.
+ The option requires the {\tt -ini} option because its effect is just
+ the definition of additional primitives during initialization. It further
+ implies the {\tt -ltx} option because the primitives necesary to
+ run \LaTeX\ are part of \pdfTeX.
+
+@<handle the options@>=
+else @+if (ARGUMENT_IS ("pdf")) @t\2@>
+ if (iniversion) pdf_on=ltxp=true;
+ else
+ { fprintf(stderr,"-pdf requires -ini; try -ini -pdf\n");
+ exit(1);
+ }
+
+@ @<more options@>=
+ { "pdf", 0, 0, 0 },
+
+@ @<Global variables@>=
+static int @!pdf_on=false;
+
+@ Let us start with implementing the \.{PDF} version primitive.
+This primitive uses the |last_item| command with a special modifier
+\pdfTeX\ adds the codes for its extensions: |pdftex_version_code|, \dots
+between \TeX's |badness_code| and \eTeX's |eTeX_int| codes.
+Some codes like |elapsed_time_code| or |random_seed_code| are already defined above
+but we duplicate them here for simplicity.
+So here is the pdf version as well as all of PDF\TeX's command codes
+for the |last_item| command:
+
+@d pdftex_version 140 /* \.{\\pdftexversion} */
+@d pdftex_revision "25" /* \.{\\pdftexrevision} */
+@d pdftex_version_string "-1.40.25" /*current \pdfTeX\ version*/
+@#
+@d pdftex_first_rint_code (badness_code+1) /*base for \pdfTeX's command codes*/
+@d pdftex_version_code (pdftex_first_rint_code+0) /*code for \.{\\pdftexversion}*/
+@d pdf_last_obj_code (pdftex_first_rint_code+1) /*code for \.{\\pdflastobj}*/
+@d pdf_last_xform_code (pdftex_first_rint_code+2) /*code for \.{\\pdflastxform}*/
+@d pdf_last_ximage_code (pdftex_first_rint_code+3) /*code for \.{\\pdflastximage}*/
+@d pdf_last_ximage_pages_code (pdftex_first_rint_code+4) /*code for \.{\\pdflastximagepages}*/
+@d pdf_last_annot_code (pdftex_first_rint_code+5) /*code for \.{\\pdflastannot}*/
+@d pdf_retval_code (pdftex_first_rint_code+6) /*global multi-purpose return value*/
+@d pdf_last_ximage_colordepth_code (pdftex_first_rint_code+7) /*code for \.{\\pdflastximagecolordepth}*/
+@d pdf_last_link_code (pdftex_first_rint_code+8) /*code for \.{\\pdflastlink}*/
+@d pdftex_last_item_codes (pdftex_first_rint_code+8) /*end of \pdfTeX's command codes*/
+
+@ Using these codes we can add the primitives:
+
+@<Put each...@>=
+if (pdf_on)
+{
+primitive("pdftexversion", last_item, pdftex_version_code);@/
+@!@:pdftex\_version\_}{\.{\\pdftexversion} primitive@>
+primitive("pdflastobj", last_item, pdf_last_obj_code);@/
+@!@:pdf\_last\_obj\_}{\.{\\pdflastobj} primitive@>
+primitive("pdflastxform", last_item, pdf_last_xform_code);@/
+@!@:pdf\_last\_xform\_}{\.{\\pdflastxform} primitive@>
+primitive("pdflastximage", last_item, pdf_last_ximage_code);@/
+@!@:pdf\_last\_ximage\_}{\.{\\pdflastximage} primitive@>
+primitive("pdflastximagepages", last_item, pdf_last_ximage_pages_code);@/
+@!@:pdf\_last\_ximage\_pages\_}{\.{\\pdflastximagepages} primitive@>
+primitive("pdflastannot", last_item, pdf_last_annot_code);@/
+@!@:pdf\_last\_annot\_}{\.{\\pdflastannot} primitive@>
+primitive("pdfretval", last_item, pdf_retval_code);@/
+@!@:pdf\_retval\_}{\.{\\pdfretval} primitive@>
+primitive("pdflastximagecolordepth", last_item, pdf_last_ximage_colordepth_code);@/
+@!@:pdf\_last\_ximage\_colordepth\_}{\.{\\pdflastximagecolordepth} primitive@>
+primitive("pdflastlink", last_item, pdf_last_link_code);@/
+@!@:pdf\_last\_link\_}{\.{\\pdflastlink} primitive@>
+}
+
+@ Next we need to print the commands:
+
+@<Cases of |last_item| for |print_cmd_chr|@>=
+ case pdftex_version_code: print_esc("pdftexversion");@+break;
+ case pdf_last_obj_code: print_esc("pdflastobj");@+break;
+ case pdf_last_xform_code: print_esc("pdflastxform");@+break;
+ case pdf_last_ximage_code: print_esc("pdflastximage");@+break;
+ case pdf_last_ximage_pages_code: print_esc("pdflastximagepages");@+break;
+ case pdf_last_annot_code: print_esc("pdflastannot");@+break;
+ case pdf_retval_code: print_esc("pdfretval");@+break;
+ case pdf_last_ximage_colordepth_code: print_esc("pdflastximagecolordepth");@+break;
+ case pdf_last_link_code: print_esc("pdflastlink");@+break;
+
+@ The implementation needs a few global variables.
+
+@<Glob...@>=
+static int
+@!pdf_last_obj,
+@!pdf_last_xform,
+@!pdf_last_ximage,
+@!pdf_last_ximage_pages,
+@!pdf_last_annot,
+@!pdf_retval,
+@!pdf_last_ximage_colordepth,
+@!pdf_last_link;
+
+
+@ And this is sufficient to finish the implementation of this first batch of primitives:
+@<Cases for fetching an integer value@>=
+ case pdftex_version_code: cur_val=pdftex_version;@+break;
+ case pdf_last_obj_code: cur_val=pdf_last_obj;@+break;
+ case pdf_last_xform_code: cur_val=pdf_last_xform;@+break;
+ case pdf_last_ximage_code: cur_val=pdf_last_ximage;@+break;
+ case pdf_last_ximage_pages_code: cur_val=pdf_last_ximage_pages;@+break;
+ case pdf_last_annot_code: cur_val=pdf_last_annot;@+break;
+ case pdf_retval_code: cur_val=pdf_retval;@+break;
+ case pdf_last_ximage_colordepth_code: cur_val=pdf_last_ximage_colordepth;@+break;
+ case pdf_last_link_code: cur_val=pdf_last_link;@+break;
+
+
+@ The next group of primitves uses the assignment commands. We start
+by defining the necessary command codes followed by the primitives.
+
+@d pdftex_first_loc tex_toks /*base for \pdfTeX's token list parameters*/
+@d pdf_pages_attr_loc (pdftex_first_loc+0) /*points to token list for \.{\\pdfpagesattr}*/
+@d pdf_page_attr_loc (pdftex_first_loc+1) /*points to token list for \.{\\pdfpageattr}*/
+@d pdf_page_resources_loc (pdftex_first_loc+2) /*points to token list for \.{\\pdfpageresources}*/
+@d pdf_pk_mode_loc (pdftex_first_loc+3) /*points to token list for \.{\\pdfpkmode}*/
+@d pdf_toks (pdftex_first_loc+4) /*end of \pdfTeX's token list parameters*/
+@#
+@d pdftex_first_integer_code tex_int_pars /*base for \pdfTeX's integer parameters*/
+@d pdf_output_code (pdftex_first_integer_code+0) /*switch on PDF output if positive*/
+@d pdf_compress_level_code (pdftex_first_integer_code+1) /*compress level of streams*/
+@d pdf_decimal_digits_code (pdftex_first_integer_code+2) /*digits after the decimal point of numbers*/
+@d pdf_move_chars_code (pdftex_first_integer_code+3) /*move chars 0..31 to higher area if possible*/
+@d pdf_image_resolution_code (pdftex_first_integer_code+4) /*default image resolution*/
+@d pdf_pk_resolution_code (pdftex_first_integer_code+5) /*default resolution of PK font*/
+@d pdf_unique_resname_code (pdftex_first_integer_code+6) /*generate unique names for resouces*/
+@d pdf_option_always_use_pdfpagebox_code (pdftex_first_integer_code+7) /*if the PDF inclusion should always use a specific PDF page box*/
+@d pdf_option_pdf_inclusion_errorlevel_code (pdftex_first_integer_code+8) /*if the PDF inclusion should treat pdfs newer than |pdf_minor_version| as an error*/
+@d pdf_major_version_code (pdftex_first_integer_code+9) /*integer part of the PDF version produced*/
+@d pdf_minor_version_code (pdftex_first_integer_code+10) /*fractional part of the PDF version produced*/
+@d pdf_force_pagebox_code (pdftex_first_integer_code+11) /*if the PDF inclusion should always use a specific PDF page box*/
+@d pdf_pagebox_code (pdftex_first_integer_code+12) /*default pagebox to use for PDF inclusion*/
+@d pdf_inclusion_errorlevel_code (pdftex_first_integer_code+13) /*if the PDF inclusion should treat pdfs newer than |pdf_minor_version| as an error*/
+@d pdf_gamma_code (pdftex_first_integer_code+14)
+@d pdf_image_gamma_code (pdftex_first_integer_code+15)
+@d pdf_image_hicolor_code (pdftex_first_integer_code+16)
+@d pdf_image_apply_gamma_code (pdftex_first_integer_code+17)
+@d pdf_adjust_spacing_code (pdftex_first_integer_code+18) /*level of spacing adjusting*/
+@d pdf_protrude_chars_code (pdftex_first_integer_code+19) /*protrude chars at left/right edge of paragraphs*/
+@d pdf_tracing_fonts_code (pdftex_first_integer_code+20) /*level of font detail in log*/
+@d pdf_objcompresslevel_code (pdftex_first_integer_code+21) /*activate object streams*/
+@d pdf_adjust_interword_glue_code (pdftex_first_integer_code+22) /*adjust interword glue?*/
+@d pdf_prepend_kern_code (pdftex_first_integer_code+23) /*prepend kern before certain characters?*/
+@d pdf_append_kern_code (pdftex_first_integer_code+24) /*append kern before certain characters?*/
+@d pdf_gen_tounicode_code (pdftex_first_integer_code+25) /*generate ToUnicode for fonts?*/
+@d pdf_draftmode_code (pdftex_first_integer_code+26) /*switch on draftmode if positive*/
+@d pdf_inclusion_copy_font_code (pdftex_first_integer_code+27) /*generate ToUnicode for fonts?*/
+@d pdf_suppress_warning_dup_dest_code (pdftex_first_integer_code+28) /*suppress warning about duplicated destinations*/
+@d pdf_suppress_warning_dup_map_code (pdftex_first_integer_code+29) /*suppress warning about duplicated map lines*/
+@d pdf_suppress_warning_page_group_code (pdftex_first_integer_code+30) /*suppress warning about multiple pdfs with page group*/
+@d pdf_info_omit_date_code (pdftex_first_integer_code+31) /*omit generating CreationDate and ModDate*/
+@d pdf_suppress_ptex_info_code (pdftex_first_integer_code+32) /*suppress /PTEX.* entries in PDF dictionaries*/
+@d pdf_omit_charset_code (pdftex_first_integer_code+33) /*suppress /PTEX.* entries in PDF dictionaries*/
+@d pdf_omit_info_dict_code (pdftex_first_integer_code+34) /*suppress /PTEX.* entries in PDF dictionaries*/
+@d pdf_omit_procset_code (pdftex_first_integer_code+35) /*suppress /PTEX.* entries in PDF dictionaries*/
+@d pdf_int_pars (pdftex_first_integer_code+36) /*total number of \pdfTeX's integer parameters*/
+@#
+@d pdftex_first_dimen_code 23 /*first number defined in this section*/
+@d pdf_h_origin_code (pdftex_first_dimen_code+0) /*horigin of the PDF output*/
+@d pdf_v_origin_code (pdftex_first_dimen_code+1) /*vorigin of the PDF output*/
+@d pdf_link_margin_code (pdftex_first_dimen_code+2) /*link margin in the PDF output*/
+@d pdf_dest_margin_code (pdftex_first_dimen_code+3) /*dest margin in the PDF output*/
+@d pdf_thread_margin_code (pdftex_first_dimen_code+4) /*thread margin in the PDF output*/
+@d pdf_first_line_height_code (pdftex_first_dimen_code+5)
+@d pdf_last_line_depth_code (pdftex_first_dimen_code+6)
+@d pdf_each_line_height_code (pdftex_first_dimen_code+7)
+@d pdf_each_line_depth_code (pdftex_first_dimen_code+8)
+@d pdf_ignored_dimen_code (pdftex_first_dimen_code+9)
+@d pdf_px_dimen_code (pdftex_first_dimen_code+10)
+@d pdftex_last_dimen_code (pdftex_first_dimen_code+10) /*last number defined in this section*/
+@d dimen_pars (pdftex_last_dimen_code+1) /*total number of dimension parameters*/
+
+@<Put each...@>=
+if (pdf_on)
+{
+primitive("pdfpagesattr", assign_toks, pdf_pages_attr_loc);
+@!@:pdf\_pages\_attr\_}{\.{\\pdfpagesattr} primitive@>
+primitive("pdfpageattr", assign_toks, pdf_page_attr_loc);
+@!@:pdf\_page\_attr\_}{\.{\\pdfpageattr} primitive@>
+primitive("pdfpageresources", assign_toks, pdf_page_resources_loc);
+@!@:pdf\_page\_resources\_}{\.{\\pdfpageresources} primitive@>
+primitive("pdfpkmode", assign_toks, pdf_pk_mode_loc);
+@!@:pdf\_pk\_mode\_}{\.{\\pdfpkmode} primitive@>
+@#
+primitive("pdfoutput", assign_int, int_base+pdf_output_code);@/
+@!@:pdf\_output\_}{\.{\\pdfoutput} primitive@>
+primitive("pdfcompresslevel", assign_int, int_base+pdf_compress_level_code);@/
+@!@:pdf\_compress\_level\_}{\.{\\pdfcompresslevel} primitive@>
+primitive("pdfobjcompresslevel", assign_int, int_base+pdf_objcompresslevel_code);@/
+@!@:pdf\_objcompresslevel\_}{\.{\\pdfobjcompresslevel} primitive@>
+primitive("pdfdecimaldigits", assign_int, int_base+pdf_decimal_digits_code);@/
+@!@:pdf\_decimal\_digits\_}{\.{\\pdfdecimaldigits} primitive@>
+primitive("pdfmovechars", assign_int, int_base+pdf_move_chars_code);@/
+@!@:pdf\_move\_chars\_}{\.{\\pdfmovechars} primitive@>
+primitive("pdfimageresolution", assign_int, int_base+pdf_image_resolution_code);@/
+@!@:pdf\_image\_resolution\_}{\.{\\pdfimageresolution} primitive@>
+primitive("pdfpkresolution", assign_int, int_base+pdf_pk_resolution_code);@/
+@!@:pdf\_pk\_resolution\_}{\.{\\pdfpkresolution} primitive@>
+primitive("pdfuniqueresname", assign_int, int_base+pdf_unique_resname_code);@/
+@!@:pdf\_unique\_resname\_}{\.{\\pdfuniqueresname} primitive@>
+primitive("pdfoptionpdfminorversion", assign_int, int_base+pdf_minor_version_code);@/
+@!@:pdf\_minor\_version\_}{\.{\\pdfoptionpdfminorversion} primitive@>
+primitive("pdfoptionalwaysusepdfpagebox", assign_int, int_base+pdf_option_always_use_pdfpagebox_code);@/
+@!@:pdf\_option\_always\_use\_pdfpagebox\_}{\.{\\pdfoptionalwaysusepdfpagebox} primitive@>
+primitive("pdfoptionpdfinclusionerrorlevel", assign_int, int_base+pdf_option_pdf_inclusion_errorlevel_code);@/
+@!@:pdf\_option\_pdf\_inclusion\_errorlevel\_}{\.{\\pdfoptionpdfinclusionerrorlevel} primitive@>
+primitive("pdfmajorversion", assign_int, int_base+pdf_major_version_code);@/
+@!@:pdf\_major\_version\_}{\.{\\pdfmajorversion} primitive@>
+primitive("pdfminorversion", assign_int, int_base+pdf_minor_version_code);@/
+@!@:pdf\_minor\_version\_}{\.{\\pdfminorversion} primitive@>
+primitive("pdfforcepagebox", assign_int, int_base+pdf_force_pagebox_code);@/
+@!@:pdf\_force\_pagebox\_}{\.{\\pdfforcepagebox} primitive@>
+primitive("pdfpagebox", assign_int, int_base+pdf_pagebox_code);@/
+@!@:pdf\_pagebox\_}{\.{\\pdfpagebox} primitive@>
+primitive("pdfinclusionerrorlevel", assign_int, int_base+pdf_inclusion_errorlevel_code);@/
+@!@:pdf\_inclusion\_errorlevel\_}{\.{\\pdfinclusionerrorlevel} primitive@>
+primitive("pdfgamma", assign_int, int_base+pdf_gamma_code);@/
+@!@:pdf\_gamma\_}{\.{\\pdfgamma} primitive@>
+@!@:pdf\_gamma\_}{\.{\\pdfgamma} primitive@>
+primitive("pdfimagegamma", assign_int, int_base+pdf_image_gamma_code);@/
+@!@:pdf\_image\_gamma\_}{\.{\\pdfimagegamma} primitive@>
+primitive("pdfimagehicolor", assign_int, int_base+pdf_image_hicolor_code);@/
+@!@:pdf\_image\_hicolor\_}{\.{\\pdfimagehicolor} primitive@>
+primitive("pdfimageapplygamma", assign_int, int_base+pdf_image_apply_gamma_code);@/
+@!@:pdf\_image\_apply\_gamma\_}{\.{\\pdfimageapplygamma} primitive@>
+primitive("pdfadjustspacing", assign_int, int_base+pdf_adjust_spacing_code);@/
+@!@:pdf\_adjust\_spacing\_}{\.{\\pdfadjustspacing} primitive@>
+primitive("pdfprotrudechars", assign_int, int_base+pdf_protrude_chars_code);@/
+@!@:pdf\_protrude\_chars\_}{\.{\\pdfprotrudechars} primitive@>
+primitive("pdftracingfonts", assign_int, int_base+pdf_tracing_fonts_code);@/
+@!@:pdf\_tracing\_fonts\_}{\.{\\pdftracingfonts} primitive@>
+primitive("pdfadjustinterwordglue", assign_int, int_base+pdf_adjust_interword_glue_code);@/
+@!@:pdf\_adjust\_interword\_glue\_}{\.{\\pdfadjustinterwordglue} primitive@>
+primitive("pdfprependkern", assign_int, int_base+pdf_prepend_kern_code);@/
+@!@:pdf\_prepend\_kern\_}{\.{\\pdfprependkern} primitive@>
+primitive("pdfappendkern", assign_int, int_base+pdf_append_kern_code);@/
+@!@:pdf\_append\_kern\_}{\.{\\pdfappendkern} primitive@>
+primitive("pdfgentounicode", assign_int, int_base+pdf_gen_tounicode_code);@/
+@!@:pdf\_gen\_tounicode\_}{\.{\\pdfgentounicode} primitive@>
+primitive("pdfdraftmode", assign_int, int_base+pdf_draftmode_code);@/
+@!@:pdf\_draftmode\_}{\.{\\pdfdraftmode} primitive@>
+primitive("pdfinclusioncopyfonts", assign_int, int_base+pdf_inclusion_copy_font_code);@/
+@!@:pdf\_inclusion\_copy\_font\_}{\.{\\pdfinclusioncopyfonts} primitive@>
+primitive("pdfsuppresswarningdupdest", assign_int, int_base+pdf_suppress_warning_dup_dest_code);@/
+@!@:pdf\_suppress\_warning\_dup\_dest\_}{\.{\\pdfsuppresswarningdupdest} primitive@>
+primitive("pdfsuppresswarningdupmap", assign_int, int_base+pdf_suppress_warning_dup_map_code);@/
+@!@:pdf\_suppress\_warning\_dup\_map\_}{\.{\\pdfsuppresswarningdupmap} primitive@>
+primitive("pdfsuppresswarningpagegroup", assign_int, int_base+pdf_suppress_warning_page_group_code);@/
+@!@:pdf\_suppress\_warning\_page\_group\_}{\.{\\pdfsuppresswarningpagegroup} primitive@>
+primitive("pdfinfoomitdate", assign_int, int_base+pdf_info_omit_date_code);@/
+@!@:pdf\_info\_omit\_date\_}{\.{\\pdfinfoomitdate} primitive@>
+primitive("pdfsuppressptexinfo", assign_int, int_base+pdf_suppress_ptex_info_code);@/
+@!@:pdf\_suppress\_ptex\_info\_}{\.{\\pdfsuppressptexinfo} primitive@>
+primitive("pdfomitcharset", assign_int, int_base+pdf_omit_charset_code);@/
+@!@:pdf\_omit\_charset}{\.{\\pdfomitcharset} primitive@>
+primitive("pdfomitinfodict", assign_int, int_base+pdf_omit_info_dict_code);@/
+@!@:pdf\_omit\_info\_dict}{\.{\\pdfomitinfodict} primitive@>
+primitive("pdfomitprocset", assign_int, int_base+pdf_omit_procset_code);@/
+@!@:pdf\_omit\_procset}{\.{\\pdfomitprocset} primitive@>
+@#
+primitive("pdfhorigin", assign_dimen, dimen_base+pdf_h_origin_code);@/
+@!@:pdf\_h\_origin\_}{\.{\\pdfhorigin} primitive@>
+primitive("pdfvorigin", assign_dimen, dimen_base+pdf_v_origin_code);@/
+@!@:pdf\_v\_origin\_}{\.{\\pdfvorigin} primitive@>
+primitive("pdflinkmargin", assign_dimen, dimen_base+pdf_link_margin_code);@/
+@!@:pdf\_link\_margin\_}{\.{\\pdflinkmargin} primitive@>
+primitive("pdfdestmargin", assign_dimen, dimen_base+pdf_dest_margin_code);@/
+@!@:pdf\_dest\_margin\_}{\.{\\pdfdestmargin} primitive@>
+primitive("pdfthreadmargin", assign_dimen, dimen_base+pdf_thread_margin_code);@/
+@!@:pdf\_thread\_margin\_}{\.{\\pdfthreadmargin} primitive@>
+primitive("pdffirstlineheight", assign_dimen, dimen_base+pdf_first_line_height_code);@/
+@!@:pdf\_first\_line\_height\_}{\.{\\pdffirstlineheight} primitive@>
+primitive("pdflastlinedepth", assign_dimen, dimen_base+pdf_last_line_depth_code);@/
+@!@:pdf\_last\_line\_depth\_}{\.{\\pdflastlinedepth} primitive@>
+primitive("pdfeachlineheight", assign_dimen, dimen_base+pdf_each_line_height_code);@/
+@!@:pdf\_each\_line\_height\_}{\.{\\pdfeachlineheight} primitive@>
+primitive("pdfeachlinedepth", assign_dimen, dimen_base+pdf_each_line_depth_code);@/
+@!@:pdf\_each\_line\_depth\_}{\.{\\pdfeachlinedepth} primitive@>
+primitive("pdfignoreddimen", assign_dimen, dimen_base+pdf_ignored_dimen_code);@/
+@!@:pdf\_ignored\_dimen\_}{\.{\\pdfignoreddimen} primitive@>
+primitive("pdfpxdimen", assign_dimen, dimen_base+pdf_px_dimen_code);@/
+@!@:pdf\_px\_dimen\_}{\.{\\pdfpxdimen} primitive@>
+primitive("ifpdfprimitive", if_test, if_primitive_code);
+@!@:if\_primitive\_}{\.{\\if} primitive@>
+
+
+}
+
+@ As before we add cases to the printing routines.
+
+@<Cases of |assign_toks| for |print_cmd_chr|@>=
+ case pdf_pages_attr_loc: print_esc("pdfpagesattr");@+break;
+ case pdf_page_attr_loc: print_esc("pdfpageattr");@+break;
+ case pdf_page_resources_loc: print_esc("pdfpageresources");@+break;
+ case pdf_pk_mode_loc: print_esc("pdfpkmode");@+break;
+
+@ @<Cases for |print_param|@>=
+case pdf_output_code: print_esc("pdfoutput");@+break;
+case pdf_compress_level_code: print_esc("pdfcompresslevel");@+break;
+case pdf_objcompresslevel_code: print_esc("pdfobjcompresslevel");@+break;
+case pdf_decimal_digits_code: print_esc("pdfdecimaldigits");@+break;
+case pdf_move_chars_code: print_esc("pdfmovechars");@+break;
+case pdf_image_resolution_code: print_esc("pdfimageresolution");@+break;
+case pdf_pk_resolution_code: print_esc("pdfpkresolution");@+break;
+case pdf_unique_resname_code: print_esc("pdfuniqueresname");@+break;
+case pdf_option_always_use_pdfpagebox_code: print_esc("pdfoptionalwaysusepdfpagebox");@+break;
+case pdf_option_pdf_inclusion_errorlevel_code: print_esc("pdfoptionpdfinclusionerrorlevel");@+break;
+case pdf_major_version_code: print_esc("pdfmajorversion");@+break;
+case pdf_minor_version_code: print_esc("pdfminorversion");@+break;
+case pdf_force_pagebox_code: print_esc("pdfforcepagebox");@+break;
+case pdf_pagebox_code: print_esc("pdfpagebox");@+break;
+case pdf_inclusion_errorlevel_code: print_esc("pdfinclusionerrorlevel");@+break;
+case pdf_gamma_code: print_esc("pdfgamma");@+break;
+case pdf_image_gamma_code: print_esc("pdfimagegamma");@+break;
+case pdf_image_hicolor_code: print_esc("pdfimagehicolor");@+break;
+case pdf_image_apply_gamma_code: print_esc("pdfimageapplygamma");@+break;
+case pdf_adjust_spacing_code: print_esc("pdfadjustspacing");@+break;
+case pdf_protrude_chars_code: print_esc("pdfprotrudechars");@+break;
+case pdf_tracing_fonts_code: print_esc("pdftracingfonts");@+break;
+case pdf_adjust_interword_glue_code: print_esc("pdfadjustinterwordglue");@+break;
+case pdf_prepend_kern_code: print_esc("pdfprependkern");@+break;
+case pdf_append_kern_code: print_esc("pdfappendkern");@+break;
+case pdf_gen_tounicode_code: print_esc("pdfgentounicode");@+break;
+case pdf_draftmode_code: print_esc("pdfdraftmode");@+break;
+case pdf_inclusion_copy_font_code: print_esc("pdfinclusioncopyfonts");@+break;
+case pdf_suppress_warning_dup_dest_code: print_esc("pdfsuppresswarningdupdest");@+break;
+case pdf_suppress_warning_dup_map_code: print_esc("pdfsuppresswarningdupmap");@+break;
+case pdf_suppress_warning_page_group_code: print_esc("pdfsuppresswarningpagegroup");@+break;
+case pdf_info_omit_date_code: print_esc("pdfinfoomitdate");@+break;
+case pdf_suppress_ptex_info_code: print_esc("pdfsuppressptexinfo");@+break;
+case pdf_omit_charset_code: print_esc("pdfomitcharset");@+break;
+case pdf_omit_info_dict_code: print_esc("pdfomitinfodict");@+break;
+case pdf_omit_procset_code: print_esc("pdfomitprocset");@+break;
+
+@ @<Cases for |print_lenght_param|@>=
+case pdf_h_origin_code: print_esc("pdfhorigin");@+break;
+case pdf_v_origin_code: print_esc("pdfvorigin");@+break;
+case pdf_link_margin_code: print_esc("pdflinkmargin");@+break;
+case pdf_dest_margin_code: print_esc("pdfdestmargin");@+break;
+case pdf_thread_margin_code: print_esc("pdfthreadmargin");@+break;
+case pdf_first_line_height_code: print_esc("pdffirstlineheight");@+break;
+case pdf_last_line_depth_code: print_esc("pdflastlinedepth");@+break;
+case pdf_each_line_height_code: print_esc("pdfeachlineheight");@+break;
+case pdf_each_line_depth_code: print_esc("pdfeachlinedepth");@+break;
+case pdf_ignored_dimen_code: print_esc("pdfignoreddimen");@+break;
+case pdf_px_dimen_code: print_esc("pdfpxdimen");@+break;
+
+@ It remains to initialize some of these entries.
+
+@<Initialize table entries...@>=
+dimen_par(pdf_h_origin_code)=4736287; /* 0.5 inch */
+dimen_par(pdf_v_origin_code)=4736287;
+int_par(pdf_compress_level_code)=9;
+int_par(pdf_objcompresslevel_code)=0;
+int_par(pdf_decimal_digits_code)=3;
+int_par(pdf_image_resolution_code)=72;
+int_par(pdf_major_version_code)=1;
+int_par(pdf_minor_version_code)=4;
+int_par(pdf_gamma_code)=1000;
+int_par(pdf_image_gamma_code)=2200;
+int_par(pdf_image_hicolor_code)=1;
+int_par(pdf_image_apply_gamma_code)=0;
+int_par(pdf_draftmode_code)=0;
+dimen_par(pdf_px_dimen_code)=65782; /* 1bp */
+
+@ The next class of primitives extend the |convert| comand.
+These codes go in between |Prote_last_convert_code| and |job_name_code|.
+
+@d pdftex_first_convert_code (Prote_last_convert_code+1) /*base for \pdfTeX's command codes*/
+@d pdftex_revision_code (pdftex_first_convert_code+0) /*command code for \.{\\pdftexrevision}*/
+@d pdftex_banner_code (pdftex_first_convert_code+1) /*command code for \.{\\pdftexbanner}*/
+@d pdf_font_name_code (pdftex_first_convert_code+2) /*command code for \.{\\pdffontname}*/
+@d pdf_font_objnum_code (pdftex_first_convert_code+3) /*command code for \.{\\pdffontobjnum}*/
+@d pdf_font_size_code (pdftex_first_convert_code+4) /*command code for \.{\\pdffontsize}*/
+@d pdf_page_ref_code (pdftex_first_convert_code+5) /*command code for \.{\\pdfpageref}*/
+@d pdf_xform_name_code (pdftex_first_convert_code+6) /*command code for \.{\\pdfxformname}*/
+@d pdf_escape_string_code (pdftex_first_convert_code+7) /*command code for \.{\\pdfescapestring}*/
+@d pdf_escape_name_code (pdftex_first_convert_code+8) /*command code for \.{\\pdfescapename}*/
+@d left_margin_kern_code (pdftex_first_convert_code+9) /*command code for \.{\\leftmarginkern}*/
+@d right_margin_kern_code (pdftex_first_convert_code+10) /*command code for \.{\\rightmarginkern}*/
+@d pdf_colorstack_init_code (pdftex_first_convert_code+11) /*command code for \.{\\pdfcolorstackinit}*/
+@d pdf_escape_hex_code (pdftex_first_convert_code+12) /*command code for \.{\\pdfescapehex}*/
+@d pdf_unescape_hex_code (pdftex_first_convert_code+13) /*command code for \.{\\pdfunescapehex}*/
+@d pdf_match_code (pdftex_first_convert_code+14) /*command code for \.{\\pdfmatch}*/
+@d pdf_last_match_code (pdftex_first_convert_code+15) /*command code for \.{\\pdflastmatch}*/
+@d pdf_insert_ht_code (pdftex_first_convert_code+16) /*command code for \.{\\pdfinsertht}*/
+@d pdf_ximage_bbox_code (pdftex_first_convert_code+17) /*command code for \.{\\pdfximagebbox}*/
+@d pdftex_convert_codes (pdftex_first_convert_code+18) /*end of \pdfTeX's command codes*/
+
+
+@<Put each...@>=
+if (pdf_on)
+{
+primitive("pdftexrevision", convert, pdftex_revision_code);@/
+@!@:pdftex\_revision\_}{\.{\\pdftexrevision} primitive@>
+primitive("pdftexbanner", convert, pdftex_banner_code);@/
+@!@:pdftex\_banner\_}{\.{\\pdftexbanner} primitive@>
+primitive("pdffontname", convert, pdf_font_name_code);@/
+@!@:pdf\_font\_name\_}{\.{\\pdffontname} primitive@>
+primitive("pdffontobjnum", convert, pdf_font_objnum_code);@/
+@!@:pdf\_font\_objnum\_}{\.{\\pdffontobjnum} primitive@>
+primitive("pdffontsize", convert, pdf_font_size_code);@/
+@!@:pdf\_font\_size\_}{\.{\\pdffontsize} primitive@>
+primitive("pdfpageref", convert, pdf_page_ref_code);@/
+@!@:pdf\_page\_ref\_}{\.{\\pdfpageref} primitive@>
+primitive("leftmarginkern", convert, left_margin_kern_code);@/
+@!@:left\_margin\_kern\_}{\.{\\leftmarginkern} primitive@>
+primitive("rightmarginkern", convert, right_margin_kern_code);@/
+@!@:right\_margin\_kern\_}{\.{\\rightmarginkern} primitive@>
+primitive("pdfxformname", convert, pdf_xform_name_code);@/
+@!@:pdf\_xform\_name\_}{\.{\\pdfxformname} primitive@>
+primitive("pdfescapestring", convert, pdf_escape_string_code);@/
+@!@:pdf\_escape\_string\_}{\.{\\pdfescapestring} primitive@>
+primitive("pdfescapename", convert, pdf_escape_name_code);@/
+@!@:pdf\_escape\_name\_}{\.{\\pdfescapename} primitive@>
+primitive("pdfescapehex", convert, pdf_escape_hex_code);@/
+@!@:pdf\_escape\_hex\_}{\.{\\pdfescapehex} primitive@>
+primitive("pdfunescapehex", convert, pdf_unescape_hex_code);@/
+@!@:pdf\_unescape\_hex\_}{\.{\\pdfunescapehex} primitive@>
+primitive("pdfmatch", convert, pdf_match_code);@/
+@!@:pdf\_match\_}{\.{\\pdfmatch} primitive@>
+primitive("pdflastmatch", convert, pdf_last_match_code);@/
+@!@:pdf\_last\_match\_}{\.{\\pdflastmatch} primitive@>
+primitive("pdfcolorstackinit", convert, pdf_colorstack_init_code);@/
+@!@:pdf\_colorstack\_init\_}{\.{\\pdfcolorstackinit} primitive@>
+}
+
+@ The usual code for printing:
+
+@<Cases of |convert| for |print_cmd_chr|@>=
+ case pdftex_revision_code: print_esc("pdftexrevision");@+break;
+ case pdftex_banner_code: print_esc("pdftexbanner");@+break;
+ case pdf_font_name_code: print_esc("pdffontname");@+break;
+ case pdf_font_objnum_code: print_esc("pdffontobjnum");@+break;
+ case pdf_font_size_code: print_esc("pdffontsize");@+break;
+ case pdf_page_ref_code: print_esc("pdfpageref");@+break;
+ case left_margin_kern_code: print_esc("leftmarginkern");@+break;
+ case right_margin_kern_code: print_esc("rightmarginkern");@+break;
+ case pdf_xform_name_code: print_esc("pdfxformname");@+break;
+ case pdf_escape_string_code: print_esc("pdfescapestring");@+break;
+ case pdf_escape_name_code: print_esc("pdfescapename");@+break;
+ case pdf_escape_hex_code: print_esc("pdfescapehex");@+break;
+ case pdf_unescape_hex_code: print_esc("pdfunescapehex");@+break;
+ case pdf_match_code: print_esc("pdfmatch");@+break;
+ case pdf_last_match_code: print_esc("pdflastmatch");@+break;
+ case pdf_colorstack_init_code: print_esc("pdfcolorstackinit");@+break;
+ case pdf_insert_ht_code: print_esc("pdfinsertht");@+break;
+ case pdf_ximage_bbox_code: print_esc("pdfximagebbox");@+break;
+
+@ The following code is taken from pdftex. At this point, I should drop
+some of the primitives that I do not want to implement.
+To skip general text we use the following macro
+
+
+@<Cases of `Scan the argument for command |c|'@>=
+case pdftex_revision_code: do_nothing;@+break;
+case pdftex_banner_code: do_nothing;@+break;
+case pdf_font_name_code: case pdf_font_objnum_code:
+ case pdf_font_size_code: {@+
+ scan_font_ident();
+ if (cur_val==null_font)
+ pdf_error("font","invalid font identifier");
+} @+break;
+case pdf_page_ref_code: {@+
+ scan_int();
+ if (cur_val <= 0)
+ pdf_error("pageref","invalid page number");
+} @+break;
+case left_margin_kern_code: case right_margin_kern_code: {@+
+ pointer p;
+ scan_register_num();
+ fetch_box(p);
+ if ((p==null)||(type(p)!=hlist_node))
+ pdf_error("marginkern","a non-empty hbox expected");
+} @+break;
+case pdf_xform_name_code: {@+
+ scan_int();
+} @+break;
+case pdf_escape_string_code:
+case pdf_escape_name_code:
+case pdf_escape_hex_code:
+case pdf_unescape_hex_code:
+ scan_general_x_text(); /*the simplified version*/
+ ins_list(link(link(garbage)));
+ free_avail(link(garbage)); /*drop reference count*/
+ return;
+case pdf_match_code:
+ scan_keyword("icase");
+ if (scan_keyword("subcount")) scan_int();
+ scan_pdf_ext_toks(); /*the simplified version*/
+ scan_pdf_ext_toks(); /*the simplified version*/
+ break;
+case pdf_last_match_code:
+ {@+pointer p,q;
+ scan_int();
+ /* always returning ``\.{-1->}''*/
+ p=temp_head;link(p)=null;
+ store_new_token(other_token+'-');
+ store_new_token(other_token+'1');
+ store_new_token(other_token+'-');
+ store_new_token(other_token+'>');
+ ins_list(link(temp_head));
+ return;
+ }
+case pdf_colorstack_init_code:
+ {@+
+ scan_keyword("page");
+ if (!scan_keyword("direct"))
+ scan_keyword("page");
+ scan_pdf_ext_toks(); /*the simplified version*/
+ cur_val=0;
+ } @+break;
+case pdf_insert_ht_code: scan_register_num();@+break;
+case pdf_ximage_bbox_code: {@+
+ scan_int();
+ scan_int();
+}@+break;
+
+
+@ This is the ``dummy'' |scan_pdf_ext_toks| routine we used above
+and a variation of it which we need later.
+
+@<Declare procedures needed in |do_ext...@>=
+static void scan_pdf_ext_toks(void)
+{ scan_toks(false,true); /*like \.{\\special}*/
+ flush_list(def_ref);
+}
+static void scan_pdf_ext_late_toks(void)
+{ scan_toks(false, false); /*like \.{\\special}, but doesn't expand*/
+ flush_list(def_ref);
+}
+
+
+@ The second part of handling conversion commands is the printing of the result.
+
+@<Cases of `Print the result of command |c|'@>=
+case pdftex_revision_code: print(pdftex_revision);@+break;
+case pdftex_banner_code: print("This is pdfTeX, Version 3.141592653");@+break;
+case pdf_font_name_code: case pdf_font_objnum_code: print_int(cur_val);@+break;
+case pdf_font_size_code:
+ print_scaled(font_size[cur_val]);
+ print("pt");@+break;
+case pdf_page_ref_code: print_int(1);@+break;
+case pdf_xform_name_code: print_int(cur_val);@+break;
+case pdf_escape_string_code:
+case pdf_escape_name_code:
+case left_margin_kern_code: print("0pt");@+break;
+case right_margin_kern_code: print("0pt");@+break;
+case pdf_colorstack_init_code: print_int(cur_val);@+break;
+case pdf_escape_hex_code:
+case pdf_unescape_hex_code:
+case pdf_match_code:
+case pdf_last_match_code:
+case pdf_insert_ht_code: print("10pt");@+break;
+case pdf_ximage_bbox_code: print("10pt");@+break;
+
+
+@ A few functions are neede to complete the implementation of the \pdfTeX\ simulation.
+
+
+@<Error handling procedures@>=
+static void pdf_error(char *t, char *p)
+{@+
+ normalize_selector();
+ print_err("pdfTeX error");
+ if (t!=0) {@+
+ print(" (");
+ print(t);
+ print(")");
+ }
+ print(": ");print(p);
+ succumb;
+}
+
+
+@ The last command we need to consider is the |extension| command.
+
+@d pdf_literal_node (pdftex_first_extension_code+0)
+
+@d pdf_lateliteral_node (pdftex_first_extension_code+1) /*Not needed*/
+
+@d pdf_obj_code (pdftex_first_extension_code+2)
+@d pdf_refobj_node (pdftex_first_extension_code+3)
+@d pdf_xform_code (pdftex_first_extension_code+4)
+@d pdf_refxform_node (pdftex_first_extension_code+5)
+@d pdf_ximage_code (pdftex_first_extension_code+6)
+@d pdf_refximage_node (pdftex_first_extension_code+7)
+@d pdf_annot_node (pdftex_first_extension_code+8)
+@d pdf_start_link_node (pdftex_first_extension_code+9)
+@d pdf_end_link_node (pdftex_first_extension_code+10)
+@d pdf_outline_code (pdftex_first_extension_code+11)
+@d pdf_dest_node (pdftex_first_extension_code+12)
+@d pdf_thread_node (pdftex_first_extension_code+13)
+@d pdf_start_thread_node (pdftex_first_extension_code+14)
+@d pdf_end_thread_node (pdftex_first_extension_code+15)
+
+@d pdf_info_code (pdftex_first_extension_code+16)
+@d pdf_catalog_code (pdftex_first_extension_code+17)
+@d pdf_names_code (pdftex_first_extension_code+18)
+
+@d pdf_font_attr_code (pdftex_first_extension_code+19)
+@d pdf_include_chars_code (pdftex_first_extension_code+20)
+@d pdf_map_file_code (pdftex_first_extension_code+21)
+@d pdf_map_line_code (pdftex_first_extension_code+22)
+@d pdf_trailer_code (pdftex_first_extension_code+23)
+@d pdf_trailer_id_code (pdftex_first_extension_code+24)
+
+@d pdf_font_expand_code (pdftex_first_extension_code+25)
+
+@d pdf_snap_ref_point_node (pdftex_first_extension_code+26)
+@d pdf_snapy_node (pdftex_first_extension_code+27)
+@d pdf_snapy_comp_node (pdftex_first_extension_code+28)
+@d pdf_glyph_to_unicode_code (pdftex_first_extension_code+29)
+@d pdf_colorstack_node (pdftex_first_extension_code+30)
+@d pdf_setmatrix_node (pdftex_first_extension_code+31)
+@d pdf_save_node (pdftex_first_extension_code+32)
+@d pdf_restore_node (pdftex_first_extension_code+33)
+@d pdf_nobuiltin_tounicode_code (pdftex_first_extension_code+34)
+@d pdf_interword_space_on_node (pdftex_first_extension_code+35)
+@d pdf_interword_space_off_node (pdftex_first_extension_code+36)
+@d pdf_fake_space_node (pdftex_first_extension_code+37)
+@d pdf_running_link_off_node (pdftex_first_extension_code+38)
+@d pdf_running_link_on_node (pdftex_first_extension_code+39)
+@d pdf_space_font_code (pdftex_first_extension_code+40)
+@#
+@d tracing_stack_level_code (pdftex_first_extension_code+41)
+@d pdf_par_token_context_code (pdftex_first_extension_code+42)
+@d pdf_par_token_name_code (pdftex_first_extension_code+43)
+@d pdf_show_stream_code (pdftex_first_extension_code+44)
+@#
+@d pdftex_last_extension_code (pdftex_first_extension_code+44)
+
+@<Put each...@>=
+if (pdf_on)
+{
+primitive("pdfliteral", extension, pdf_literal_node);@/
+@!@:pdf\_literal\_}{\.{\\pdfliteral} primitive@>
+primitive("pdfobj", extension, pdf_obj_code);@/
+@!@:pdf\_obj\_}{\.{\\pdfobj} primitive@>
+primitive("pdfrefobj", extension, pdf_refobj_node);@/
+@!@:pdf\_refobj\_}{\.{\\pdfrefobj} primitive@>
+primitive("pdfxform", extension, pdf_xform_code);@/
+@!@:pdf\_xform\_}{\.{\\pdfxform} primitive@>
+primitive("pdfrefxform", extension, pdf_refxform_node);@/
+@!@:pdf\_refxform\_}{\.{\\pdfrefxform} primitive@>
+primitive("pdfximage", extension, pdf_ximage_code);@/
+@!@:pdf\_ximage\_}{\.{\\pdfximage} primitive@>
+primitive("pdfrefximage", extension, pdf_refximage_node);@/
+@!@:pdf\_refximage\_}{\.{\\pdfrefximage} primitive@>
+primitive("pdfannot", extension, pdf_annot_node);@/
+@!@:pdf\_annot\_}{\.{\\pdfannot} primitive@>
+primitive("pdfstartlink", extension, pdf_start_link_node);@/
+@!@:pdf\_start\_link\_}{\.{\\pdfstartlink} primitive@>
+primitive("pdfendlink", extension, pdf_end_link_node);@/
+@!@:pdf\_end\_link\_}{\.{\\pdfendlink} primitive@>
+primitive("pdfoutline", extension, pdf_outline_code);@/
+@!@:pdf\_outline\_}{\.{\\pdfoutline} primitive@>
+primitive("pdfdest", extension, pdf_dest_node);@/
+@!@:pdf\_dest\_}{\.{\\pdfdest} primitive@>
+primitive("pdfthread", extension, pdf_thread_node);@/
+@!@:pdf\_thread\_}{\.{\\pdfthread} primitive@>
+primitive("pdfstartthread", extension, pdf_start_thread_node);@/
+@!@:pdf\_start\_thread\_}{\.{\\pdfstartthread} primitive@>
+primitive("pdfendthread", extension, pdf_end_thread_node);@/
+@!@:pdf\_end\_thread\_}{\.{\\pdfendthread} primitive@>
+
+primitive("pdfinfo", extension, pdf_info_code);@/
+@!@:pdf\_info\_}{\.{\\pdfinfo} primitive@>
+primitive("pdfcatalog", extension, pdf_catalog_code);@/
+@!@:pdf\_catalog\_}{\.{\\pdfcatalog} primitive@>
+primitive("pdfnames", extension, pdf_names_code);@/
+@!@:pdf\_names\_}{\.{\\pdfnames} primitive@>
+
+primitive("pdffontattr", extension, pdf_font_attr_code);@/
+@!@:pdf\_font\_attr\_}{\.{\\pdffontattr} primitive@>
+primitive("pdfincludechars", extension, pdf_include_chars_code);@/
+@!@:pdf\_include\_chars\_}{\.{\\pdfincludechars} primitive@>
+primitive("pdfmapfile", extension, pdf_map_file_code);@/
+@!@:pdf\_map\_file\_}{\.{\\pdfmapfile} primitive@>
+primitive("pdfmapline", extension, pdf_map_line_code);@/
+@!@:pdf\_map\_line\_}{\.{\\pdfmapline} primitive@>
+primitive("pdftrailer", extension, pdf_trailer_code);@/
+@!@:pdf\_trailer\_}{\.{\\pdftrailer} primitive@>
+primitive("pdftrailerid", extension, pdf_trailer_id_code);@/
+@!@:pdf\_trailer\_id\_}{\.{\\pdftrailerid} primitive@>
+
+primitive("pdffontexpand", extension, pdf_font_expand_code);@/
+@!@:pdf\_font\_expand\_}{\.{\\pdffontexpand} primitive@>
+
+primitive("pdfsnaprefpoint", extension, pdf_snap_ref_point_node);@/
+@!@:pdf\_snap\_ref\_point\_}{\.{\\pdfsnaprefpoint} primitive@>
+primitive("pdfsnapy", extension, pdf_snapy_node);@/
+@!@:pdf\_snapy\_}{\.{\\pdfsnapy} primitive@>
+primitive("pdfsnapycomp", extension, pdf_snapy_comp_node);@/
+@!@:pdf\_snapy\_comp\_}{\.{\\pdfsnapycomp} primitive@>
+primitive("pdfglyphtounicode", extension, pdf_glyph_to_unicode_code);@/
+@!@:pdf\_glyph\_to\_unicode\_}{\.{\\pdfglyphtounicode} primitive@>
+primitive("pdfcolorstack", extension, pdf_colorstack_node);@/
+@!@:pdf\_colorstack\_}{\.{\\pdfcolorstack} primitive@>
+primitive("pdfsetmatrix", extension, pdf_setmatrix_node);@/
+@!@:pdf\_setmatrix\_}{\.{\\pdfsetmatrix} primitive@>
+primitive("pdfsave", extension, pdf_save_node);@/
+@!@:pdf\_save\_}{\.{\\pdfsave} primitive@>
+primitive("pdfrestore", extension, pdf_restore_node);@/
+@!@:pdf\_restore\_}{\.{\\pdfrestore} primitive@>
+primitive("pdfnobuiltintounicode", extension, pdf_nobuiltin_tounicode_code);@/
+@!@:pdf\_nobuiltin\_tounicode\_}{\.{\\pdfnobuiltintounicode} primitive@>
+primitive("pdfinterwordspaceon", extension, pdf_interword_space_on_node);@/
+@!@:pdf\_interword\_space\_on\_}{\.{\\pdfinterwordspaceon} primitive@>
+primitive("pdfinterwordspaceoff", extension, pdf_interword_space_off_node);@/
+@!@:pdf\_interword\_space\_off\_}{\.{\\pdfinterwordspaceoff} primitive@>
+primitive("pdffakespace", extension, pdf_fake_space_node);@/
+@!@:pdf\_fake\_space\_}{\.{\\pdffakespace} primitive@>
+primitive("pdfrunninglinkoff", extension, pdf_running_link_off_node);@/
+@!@:pdf\_running\_link\_off\_}{\.{\\pdfrunninglinkoff} primitive@>
+primitive("pdfrunninglinkon", extension, pdf_running_link_on_node);@/
+@!@:pdf\_running\_link\_on\_}{\.{\\pdfrunninglinkon} primitive@>
+primitive("pdfspacefont", extension, pdf_space_font_code);@/
+@!@:pdf\_space\_font\_}{\.{\\pdfspacefont} primitive@>
+@#
+primitive("tracingstacklevels", extension, tracing_stack_level_code);@/
+@!@:tracingstacklevels\_}{\.{\\tracingstacklevels} primitive@>
+primitive("partokencontext", extension, pdf_par_token_context_code);@/
+@!@:\partokencontext}{\.{\\partokencontext} primitive@>
+primitive("partokenname", extension, pdf_par_token_name_code);@/
+@!@:partokenname\_}{\.{\\partokenname} primitive@>
+primitive("showstream", extension, pdf_show_stream_code);@/
+@!@:showstream\_}{\.{\\showstream} primitive@>
+}
+
+@ After having defined the primitives, we arange for printing them.
+
+@<Cases of |extension| for |print_cmd_chr|@>=
+ case pdf_annot_node: print_esc("pdfannot");@+break;
+ case pdf_catalog_code: print_esc("pdfcatalog");@+break;
+ case pdf_dest_node: print_esc("pdfdest");@+break;
+ case pdf_end_link_node: print_esc("pdfendlink");@+break;
+ case pdf_end_thread_node: print_esc("pdfendthread");@+break;
+ case pdf_font_attr_code: print_esc("pdffontattr");@+break;
+ case pdf_font_expand_code: print_esc("pdffontexpand");@+break;
+ case pdf_include_chars_code: print_esc("pdfincludechars");@+break;
+ case pdf_info_code: print_esc("pdfinfo");@+break;
+ case pdf_literal_node: print_esc("pdfliteral");@+break;
+ case pdf_colorstack_node: print_esc("pdfcolorstack");@+break;
+ case pdf_setmatrix_node: print_esc("pdfsetmatrix");@+break;
+ case pdf_save_node: print_esc("pdfsave");@+break;
+ case pdf_restore_node: print_esc("pdfrestore");@+break;
+ case pdf_map_file_code: print_esc("pdfmapfile");@+break;
+ case pdf_map_line_code: print_esc("pdfmapline");@+break;
+ case pdf_names_code: print_esc("pdfnames");@+break;
+ case pdf_obj_code: print_esc("pdfobj");@+break;
+ case pdf_outline_code: print_esc("pdfoutline");@+break;
+ case pdf_refobj_node: print_esc("pdfrefobj");@+break;
+ case pdf_refxform_node: print_esc("pdfrefxform");@+break;
+ case pdf_refximage_node: print_esc("pdfrefximage");@+break;
+ case pdf_snap_ref_point_node: print_esc("pdfsnaprefpoint");@+break;
+ case pdf_snapy_comp_node: print_esc("pdfsnapycomp");@+break;
+ case pdf_snapy_node: print_esc("pdfsnapy");@+break;
+ case pdf_start_link_node: print_esc("pdfstartlink");@+break;
+ case pdf_start_thread_node: print_esc("pdfstartthread");@+break;
+ case pdf_thread_node: print_esc("pdfthread");@+break;
+ case pdf_trailer_code: print_esc("pdftrailer");@+break;
+ case pdf_trailer_id_code: print_esc("pdftrailerid");@+break;
+ case pdf_xform_code: print_esc("pdfxform");@+break;
+ case pdf_ximage_code: print_esc("pdfximage");@+break;
+ case pdf_nobuiltin_tounicode_code: print_esc("pdfnobuiltintounicode");@+break;
+ case pdf_glyph_to_unicode_code: print_esc("pdfglyphtounicode");@+break;
+ case pdf_interword_space_on_node: print_esc("pdfinterwordspaceon");@+break;
+ case pdf_interword_space_off_node: print_esc("pdfinterwordspaceoff");@+break;
+ case pdf_fake_space_node: print_esc("pdffakespace");@+break;
+ case pdf_running_link_off_node: print_esc("pdfrunninglinkoff");@+break;
+ case pdf_running_link_on_node: print_esc("pdfrunninglinkon");@+break;
+ case pdf_space_font_code: print_esc("pdfspacefont");@+break;
+ case tracing_stack_level_code: print_esc("tracingstacklevels");@+break;
+ case pdf_par_token_context_code: print_esc("partokencontext");@+break;
+ case pdf_par_token_name_code: print_esc("partokenname");@+break;
+ case pdf_show_stream_code: print_esc("showstream");@+break;
+
+
+@ And last not least we have to implement the primitives.
+
+@<Cases for |do_extension|@>=
+ case pdf_annot_node: @<Implement \.{\\pdfannot}@>@;@+break;
+ case pdf_catalog_code: @<Implement \.{\\pdfcatalog}@>@;@+break;
+ case pdf_dest_node: @<Implement \.{\\pdfdest}@>@;@+break;
+ case pdf_end_link_node: @+break;
+ case pdf_end_thread_node: @+break;
+ case pdf_font_attr_code: @<Implement \.{\\pdffontattr}@>@;@+break;
+ case pdf_font_expand_code: read_expand_font();@+break;
+ case pdf_include_chars_code: pdf_include_chars();@+break;
+ case pdf_info_code: scan_pdf_ext_toks();@+break;
+ case pdf_literal_node: @<Implement \.{\\pdfliteral}@>@;@+break;
+ case pdf_colorstack_node: @<Implement \.{\\pdfcolorstack}@>@;@+break;
+ case pdf_setmatrix_node: scan_pdf_ext_toks();@+break;
+ case pdf_save_node: @+break;
+ case pdf_restore_node: @+break;
+ case pdf_map_file_code: scan_pdf_ext_toks();@+break;
+ case pdf_map_line_code: scan_pdf_ext_toks();@+break;
+ case pdf_names_code: scan_pdf_ext_toks();@+break;
+ case pdf_obj_code: @<Implement \.{\\pdfobj}@>@;@+break;
+ case pdf_outline_code: @<Implement \.{\\pdfoutline}@>@;@+break;
+ case pdf_refobj_node: scan_int();@+break;
+ case pdf_refxform_node: scan_int();@+break;
+ case pdf_refximage_node: scan_int();@+break;
+ case pdf_snap_ref_point_node: @+break;
+ case pdf_snapy_comp_node: scan_int();@+break;
+ case pdf_snapy_node: @+break;
+ case pdf_start_link_node: scan_annot(); scan_action();@+break;
+ case pdf_start_thread_node: scan_annot(); scan_thread_id();@+break;
+ case pdf_thread_node: scan_annot(); scan_thread_id();@+break;
+ case pdf_trailer_code: scan_pdf_ext_toks();@+break;
+ case pdf_trailer_id_code: scan_pdf_ext_toks();@+break;
+ case pdf_xform_code: @<Implement \.{\\pdfxform}@>@;@+break;
+ case pdf_ximage_code: scan_image(); @+break;
+ case pdf_glyph_to_unicode_code: scan_pdf_ext_toks();scan_pdf_ext_toks();@+break;
+ case pdf_nobuiltin_tounicode_code: scan_font_ident();@+break;
+ case pdf_interword_space_on_node: @+break;
+ case pdf_interword_space_off_node: @+break;
+ case pdf_fake_space_node: @+break;
+ case pdf_running_link_off_node: @+break;
+ case pdf_running_link_on_node: @+break;
+ case pdf_space_font_code: scan_pdf_ext_toks(); @+break;
+ case tracing_stack_level_code: scan_int();@+break;
+ case pdf_par_token_context_code: scan_optional_equals();scan_int();@+break;
+ case pdf_par_token_name_code: scan_optional_equals();get_token();@+break;
+ case pdf_show_stream_code: scan_optional_equals();scan_int();@+break;
+
+
+@ @<Implement \.{\\pdfannot}@>=
+{@+ if (scan_keyword("reserveobjnum")) {@+@<Scan an optional space@>;}
+ else{@+
+ if (scan_keyword("useobjnum")) {@+scan_int();}
+ scan_annot();
+ scan_pdf_ext_toks();
+ }
+}
+
+@ @<Implement \.{\\pdfcatalog}@>=
+{@+ scan_pdf_ext_toks();
+ if (scan_keyword("openaction")) scan_action();
+}
+
+@ The |scan_action| routine is used not only here
+but also for links and outlines.
+
+
+@d pdf_action_page 0 /*GoTo action*/
+@d pdf_action_goto 1 /*GoTo action*/
+@d pdf_action_thread 2 /*Thread action*/
+@d pdf_action_user 3 /*user-defined action*/
+
+@<Declare procedures needed in |do_ext...@>=
+static void scan_action(void) /*read an action specification*/
+{@+int pdf_action;
+ int pdf_action_file=0;
+ int pdf_action_new_window=0;
+ if (scan_keyword("user"))
+ pdf_action=pdf_action_user;
+ else if (scan_keyword("goto"))
+ pdf_action=pdf_action_goto;
+ else if (scan_keyword("thread"))
+ pdf_action=pdf_action_thread;
+ else
+ pdf_error("ext1","action type missing");
+ if (pdf_action==pdf_action_user) {@+
+ scan_pdf_ext_toks();
+ return; }
+ if (scan_keyword("file")) {@+
+ scan_pdf_ext_toks();
+ pdf_action_file=1;
+ }
+ if (scan_keyword("struct")) {@+
+ if (pdf_action!=pdf_action_goto)
+ pdf_error("ext1","only GoTo action can be used with `struct'");
+ if (pdf_action_file!=0) {@+
+ scan_pdf_ext_toks();
+ }
+ else if (scan_keyword("name")) {@+
+ scan_pdf_ext_toks();
+ }
+ else if (scan_keyword("num")) {@+
+ scan_int();
+ if (cur_val <= 0)
+ pdf_error("ext1","num identifier must be positive");
+ }
+ else
+ pdf_error("ext1","identifier type missing");
+ }
+ if (scan_keyword("page")) {@+
+ if (pdf_action!=pdf_action_goto)
+ pdf_error("ext1","only GoTo action can be used with `page'");
+ pdf_action=pdf_action_page;
+ scan_int();
+ if (cur_val <= 0)
+ pdf_error("ext1","page number must be positive");
+ scan_pdf_ext_toks();
+ }
+ else if (scan_keyword("name")) {@+
+ scan_pdf_ext_toks();
+ }
+ else if (scan_keyword("num")) {@+
+ if ((pdf_action==pdf_action_goto)&&
+ (pdf_action_file!=0))
+ pdf_error("ext1",
+ "`goto' option cannot be used with both `file' and `num'");
+ scan_int();
+ if (cur_val <= 0)
+ pdf_error("ext1","num identifier must be positive");
+ }
+ else
+ pdf_error("ext1","identifier type missing");
+ if (scan_keyword("newwindow")) {@+
+ pdf_action_new_window=1;
+ @<Scan an optional space@>;}
+ else if (scan_keyword("nonewwindow")) {@+
+ pdf_action_new_window=2;
+ @<Scan an optional space@>;}
+ else
+ pdf_action_new_window=0;
+ if ((pdf_action_new_window > 0)&&
+ (((pdf_action!=pdf_action_goto)&&
+ (pdf_action!=pdf_action_page))||
+ (pdf_action_file==0)))
+ pdf_error("ext1",
+ "`newwindow'/`nonewwindow' must be used with `goto' and `file' option");
+}
+
+@ Next we scan a destination.
+
+@d pdf_dest_xyz 0
+@d pdf_dest_fit 1
+@d pdf_dest_fith 2
+@d pdf_dest_fitv 3
+@d pdf_dest_fitb 4
+@d pdf_dest_fitbh 5
+@d pdf_dest_fitbv 6
+@d pdf_dest_fitr 7
+
+@<Implement \.{\\pdfdest}@>=
+{@+ int pdf_dest_type;
+ if (scan_keyword("struct")) {@+
+ scan_int();
+ if (cur_val <= 0)
+ pdf_error("ext1","struct identifier must be positive");
+ }
+ if (scan_keyword("num")) {@+
+ scan_int();
+ if (cur_val <= 0)
+ pdf_error("ext1","num identifier must be positive");
+ if (cur_val > max_halfword)
+ pdf_error("ext1","number too big");
+ }
+ else if (scan_keyword("name")) {@+
+ scan_pdf_ext_toks();
+ }
+ else
+ pdf_error("ext1","identifier type missing");
+ if (scan_keyword("xyz")) {@+
+ pdf_dest_type=pdf_dest_xyz;
+ if (scan_keyword("zoom")) {@+
+ scan_int();
+ if (cur_val > max_halfword)
+ pdf_error("ext1","number too big");
+ }
+ }
+ else if (scan_keyword("fitbh"))
+ pdf_dest_type=pdf_dest_fitbh;
+ else if (scan_keyword("fitbv"))
+ pdf_dest_type=pdf_dest_fitbv;
+ else if (scan_keyword("fitb"))
+ pdf_dest_type=pdf_dest_fitb;
+ else if (scan_keyword("fith"))
+ pdf_dest_type=pdf_dest_fith;
+ else if (scan_keyword("fitv"))
+ pdf_dest_type=pdf_dest_fitv;
+ else if (scan_keyword("fitr"))
+ pdf_dest_type=pdf_dest_fitr;
+ else if (scan_keyword("fit"))
+ pdf_dest_type=pdf_dest_fit;
+ else
+ pdf_error("ext1","destination type missing");
+ @<Scan an optional space@>;
+ if (pdf_dest_type==pdf_dest_fitr) {@+
+ scan_alt_rule(); /*scans | < rule spec > | to |alt_rule|*/
+ }
+}
+
+
+
+@ @<Declare procedures needed in |do_ext...@>=
+static void scan_alt_rule(void) /*scans rule spec to |alt_rule|*/
+{@+
+reswitch:
+ if (scan_keyword("width")) {@+
+ scan_normal_dimen;
+ goto reswitch;
+ }
+ if (scan_keyword("height")) {@+
+ scan_normal_dimen;
+ goto reswitch;
+ }
+ if (scan_keyword("depth")) {@+
+ scan_normal_dimen;
+ goto reswitch;
+ }
+}
+
+static void scan_annot(void) /*scan an annotation*/
+{@+
+ scan_alt_rule(); /*scans | < rule spec > |*/
+ if (scan_keyword("attr"))
+ scan_pdf_ext_toks();
+}
+
+
+
+@ @<Implement \.{\\pdffontattr}@>=
+{@+
+ scan_font_ident();
+ if (cur_val==null_font)
+ pdf_error("font","invalid font identifier");
+ scan_pdf_ext_toks();
+}
+
+@ To implement \.{\\pdffontexpand} we need the following routine:
+
+@<Declare procedures needed in |do_ext...@>=
+static void read_expand_font(void) /*read font expansion spec*/
+{@+int shrink_limit, stretch_limit, font_step;
+ internal_font_number f;
+
+ /*read font expansion parameters*/
+ scan_font_ident();
+ f=cur_val;
+ if (f==null_font)
+ pdf_error("font expansion","invalid font identifier");
+ scan_optional_equals();
+ scan_int();
+ scan_int();
+ scan_int();
+ if (scan_keyword("autoexpand")) {@+
+ @<Scan an optional space@>;
+ }
+}
+
+@ Do implement \.{\\pdfincludechars} we need the next function.
+@<Declare procedures needed in |do_ext...@>=
+static void pdf_include_chars(void)
+{@+str_number s;
+ pool_pointer k; /*running indices*/
+ internal_font_number f;
+
+ scan_font_ident();
+ f=cur_val;
+ if (f==null_font)
+ pdf_error("font","invalid font identifier");
+ scan_pdf_ext_toks();
+}
+
+@ @<Implement \.{\\pdfliteral}@>=
+{@+ int k;
+ if (scan_keyword("shipout")) k=pdf_lateliteral_node;
+ else k=pdf_literal_node;
+ if (scan_keyword("direct")) do_nothing;
+ else if (scan_keyword("page")) do_nothing;
+ else do_nothing;
+ if (k==pdf_literal_node) scan_pdf_ext_toks();
+ else scan_pdf_ext_late_toks();
+}
+
+@
+
+@d colorstack_set 0
+@d colorstack_push 1
+@d colorstack_data 1 /* last value where data field is set */
+@d colorstack_pop 2
+@d colorstack_current 3
+
+@<Implement \.{\\pdfcolorstack}@>=
+{@+int i;
+ scan_int();
+ if (scan_keyword("set")) {@+
+ i=colorstack_set;
+ }
+ else if (scan_keyword("push")) {@+
+ i=colorstack_push;
+ }
+ else if (scan_keyword("pop")) {@+
+ i=colorstack_pop;
+ }
+ else if (scan_keyword("current")) {@+
+ i=colorstack_current;
+ }
+ else{@+
+ i=-1; /*error*/
+ }
+ if (i >= 0) {@+
+ if (i <= colorstack_data) {@+
+ scan_pdf_ext_toks();
+ }
+ }
+}
+
+@ @<Implement \.{\\pdfobj}@>=
+{@+
+ if (scan_keyword("reserveobjnum")) {@+
+ @<Scan an optional space@>;
+ }
+ else{@+
+ if (scan_keyword("useobjnum")) scan_int();
+ if (scan_keyword("stream")) {@+
+ if (scan_keyword("attr")) scan_pdf_ext_toks();
+ }
+ if (scan_keyword("file")) do_nothing;
+ scan_pdf_ext_toks();
+ }
+}
+
+@ @<Implement \.{\\pdfoutline}@>=
+{@+
+ if (scan_keyword("attr")) scan_pdf_ext_toks();
+ scan_action();
+ if (scan_keyword("count")) scan_int();
+ scan_pdf_ext_toks();
+}
+
+@ @<Implement \.{\\pdfxform}@>=
+{@+
+ if (scan_keyword("attr")) scan_pdf_ext_toks();
+ if (scan_keyword("resources")) scan_pdf_ext_toks();
+ scan_register_num();
+}
+
+@ The next procedure is neede for \.{\\pdfthread} and \.{\\pdfstartthread}
+
+@<Declare procedures needed in |do_ext...@>=
+static void scan_thread_id(void)
+{@+if (scan_keyword("num")) scan_int();
+ else if (scan_keyword("name")) scan_pdf_ext_toks();
+}
+
+@ The next functions are used to implement \.{\\pdfximage}.
+@<Declare procedures needed in |do_ext...@>=
+static void scan_pdf_box_spec(void) /*scans PDF pagebox specification*/
+{@+
+ if (scan_keyword("mediabox")) return;
+ else if (scan_keyword("cropbox")) return;
+ else if (scan_keyword("bleedbox")) return;
+ else if (scan_keyword("trimbox")) return;
+ else if (scan_keyword("artbox")) return;
+}
+
+static void scan_image(void)
+{@+ scan_alt_rule();
+ if (scan_keyword("attr")) scan_pdf_ext_toks();
+ if (scan_keyword("named")) scan_pdf_ext_toks();
+ else if (scan_keyword("page")) scan_int();
+ if (scan_keyword("colorspace")) scan_int();
+ scan_pdf_box_spec();
+ scan_pdf_ext_toks();
+}
+
+
+@ The current version of the {\tt -pdf} version does not implementa all \pdfTeX\ primitives.
+ For example, the primitves \.{\\pdfcopyfont} and \.{\\letterspacefont} are not defined but
+ dummy versions could be implemented with |scan_font_ident| and |scan_int|.
+
+
+@* Index.
+Here is where you can find all uses of each identifier in the program,
+with underlined entries pointing to where the identifier was defined.
+If the identifier is only one letter long, however, you get to see only
+the underlined entries. {\sl All references are to section numbers instead of
+page numbers.}
+
+This index also lists error messages and other aspects of the program
+that you might want to look up some day. For example, the entry
+for ``system dependencies'' lists all sections that should receive
+special attention from people who are installing \TeX\ in a new
+operating environment. A list of various things that can't happen appears
+under ``this can't happen''. Approximately 40 sections are listed under
+``inner loop''; these account for about 60\pct! of \TeX's running time,
+exclusive of input and output.